Skip to main content

A tiny Streamlit-inspired terminal UI experiment.

Project description

stui

CI

stui v1.0.0 is the first stable release of a small Streamlit-inspired framework for building terminal-native Python apps. Write a short script, run it in your terminal, and get a Textual UI with stateful controls, reruns, and a compact public API.

It is built for local tools, demos, data scripts, model debug panels, SSH sessions, and headless environments where opening a browser, binding a port, or running a dashboard server is unnecessary ceremony. The public API is deliberately small and readable.

stui is serious about being smaller than the thing that inspired it. It is not official Streamlit, is not affiliated with Streamlit, and is not a Streamlit compatibility layer. Existing Streamlit apps usually need edits; new stui apps should be written against the documented terminal-first API below.

Preview

stui model demo terminal screenshot

┌─ stui ───────────────────────────────────────────┐
│ stui demo basic                                  │
│                                                  │
│ x                                                │
│ [██░░░░░░░░░░░░] 10                              │
│                                                  │
│ [ Increment ]                                    │
│                                                  │
│ x = 10                                           │
│ count = 0                                        │
│                                                  │
│ q Quit   r Rerun   tab Focus next                │
└──────────────────────────────────────────────────┘

Install

Use Python 3.11 or newer.

Install the PyPI distribution named stui-terminal. The import package and command are both still named stui, which is the name used throughout the examples and API reference:

python -m pip install stui-terminal
import stui as st

Check the installed version, diagnostics, and terminal environment before filing terminal or keyboard issues:

python -m stui --version
python -m stui doctor
python -m stui doctor --json

For local development from a checkout, use an editable install with the dev extra:

python3.11 -m venv .venv
. .venv/bin/activate
python -m pip install -e ".[dev]"

For runtime-only local use, install without the dev extra:

python -m pip install -e .

The distribution/import split is intentional:

  • PyPI package: stui-terminal
  • Python import: import stui as st
  • CLI command: stui
  • Module CLI fallback: python -m stui

That split is also what users see on PyPI: install stui-terminal, then import and run stui.

60-Second Quickstart

Create a file named app.py:

import stui as st

st.title("Hello from the terminal")

name = st.text_input("Name", "MarMar")
level = st.slider("Level", 1, 10, 5)

if st.button("Greet"):
    st.success(f"Hi {name}. Level {level} selected.")

Install, launch a bundled demo, then run the app you just wrote:

python -m pip install stui-terminal
stui demo dashboard
stui run app.py

Or generate a starter file instead of writing app.py by hand:

stui init starter_app.py
stui run starter_app.py

If the stui command is not on your PATH, use the module entry point:

python -m stui run app.py

Check your install and terminal details:

stui --version
stui doctor
stui doctor --json

Build Your First App

stui scripts rerun when users interact with widgets. Use st.session_state for values that should survive reruns:

import stui as st

st.title("Counter")

if "count" not in st.session_state:
    st.session_state.count = 0

step = st.slider("Step", 1, 10, 1)

if st.button("Add"):
    st.session_state.count += step

if st.button("Reset"):
    st.session_state.count = 0

st.write("count =", st.session_state.count)

Run it with:

stui run app.py

Start from the repository examples when you want a larger reference:

stui run examples/counter.py
stui run examples/inputs.py
stui run examples/data_display.py
stui run examples/dashboard.py
stui run examples/forms.py
stui run examples/layouts.py
stui run examples/charts.py
stui run examples/kitchen_sink.py

Those examples/... paths are repository files. Installed packages also expose bundled examples that can be listed or copied into any working directory:

stui demo list
stui demo basic
stui demo dashboard
stui demo forms
stui demo charts
stui demo kitchen_sink
stui examples
stui example list
stui example copy basic ./basic.py
stui run ./basic.py
stui example copy counter ./counter.py
stui run ./counter.py
stui init ./new_app.py
stui init ./dashboard.py --template dashboard
stui init ./forms_app.py --template forms

Automated tests cover demo CLI behavior and bundled-resource resolution without starting a full TUI. For an interactive smoke check, run stui demo dashboard from any directory and press q to quit.

stui init currently supports the basic, dashboard, and forms templates. Use python -m stui ... for the same commands when the stui script directory is not on PATH.

Copied examples and generated starter files are plain Python scripts. They do not require a checkout after they are copied:

stui example copy forms ./forms_app.py
python -m stui run ./forms_app.py

stui init ./signup.py --template forms
python -m stui run ./signup.py

stui init ./ops_dashboard.py --template dashboard
python -m stui run ./ops_dashboard.py

The v1.0.0 release treats these demo/example/init/copy commands as part of the stable documentation contract. If an installed-package flow does not work without a repository checkout, that is patch-release-worthy docs or packaging debt.

The demo screenshot above is generated from a real terminal app in this repository, not a browser mockup. If the image on PyPI or GitHub ever drifts from the current package behavior, treat that as release polish debt.

For more detail, see the API reference, API stability labels, terminal compatibility matrix, and v1 readiness checklist.

Copy-Paste Examples

Slider and Button

import stui as st

st.title("Slider and Button")

if "runs" not in st.session_state:
    st.session_state.runs = 0

threshold = st.slider("Threshold", 0.0, 1.0, 0.5, step=0.1)

if st.button("Run"):
    st.session_state.runs += 1
    st.success(f"Run {st.session_state.runs} at threshold {threshold}")

Inputs

import stui as st

st.title("Inputs")

name = st.text_input("Name", "MarMar")
batch = st.number_input("Batch size", min_value=1, max_value=128, value=16)
model = st.selectbox("Model", ["tiny", "base", "large"], index=1)
mode = st.radio("Mode", ["fast", "balanced", "careful"], index=1)
dry_run = st.checkbox("Dry run", value=True)

st.write("name =", name)
st.write("batch =", batch)
st.write("model =", model)
st.write("mode =", mode)
st.write("dry run =", dry_run)

Table and Dataframe

import stui as st

st.title("Runs")

rows = [
    {"name": "baseline", "accuracy": 0.81, "latency_ms": 42},
    {"name": "quantized", "accuracy": 0.79, "latency_ms": 24},
    {"name": "distilled", "accuracy": 0.77, "latency_ms": 18},
]

st.table(rows)
st.dataframe({"setting": ["device", "batch"], "value": ["cpu", 16]})

Progress and Status

import stui as st

st.title("Job Status")

complete = st.slider("Complete", 0, 100, 35)
st.progress(complete, text="current job")

if complete == 100:
    st.success("Done")
elif complete >= 75:
    st.warning("Almost there")
else:
    st.info("Running")

Why terminal-native?

Some useful Python apps do not need a browser runtime. stui keeps the interface inside the terminal so it can fit naturally into:

  • SSH sessions, remote machines, and headless boxes.
  • Internal tools where opening ports or managing local server URLs is friction.
  • Offline or locked-down environments where browser access is limited.
  • Model, data, and DevOps workflows that already start from a shell.

That also keeps the boundary simple: stui does not start a web server, use websockets, require port-forwarding, or depend on Streamlit at runtime.

Commands

# Install the package from PyPI.
python -m pip install stui-terminal

# Run a bundled first-run demo, then create and run an app.
stui demo dashboard
stui init app.py
stui run app.py
python -m stui run app.py

# List, copy, or create starter examples.
stui demo list
stui examples
stui example list
stui example copy counter ./counter.py
stui init ./new_app.py
stui init ./dashboard.py --template dashboard
stui init ./forms_app.py --template forms

# Print version and install/terminal diagnostics.
stui --version
stui doctor
stui doctor --json

# Install the project for local development from a checkout.
python3.11 -m venv .venv
. .venv/bin/activate
python -m pip install -e ".[dev]"

# Run the smoke-size example app.
stui run examples/basic.py

# Run the stateful counter example.
stui run examples/counter.py

# Run the deterministic model-parameter demo.
stui run examples/model_demo.py

# Run the test suite.
python3.11 -m pytest

Keyboard Shortcuts

  • q: quit the app
  • r: rerun the script
  • tab: focus the next widget
  • shift+tab: focus the previous widget
  • enter or space: press the focused button
  • space: toggle the focused checkbox
  • enter in text and number inputs: submit the edited value
  • enter, right, or down: move a selectbox to the next choice
  • left or up: move a selectbox to the previous choice
  • arrow keys in radio groups: choose another option
  • enter or space: toggle a focused expander
  • left or h: decrease the focused slider
  • right or l: increase the focused slider
  • home: set the focused slider to its minimum value
  • end: set the focused slider to its maximum value

Some lower-level editing and focus behavior comes from Textual and can vary by terminal. See Terminal Compatibility and the v1 compatibility gate for the current checklist.

API

Import the API as:

import stui as st

The public API is intentionally compact and Streamlit-inspired, not Streamlit-compatible.

For the working API reference, see docs/api-reference.md. The current API contract and v1 stability checklist are tracked in docs/v1-readiness.md#api-contract-status and docs/v1-readiness.md#stable-api. The terminal support checklist lives in docs/terminal-compatibility.md.

Area APIs Status in v1.0.0
Text st.title, st.header, st.subheader, st.caption, st.text, st.markdown, st.write, st.divider v1-stable
Status st.info, st.success, st.warning, st.error, st.exception v1-stable
Status/help primitives st.status, st.spinner, st.help Pre-v1 experimental while terminal grouping/help formatting gathers feedback
Display st.code, st.json, st.progress, st.table, st.dataframe Mixed: st.code is v1-stable; st.json, st.progress, st.table, and st.dataframe are pre-v1 experimental static terminal displays
Inputs st.button, st.slider, st.text_input, st.checkbox, st.number_input, st.selectbox, st.radio Mixed: core inputs are v1-stable; st.number_input, st.selectbox, and st.radio are pre-v1 experimental
Forms st.form, st.form_submit_button Pre-v1 experimental deferred commit to session_state until submit
Layout/grouping st.container, st.columns, st.expander Pre-v1 experimental terminal grouping primitives; columns stack on narrow terminals
Metrics and charts st.metric, st.bar_chart, st.line_chart Pre-v1 experimental terminal summaries, not plotting replacements
State and flow st.session_state, st.rerun, st.stop Mixed: st.session_state is v1-stable; st.rerun and st.stop are pre-v1 experimental flow control
Package metadata st.__version__ v1-stable package version string
CLI and examples stui run, stui demo list, stui demo NAME, stui examples, stui example list, stui example copy, stui init, stui doctor, stui --version v1-stable command surface

Inputs support stable key values and optional callbacks where the function signature documents them. Tables and charts are simple static displays and do not require pandas or plotting dependencies.

Stable API

The v1.0.0 stable surface is intentionally small: text/status output, st.code, core inputs, st.session_state, and the documented CLI/demo/example commands. These names should keep their call shape, return type, and basic behavior through v1 unless a correctness, terminal, or security issue forces a change.

Experimental API

The documented experimental APIs are public enough to try, but they are not promised as frozen v1 behavior yet. This includes forms, grouping/layout helpers, selection/numeric widgets, status/help helpers, static data display beyond st.code, metrics/charts, and flow control. Release notes should call out any change with a migration note when practical.

APIs not shown in this table should be treated as private implementation details. Experimental display/layout helpers may still tighten in v1.x unless they are promoted in the API stability docs and covered by release notes. Deferred APIs for v1 include st.sidebar, st.tabs, st.file_uploader, st.cache_data, st.cache_resource, st.components, editable dataframes, custom column ratios/gaps, plotting-library parity, and browser/server runtime features.

Compatibility

stui is Streamlit-inspired, not Streamlit-compatible. Existing Streamlit apps usually need small edits before they run in stui; unsupported calls should be removed or replaced with the compact API above.

Runtime expectations:

  • Python 3.11 or newer.
  • Terminal UI powered by Textual and Rich.
  • No Streamlit runtime dependency.
  • No browser tab, local web server, websocket, or port-forwarding flow.
  • Static table/dataframe display without dataframe editing or sorting.
  • Common modern terminals should work best with UTF-8, color support, and a normal interactive TERM such as xterm-256color.

See Terminal Compatibility for the current evidence matrix and report format. v1.0.0 stays evidence-driven: common modern terminals are expected targets, but environments without project-owned evidence remain labeled test-needed instead of claimed as fully supported.

Common Mistakes

  • Installing stui instead of stui-terminal. The PyPI package is stui-terminal; the import and CLI are stui.
  • Running stui run from a different Python environment than the one where the package was installed. Try python -m stui run app.py.
  • Expecting a browser dashboard. stui renders inside your terminal.
  • Reusing Streamlit-only APIs such as sidebars, file upload, caching decorators, or arbitrary components. They are not part of this small API.
  • Assuming newer APIs are present in an older install. Check python -c "import stui; print(stui.__version__)" before using forms, grouping primitives, metrics, charts, or packaged examples.
  • Doing slow network or model work at top level. Scripts rerun after interactions, so keep top-level work light and cache expensive work yourself.
  • Forgetting stable key values when creating similar widgets in loops.

Troubleshooting

stui: command not found

Make sure you installed into the same Python environment that your shell is using:

python -m pip install stui-terminal
python -m stui --version

If python -m stui --version works but stui --version does not, your environment's script directory is not on PATH. Running through python -m stui ... is a reliable workaround.

Python Version

stui requires Python 3.11 or newer:

python --version

If that prints an older version, create a 3.11+ environment first:

python3.11 -m venv .venv
. .venv/bin/activate
python -m pip install stui-terminal

Terminal Rendering

stui renders a Textual app inside your terminal. For the best results, use a modern terminal with UTF-8 and color support. If borders, focus rings, or block characters look wrong, try another terminal app, make the window wider, and check that TERM is set to a normal interactive terminal value such as xterm-256color.

macOS Editable Install Quirk

If a local editable install appears to succeed but import stui or stui --version cannot find the package on macOS, check whether the virtual environment or editable-install .pth file was marked hidden:

chflags -R nohidden .venv
python -m pip install -e ".[dev]"
python -m stui --version

Examples

The commands below use repository paths from a checkout. If you installed stui-terminal from PyPI and do not have this repository, copy a bundled example first:

stui example copy counter ./counter.py
stui run ./counter.py

Counter

examples/counter.py shows a minimal stateful app with increment, decrement, and reset controls.

stui run examples/counter.py

Model Demo

examples/model_demo.py shows a small model-parameter playground using text input, checkbox, sliders, status messages, session state, and deterministic scoring. It is intentionally local and fake: there are no network calls or model dependencies.

stui run examples/model_demo.py

Inputs

examples/inputs.py shows text, numeric, selectbox, radio, checkbox, and button controls together.

stui run examples/inputs.py

Data Display

examples/data_display.py shows tables, JSON, and code output.

stui run examples/data_display.py

Dashboard

examples/dashboard.py combines controls, progress, status messages, and a small table into a compact terminal control panel.

stui run examples/dashboard.py

Forms

examples/forms.py shows the experimental form flow: form widget display values can change during reruns, but keyed values commit to session_state on submit.

stui run examples/forms.py

Layouts

examples/layouts.py shows responsive columns, container, and keyboard-toggleable expander patterns. The design notes are in docs/layouts.md.

stui run examples/layouts.py

Charts

examples/charts.py shows metric, bar_chart, and line_chart helpers with source data shown in a table.

stui run examples/charts.py

Kitchen Sink

examples/kitchen_sink.py exercises the stable API surface plus the experimental terminal-app primitives that remain useful feedback targets before v1.

stui run examples/kitchen_sink.py

Limitations

  • No browser, web server, websocket, or port-forwarding runtime.
  • No Streamlit dependency and no promise of Streamlit compatibility.
  • Forms still rerun the Textual app when a form widget changes, but pending form values stay out of session_state until submit.
  • Expanders are keyboard-toggleable with Enter/Space and persist their state; this is still a modest terminal grouping primitive, not a full layout system.
  • Columns accept only an integer count, stack when the terminal is narrow, and do not support custom ratios, sidebars, tabs, browser grids, or horizontal scrolling.
  • Charts are compact terminal summaries, not plotting-library replacements. st.bar_chart supports signed values and zero-only data; st.line_chart is a simple static sparkline for numeric lists or dictionaries of numeric series.
  • st.status, st.spinner, and st.help are display helpers, not live animation, background task, or pager systems.
  • st.rerun and st.stop are small flow-control helpers, not a full job scheduler or async runtime.
  • No sidebars, file upload, browser components, or caching decorators yet.
  • Tables are static display only; there is no full dataframe editing or sorting.
  • Slider input supports numeric values only.
  • Layout remains terminal-first and intentionally modest.
  • The app reruns the script as interactions change state, so examples should keep top-level work lightweight.
  • Error handling is still early and meant for development feedback.
  • Experimental APIs remain public, but may still tighten in v1.x releases with release-note coverage and migration notes when practical.
  • Public announcement copy is prepared for v1.0.0, but social posts should be published manually only after PyPI install, docs, examples, CI, and terminal compatibility are verified together.

Non-Goals

stui is deliberately not trying to become:

  • A Streamlit compatibility layer or migration tool.
  • A browser dashboard framework.
  • A hosted/cloud product with auth, sync, collaboration, or deployment management.
  • A plotting library or dataframe editor.
  • A large component marketplace before the terminal API is stable.
  • A wrapper around GPL slider/widget code or textual-slider.

v1.0.0 Stable Status

v1.0.0 is the first stable release. The stable API contract, PyPI install path, bundled demo/example commands, starter templates, docs, CI, and release notes are verified together for this line.

The remaining experimental APIs and terminal compatibility unknowns are visible instead of hidden. Post-v1 work should be feedback-driven and kept out of the core stable API unless it has enough real terminal evidence.

See ROADMAP.md and docs/v1-readiness.md for the full path to v1.

Contributing

See CONTRIBUTING.md for the local development workflow and project boundaries.

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

stui_terminal-1.0.0.tar.gz (1.0 MB view details)

Uploaded Source

Built Distribution

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

stui_terminal-1.0.0-py3-none-any.whl (41.4 kB view details)

Uploaded Python 3

File details

Details for the file stui_terminal-1.0.0.tar.gz.

File metadata

  • Download URL: stui_terminal-1.0.0.tar.gz
  • Upload date:
  • Size: 1.0 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for stui_terminal-1.0.0.tar.gz
Algorithm Hash digest
SHA256 0448e610a8627af7a95e2e932ff2596344c70763decf45d97ba8dfcfe0981179
MD5 b5c23f5ebf85a4995e8cba9ac51451ab
BLAKE2b-256 94c6a0de5f2e0fdbba4368ef96df0baa251f3972181faa88ca9507c7811eca29

See more details on using hashes here.

Provenance

The following attestation bundles were made for stui_terminal-1.0.0.tar.gz:

Publisher: publish.yml on marmar9615-cloud/stui-terminal

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

File details

Details for the file stui_terminal-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: stui_terminal-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 41.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for stui_terminal-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 22291df3caa02ffb04f99cdfec74db24a08aee2e327012845ebb44dff50dcc82
MD5 a19332b5e4a2d0ea50652a31db93c0d3
BLAKE2b-256 8de472299e9d7ada06fb9e0ea13b8838c1f105f2eade0e4b0698f54c1ed3fe9a

See more details on using hashes here.

Provenance

The following attestation bundles were made for stui_terminal-1.0.0-py3-none-any.whl:

Publisher: publish.yml on marmar9615-cloud/stui-terminal

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