Metadata-Version: 2.4
Name: fletchr-studio
Version: 0.2.0
Summary: Flowgraph engine for the fletchr studio GUI: graph model, palette introspection, validation, and Python code generation.
Project-URL: Homepage, https://github.com/fletchr-labs/fletchr
Project-URL: Repository, https://github.com/fletchr-labs/fletchr
Project-URL: Issues, https://github.com/fletchr-labs/fletchr/issues
Project-URL: Changelog, https://github.com/fletchr-labs/fletchr/blob/main/fletchr-studio/CHANGELOG.md
Author-email: Jonathan Olsten <jolsten@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: arrow,codegen,flowgraph,gui,pipeline,pyarrow
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
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: Topic :: Scientific/Engineering
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: attrs>=22.1.0
Requires-Dist: fastapi>=0.110
Requires-Dist: fletchr-core>=0.3.0
Requires-Dist: uvicorn>=0.29
Description-Content-Type: text/markdown

# fletchr-studio

The flowgraph engine behind the fletchr studio GUI: a graph document
model for source → transformer → sink flowgraphs, a block palette built
by introspecting the live fletchr registries, graph validation, and
Python code generation with round-trip reopen.

The package also ships the studio itself: a FastAPI host
(`fletchr_studio.server`) serving a React Flow canvas UI, with
sessions, sampled/full graph runs, and per-node table previews. See
[`docs/design/studio.md`](../docs/design/studio.md) at the workspace
root for the design record and phased plan.

## Running the GUI

```bash
fletchr-studio            # serves http://127.0.0.1:8410
```

Drag blocks from the palette, connect them (incompatible ports are
rejected), configure params in the inspector, then **Preview Run** —
sources are sampled and sinks are skipped, so a preview never
overwrites real outputs. Click any node to see its table preview.
**Full Run** executes everything, sinks included. **Generate** shows
the emitted script; **Save** writes it; **Open** reopens a generated
`.py` (hand edits are flagged as drift).

## Rebranding for meta-packages

A downstream protocol wrapper can present the studio as its own tool.
Ship a console script that launches it with a `Branding`:

```python
# acme_decoder/studio.py
import acme_decoder.plugins  # noqa: F401 - registers blocks via entry points
from fletchr_studio import Branding
from fletchr_studio.server import main


def cli() -> None:
    main(branding=Branding(name="ACME Decoder Studio", favicon="path/to/icon.svg"))
```

```toml
[project.scripts]
acme-studio = "acme_decoder.studio:cli"
```

The name flows into the browser tab, the top bar, and the FastAPI
title; the favicon replaces the default. Generated scripts still
record `fletchr-studio <version>` as their generator — that's
provenance, not presentation. The wrapper's transformers, readers,
and writers appear in the palette automatically via the normal
entry-point plugin discovery.

The canvas bundle builds into `src/fletchr_studio/static/` and ships
in both the wheel and the sdist. Packaging is guarded by
`hatch-jupyter-builder`: building a dist with the bundle already
present needs no Node (`skip-if-exists`); with the bundle missing it
runs `npm install` + `npm run build` itself; and with neither bundle
nor Node the build **fails** rather than producing a headless dist
(`ensured-targets`). Editable installs only warn, so Python-only
contributors without Node get a working headless dev server. Escape
hatch for deliberate headless builds: `SKIP_JUPYTER_BUILDER=1`.

To rebuild the bundle by hand you need Node 18+:

```bash
cd frontend
npm install
npm run build     # type-checks and outputs to ../src/fletchr_studio/static
npm run dev       # dev server with /api proxied to a running fletchr-studio
```

## What it does

```python
from fletchr_studio import GraphDoc, generate_code, load_graph

doc = GraphDoc()
doc.add_node("capture", "source", "read_file", path="capture.arrow")
doc.add_node("sel", "transform", "Subframe", columns="1-32")
doc.add_node("inv", "transform", "Invert")
doc.add_node("out", "sink", "write_file", path="frames.parquet")
doc.add_edge("capture", "sel")
doc.add_edge("sel", "inv")
doc.add_edge("inv", "out")

code = generate_code(doc)
```

`generate_code` validates the graph (unknown blocks, port type
mismatches, cycles, missing params — reusing the same subclass-aware
compatibility rules `Pipeline` enforces) and emits a plain Python
script depending only on the fletchr packages:

```python
"""Flowgraph generated by fletchr-studio 0.1.0."""

import argparse

from fletchr_core import read_file, write_file
from fletchr_core.transform import Invert, Subframe


def main(capture_path='capture.arrow', out_path='frames.parquet'):
    capture = read_file(capture_path)
    inv = (Subframe(columns='1-32') | Invert())(capture)
    write_file(out_path, inv)


def _cli():
    parser = argparse.ArgumentParser(description="Flowgraph generated by fletchr-studio.")
    parser.add_argument("--capture", dest="capture_path", default='capture.arrow', help="source 'capture' path")
    parser.add_argument("--out", dest="out_path", default='frames.parquet', help="sink 'out' path")
    main(**vars(parser.parse_args()))


if __name__ == "__main__":
    _cli()


__fletchr_graph__ = {...}
```

Source and sink paths are lifted into `main()`'s signature and an
argparse CLI, so the artifact is parameterizable without editing:
`python flow.py --capture other.bits`, or import it and call
`main(capture_path=...)` from a loop.

The embedded `__fletchr_graph__` literal is the graph document —
`load_graph("flow.py")` reopens it, and a stored SHA-256 of the code
section detects hand edits (drift) without parsing arbitrary Python.
Linear chains compile to `|` pipelines; fan-out becomes named
intermediates; merge blocks (`stack`, `merge`) compile to plain calls.

The palette (`build_palette()`) discovers every registered transformer
— including plugin-contributed ones — plus file source/sink blocks and
the merge blocks, with parameter schemas introspected from attrs fields
or `__init__` signatures and port types from `apply` annotations.
