Skip to main content

Segundo cerebro científico con LaTeX en vivo, voz y bóveda segura.

Project description

lablog

lablog

A live LaTeX laboratory notebook for working scientists.

version CI Pages tests coverage python react license

Live preview

Part of the Pharos Project · José Labarca Baeza · USM, Valparaíso


About Features Architecture Installation Quick start Configuration
Editor Security Testing Publishing Roadmap License

About

lablog is a research-grade notebook that lives where the experiment happens. It pairs a structural LaTeX editor with live preview, executable cells, voice dictation and an immutable, event-sourced history so that the record of an investigation can be reconstructed exactly as it unfolded.

Unlike a typesetting tool used after the experiment, lablog is meant to run while the work is in progress — gloves on, instrument in front, hands occupied. It is built on the premise that the act of writing the paper should not be separated from the act of producing the data.

ToolWhen you use it
OverleafAfter the experiment, when the manuscript is being prepared.
TeXstudio / TeXmacsFor traditional LaTeX editing on a desktop.
lablogDuring the experiment: to dictate, execute and preserve as it happens.

Features

Module Capability
Structural LaTeX renderer Sections, emphasis, lists, hyperlinks, and inline mathematics flow within paragraphs. KaTeX powers display environments (align, equation, gather, cases, pmatrix) with automatic equation numbering.
Editor Line gutter, parameter overlay, auto-save, Ctrl+F find & replace with hit counter, Ctrl+Z / Ctrl+Y undo / redo, Ctrl+B/I/E bold / italic / math, smart delimiter wrapping, snippet and symbol insertion at cursor.
Voice dictation Browser SpeechRecognition with an intent detector and local Whisper pipeline. A session safety timeout prevents the recogniser from hanging if the API stalls.
Executable cells Write \begin{python}...\end{python} and run real Jupyter kernels in place. Stdout, results and figures are captured back into the document; the kernel is interrupted on timeout to prevent runaway code.
Per-page vault Attach images, CSV, PDF, DOCX, audio and scripts; preview them without leaving the notebook. Metadata is written atomically; uploads are sanitised against path traversal and bounded by an explicit size limit.
Event-sourced history Every edit, execution and attachment is appended as an immutable JSONL event. The current state is a deterministic projection — reproducible, auditable, and the foundation for future time-travel UI.
Project grouping Pages cluster into projects directly from the sidebar; renaming, deletion and reorganisation are first-class operations.
Export LaTeX (.tex), plain text, HTML, PDF and DOCX via pandoc; Jupyter notebook (.ipynb); static site for GitHub Pages; Canva-ready HTML. Titles are escaped to prevent LaTeX injection.
Diagram workbench Twelve parameterized presets (circuits, control, mechanics, optics, Feynman). Dial values, re-apply TikZ, optional PySpice cells with numpy fallback, dual highlight (editor line + colored circuitikz nodes).
Personalization Density, editor font, Nord palette, reduced motion, lab/paper/teaching profiles, exportable preferences JSON, configurable keyboard shortcuts.

Architecture

%%{init: { 'theme': 'base', 'themeVariables': { 'primaryColor':'#1C1C1E', 'primaryTextColor':'#F2F2F7', 'primaryBorderColor':'#48484A', 'lineColor':'#A1A1A6', 'secondaryColor':'#48484A', 'tertiaryColor':'#1C1C1E' }}}%%
flowchart LR
    subgraph UI[lablog UI]
        direction TB
        React["React 19 + Vite"]
        Tailwind["Tailwind CSS v4"]
        Zustand["Zustand store"]
        KaTeX["KaTeX renderer"]
    end

    subgraph Engine[lablog Engine]
        direction TB
        FastAPI["FastAPI"]
        Events["Event Store (JSONL)"]
        Projector["Projector"]
        AST["LaTeX AST"]
        Jupyter["Jupyter / Python"]
        Vault["Vault"]
    end

    User((Researcher))
    User --> UI
    UI <-->|HTTP| Engine
    Engine -->|export| Site["Static site"]
    Site -->|GitHub Actions| Pages["GitHub Pages"]

The engine is decoupled from the interface: it can be driven from the CLI, embedded in tests, or replaced by another front-end. The projector reconstructs the current DocumentNode from the event log; the renderer reduces that tree to HTML.

Module map
Path Responsibility
src/lablog/api.py FastAPI routes; export pipeline; vault uploads.
src/lablog/event_store.py Append-only JSONL log; path-validated page identifiers.
src/lablog/events.py Event schema and constructors.
src/lablog/projector.py Pure folding of events into a DocumentNode.
src/lablog/latex_ast.py Tokeniser; environment whitelist; round-trip serialiser.
src/lablog/code_engine.py Jupyter kernel manager with enforced timeout.
src/lablog/vault.py Attachment store with atomic metadata and time-locked deletion.
src/lablog/exporter.py Static site exporter for GitHub Pages.
ui/src/lib/latex-render.ts Structural renderer (prose × mathematics).
ui/src/components/editor/latex-editor.tsx Editor, find & replace, undo / redo, shortcuts.
ui/src/components/preview/latex-preview.tsx Node coalescing & live preview.
ui/src/stores/app-store.ts Single source of truth on the client; persistent state.

Stack

Layer Stack
EnginePython 3.11+, FastAPI, Pydantic, Jupyter Client, faster-whisper.
PersistenceJSONL event log; atomic metadata writes; deterministic projection.
InterfaceReact 19, TypeScript, Vite 8, Tailwind CSS v4, Zustand, shadcn/ui, Radix.
MathematicsKaTeX with automatic numbering for display environments.
Toolinguv (Python), npm (Node), Ruff, Mypy (strict), oxlint, pytest, GitHub Actions.

Installation

From PyPI (recommended)

pip install jose-labarca-lablog
lablog serve

The wheel bundles the compiled UI, so a simple pip install is enough to run the application. The optional desktop extra adds the native window:

pip install jose-labarca-lablog[desktop]
lablog app

From source

Prerequisites. Python 3.11 or newer, Node 22, uv, and npm. Optional: pandoc and xelatex (or pdflatex) for PDF and DOCX export.

git clone https://github.com/kegouro/lablog.git
cd lablog
uv sync --extra dev
source .venv/bin/activate
cp .env.example .env

cd ui && npm install && cd ..

Optional extras (the core install stays lean):

Extra Command Adds
Desktop app uv sync --extra desktop pywebview — native window via lablog app.
Offline voice uv sync --extra voice local Whisper model + audio capture (several hundred MB).

Quick start

Run the engine

source .venv/bin/activate
uvicorn lablog.api:app --host 127.0.0.1 --port 8000 --reload

Run the interface

cd ui
npm run dev
Endpoint URL
User interface http://127.0.0.1:5173
API http://127.0.0.1:8000/api/v1
Liveness probe http://127.0.0.1:8000/api/v1/health

For a production build served by the engine itself:

cd ui && npm run build && cd ..
uvicorn lablog.api:app --host 127.0.0.1 --port 8000

The interface is then served statically from ui/dist by FastAPI.


Desktop application

lablog runs as a native desktop window — no browser, no internet. The engine binds to 127.0.0.1 on an ephemeral port inside the process and the interface opens in the operating system's own webview (WKWebView on macOS, WebView2 on Windows, WebKitGTK on Linux). Every asset — JavaScript, CSS, fonts and the KaTeX renderer — is bundled, so the application is fully offline.

uv sync --extra desktop      # installs pywebview
cd ui && npm run build && cd ..
lablog app                   # opens the native window

Offline note. The core notebook (editor, live preview, executable cells, vault) is entirely offline. The in-browser dictation button relies on the platform speech API and degrades gracefully when offline; for offline dictation install the local Whisper model with uv sync --extra voice.

Packaging a portable bundle

To ship a self-contained folder that runs without any Python install:

./scripts/package_desktop.sh      # builds the UI, then PyInstaller
# → dist/lablog/  (zip it and run dist/lablog/lablog)

The bundle is described by lablog.spec. It includes ui/dist and the Jupyter kernel used for cell execution, and excludes the heavy voice model by default. The kernel and pyzmq are discovered dynamically, so on a fresh platform the spec's collect_all lists may need a small adjustment — treat it as a verified starting point rather than a one-click cross-platform installer.


Real PDF compilation

The live preview is fast but approximate — it renders the common subset of LaTeX with KaTeX and a focused HTML renderer. For a faithful document, lablog compiles the page to a real PDF with Tectonic (a self-contained XeTeX engine). The first compile downloads and caches the required TeX packages once; every subsequent compile is fully offline.

  • The Compilar PDF button lives in the preview header; the preview itself is labelled "Aproximada" so the distinction is explicit.
  • Executable \begin{python} cells are rendered into the PDF as code + output + figure (code and output via fancyvrb, figures via \includegraphics).
  • Compilation runs asynchronously with a hard timeout; runaway documents return 504 instead of hanging the engine. Output is cached by document hash, so recompiling an unchanged page is instant.
  • When compilation fails, the error panel maps TeX errors back to the source via injected % lablog-src markers, showing "Celda N · línea M: message" rather than a meaningless line in the generated .tex.

Security. Tectonic runs without --shell-escape — LaTeX cannot execute operating-system commands. lablog is local and single-user; the compile endpoint assumes you are compiling your own content and must not be exposed publicly without adding rate limiting and isolation.

Engine note. If tectonic is already on your PATH it is used directly. Otherwise the preview shows an install banner that downloads the pinned, checksum-verified binary once to LABLOG_DATA_DIR/bin/ and warms the common packages (offline afterwards). When a managed binary falls behind the version this app pins, the banner offers a verified re-install — it never fetches "latest" at runtime, preserving the checksum trust chain.


Configuration

All configurable paths and addresses are surfaced through environment variables. See .env.example for the full list.

Variable Default Purpose
LABLOG_DATA_DIR ~/.lablog Root for events, vault, figures and exported site.
LABLOG_HOST 127.0.0.1 Bind address for the API.
LABLOG_PORT 8000 Port for the API.
LABLOG_CORS_ORIGINS http://localhost:5173, http://127.0.0.1:5173 Comma-separated CORS origins.
LABLOG_SITE_DIR ${data_dir}/site Destination of the static site exporter.

Note. lablog never reads secrets from the data directory and never embeds them in exports. Treat LABLOG_DATA_DIR as personal notes; commit only what you intend to publish.


Editor experience

Action Shortcut
Find & replace (in-editor) Ctrl+F · Ctrl+H
Next / previous match Enter · Shift+Enter
Undo / redo Ctrl+Z · Ctrl+Y
Bold · Italic · Inline math Ctrl+B · Ctrl+I · Ctrl+E
Indent selection Tab
Command palette Ctrl+K
Wrap selection in delimiters type { ( [ $ on a selection

The history stack survives programmatic insertions (symbols, snippets, voice dictation), which is where browser-native undo typically breaks.


Security model

Security is treated as a correctness property. The following invariants are enforced in the engine and exercised by the test suite where applicable:

Invariant Mechanism
Page identifiers cannot escape the events directory. Regex-validated identifiers (UUID-shaped) reject path traversal at the EventStore boundary.
Uploaded filenames cannot escape the vault. The filename is reduced to its basename before reaching the filesystem.
Uploads are bounded. Hard ceiling of 100 MB; oversized requests return 413.
User code cannot block the kernel indefinitely. A monotonic deadline triggers interrupt_kernel(); the error is reported back to the cell.
The document title cannot inject LaTeX during export. All ten LaTeX-meta characters are escaped before reaching \title{...}.
Cell figures cannot be served from outside the figure root. Paths are resolved against figures_dir and rejected if they escape.
Vault metadata cannot be corrupted by concurrent writes. meta.json is written via tempfile and atomic rename.
Corrupted events do not brick a page. Invalid JSONL lines are skipped at read time and the rest of the log is recovered.

Testing & quality

# Backend
pytest -q
ruff check .
mypy .
bandit -r src/lablog tests -ll -x .venv,prototypes,dist,ui

# Frontend
cd ui
npx tsc --noEmit
npm run build
npm run lint
npm test -- --run

The project ships with 130+ backend tests covering the parser, projector, event store, vault, code engine, snippets, symbols, and the public API, with a coverage threshold of 80%. The frontend is type-checked under strict TypeScript, linted with oxlint, tested with Vitest and built with Vite.


Publishing to GitHub Pages

lablog exports a static, KaTeX-rendered version of the notebook for public sharing. The interactive surface (editor, executable cells, voice) remains local.

source .venv/bin/activate
uv run python - <<'PY'
from lablog.exporter import export_site
export_site()
PY

A workflow at .github/workflows/pages.yml reproduces the same export in CI and publishes it on every push to main. To enable it on a fork:

  1. In Settings → Pages, set the source to GitHub Actions.
  2. Push to main.
  3. The deployment URL appears in the workflow summary.

Roadmap

MilestoneStatus
Event-sourced engine with deterministic projectionDone
Voice → intent → LaTeX pipelineDone
Structural LaTeX renderer (sections, lists, environments)Done
Executable cells with kernel timeoutDone
Vault with previews and time-locked deletionDone
Editor: find & replace, undo / redo, cursor-aware insertionDone
Static export & GitHub Pages deploymentDone
Native desktop app (offline, pywebview)Done
Time-travel: scrub and restore any point of a page's historyDone
Full LaTeX mode: raw compilation, templates menu, error-to-line jumpDone
Version diff inside the time-travel panelDone
Portable PyInstaller bundleBeta
In-app PDF compilation with line-aware error reportingDone
LaTeX autocomplete + physics templates + lablog new --templateDone
Multi-file includes (\input{page:…}) at compile timeDone (minimal)
Diagram presets + Jupyter / optional PySpiceDone (0.3.0)
Re-apply params, dual highlight, .ipynb exportDone (0.3.0)
UI profiles + configurable shortcutsDone (0.3.0)
BibTeX / full citeprocPlanned
Section and equation cross-referencesPlanned
P2P collaboration and device syncExploratory

Citing

If lablog supports work that leads to publication, the recommended citation is:

@software{labarca_lablog,
  author  = {Labarca Baeza, José},
  title   = {{lablog}: a live LaTeX laboratory notebook for working scientists},
  year    = {2026},
  version = {0.3.0},
  url     = {https://github.com/kegouro/lablog},
  note    = {Part of the Pharos Project}
}

Also see CITATION.cff.


Contributing

See CONTRIBUTING.md. Security reports: SECURITY.md. Community standards: CODE_OF_CONDUCT.md.


License

lablog is released under the MIT License.


Acknowledgements

lablog is part of the Pharos Project — an effort to lower the barrier of entry to scientific and educational infrastructure. Identity, logo and banner by José Labarca Baeza. Original idea conceived with Vicente Muñoz Tolosa.

USM · Valparaíso · Chile · built to let the science flow

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jose_labarca_lablog-0.3.0.tar.gz (43.3 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jose_labarca_lablog-0.3.0-py3-none-any.whl (1.3 MB view details)

Uploaded Python 3

File details

Details for the file jose_labarca_lablog-0.3.0.tar.gz.

File metadata

  • Download URL: jose_labarca_lablog-0.3.0.tar.gz
  • Upload date:
  • Size: 43.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for jose_labarca_lablog-0.3.0.tar.gz
Algorithm Hash digest
SHA256 99e2c3233e047cd965114a33cce48aaf79c860e45c10820ce39c6b3fb9fda200
MD5 a88e12c42541c098d6c1ef590bedc0ce
BLAKE2b-256 9fe7fc0947f060c1ba67b0d3e778d4b8080578b927a3d00b7793c91f5e397bc5

See more details on using hashes here.

Provenance

The following attestation bundles were made for jose_labarca_lablog-0.3.0.tar.gz:

Publisher: release.yml on kegouro/lablog

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file jose_labarca_lablog-0.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for jose_labarca_lablog-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 50d84f9f3d122b2825a72eab7064c7e5d497117e9e654a939eb0abcb87e8155b
MD5 a44bb3361c9adc9dab7246f656c46c7b
BLAKE2b-256 667dbce1c11a07479292c0415ef142fb35cfc2b0873b8ddc6fad6efd582b0044

See more details on using hashes here.

Provenance

The following attestation bundles were made for jose_labarca_lablog-0.3.0-py3-none-any.whl:

Publisher: release.yml on kegouro/lablog

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page