Skip to main content

flow-render

Renders a Flow Launcher search-result mockup to HTML, screenshots it headlessly with Playwright, and crops the result to a transparent PNG — useful for generating plugin screenshots for READMEs and store listings without staging a real launcher window.

example output

How it works

  1. A Config (from a JSON file, or built by running a real plugin) describes the search bar and its results.
  2. renderer.py renders templates/base.html with Jinja2 into a temporary output.html.
  3. screenshot.py loads that file in headless Chromium and screenshots it with a transparent background.
  4. image.py crops to the smallest bounding box and writes output_<timestamp>_<id>.png to the output directory.

Intermediate build files live in a temp directory that's cleaned up automatically after each run — nothing is left behind wherever you happen to invoke the CLI from. The final PNG defaults to a per-user data directory (%LOCALAPPDATA%\flow-render\output on Windows, ~/Library/Application Support/flow-render/output on macOS, $XDG_DATA_HOME/flow-render/output or ~/.local/share/flow-render/output on Linux) and accumulates there across runs; override it per-run with -o/--output.

Requirements

  • Python 3.10+
  • Chromium via Playwright (installed automatically by the run scripts)

Install

Install as a standalone CLI with uv:

uv tool install .
playwright install chromium
flow-render -c ./example/config.json

Usage

Linux / macOS / WSL

make run CONFIG=./example/config.json

Other targets and variables:

make help
make run PLUGIN=./path/to/plugin QUERY=test
make run PLUGIN_MANAGER=1 PLUGIN=./path/to/plugin
make run CONFIG=./example/config.json SKIP_PLAYWRIGHT=1   # skip the Chromium install step

make creates .venv, installs the package with uv pip install -e ., and installs Chromium on first run; later runs reuse the existing venv.

Windows

.\run.ps1 -c .\example\config.json

run.ps1 does the same venv + Playwright bootstrap, prompting for elevation only if the Chromium install needs it, then forwards all arguments to the CLI.

Direct CLI

flow-render -c ./example/config.json
Flag Meaning
-c, --config Path to a config JSON file
-p, --plugin Path to a Flow Launcher plugin directory, or just its name (see below)
-u, --plugin-url URL or local path to a plugin .zip; extracted to a temp dir and used like -p
-q, --query Query to run against the plugin
-i Render the plugin-manager "pm install" view for the given plugin (works with -p or -u) instead of running a query
--empty Render the idle search window (placeholder text and clock, no results) without a plugin
--clock Time shown by --empty, e.g. "02:42 PM" (defaults to the current time)
-s, --css Stylesheet(s) to render with, e.g. win11-dark.css or win11-dark.css ad-neon.css (only applies with -p/-u/--empty; a config file's own css field takes precedence with -c)
-o, --output Directory to save the rendered PNG in (defaults to a per-user data directory, see above)
-m, --max-results Maximum number of results to render (only applies with -p/-u; default: 3)
-W, --width Screenshot width in px (overrides any canvas size baked into the selected theme; defaults to 1280)
-H, --height Screenshot height in px (overrides any canvas size baked into the selected theme; defaults to 720)
--print-json Print the resolved config as JSON to the console
--save-json Also save the config as JSON next to the rendered PNG in the output directory (-o)
--hide-caret Hide the blinking text caret in the query box

-s/--css names don't need the .css extension — -s win11-dark and -s win11-dark.css are equivalent.

With -p, the plugin's ExecuteFileName is invoked as a subprocess with a {"method": "query", "parameters": [query]} request and its results become the rendered rows. -u is mutually exclusive with -p — the zip is extracted to a temporary directory (its plugin.json is located automatically, even if the zip wraps everything in a subfolder), then handled exactly like -p.

-p doesn't have to be a path: if the given value isn't an existing directory, it's treated as a plugin name — first a matching folder in the current directory is looked for, then (on Windows) one in Flow Launcher's own %APPDATA%\FlowLauncher\Plugins. Both searches match the folder name exactly, or a <name>-<version> prefix (how Flow Launcher itself names installed plugin folders) if that match is unambiguous. So from inside %APPDATA%\FlowLauncher\Plugins, -p "Steam Search" just works; from anywhere else, it falls back to whatever's actually installed there.

With -i, no query is run — instead it renders the plugin-manager mockup you'd see after typing pm install <name>: query box shows pm install {Name}, and the single result is {Name} by {Author} / {Description}, all read straight from the plugin's plugin.json.

With --empty, no plugin is needed. It renders what Flow Launcher shows before anything is typed: a "Type here to search" placeholder in the theme's suggestion color, a clock, and the search glyph, with the results list collapsed:

flow-render --empty -s win11-dark --clock "02:42 PM"

Edit mode

flow-render edit -p ./plugin opens a local browser tab with a live, editable preview of the mockup — useful for building promo/hero-shot themes (the ad-*.css family) without hand-tuning CSS and re-running screenshots to check the result.

flow-render edit -p ./plugin                       # blank starting theme
flow-render edit -p ./plugin -q "install"          # preview against a query
flow-render edit -p ./plugin -s ad-neon.css        # continue editing a theme
flow-render edit -p ./plugin -q "install" -m 5     # show up to 5 results

-m/--max-results works the same as on the normal command. -o, -i, -c, -W, -H don't apply to edit (canvas size is set live in the editor instead of -W/-H).

In the editor:

  • Click any element in the preview (the window, icon, a result's title, etc.) to select it, then adjust its translate X/Y/Z, rotate X/Y/Z, scale, opacity, perspective, and drop shadow (offset X/Y, blur, color, opacity) with the sliders — each has a live-editable number box and a reset (↺) button, with wide ranges (e.g. translate up to ±2000px, rotate up to ±360°, scale up to 5x) to cover everything from subtle nudges to dramatic hero-shot compositions. You can also drag an element directly in the preview to set translate X/Y, and scroll the mouse wheel over it to adjust scale — both stay in sync with the slider panel. Because these are shared CSS selectors, editing e.g. "Title" affects every result row, not just one. .icon, #GlassIcon, and .Hotkey keep their own built-in vertical centering underneath whatever you add on top.
  • "Perspective" (0 = off) is what actually gives translateZ/rotateX/rotateY real 3D depth — without it, Z movement has no visible foreshortening. Raise it for a stronger "pop toward the viewer" effect, needed for the tilted-window look the ad-*.css hero themes use.
  • Transforming anything other than the window itself (an icon, a title, etc.) lets it visually extend past the window's edges — e.g. a large translateZ or scale can make an icon "float" off the window — instead of being clipped to the window's bounds.
  • "Selected result" picks which row is highlighted, for previewing the selected-row styling on any of the plugin's actual results. "Selected row" in the Elements list is a separate selectable target (.selecteditem) for transforming whichever row that is — e.g. scaling it up slightly as emphasis — independently of "Result row", which affects every row.
  • The canvas (behind the window) is edited globally, not per element: choose exactly one of theme default, gradient, or transparent — a transparent canvas shows a gray/white checkerboard in the editor so it reads as "transparent," not just "dark." A gradient can be linear (with an angle) or radial (with a center X/Y position instead of an angle), with 2 to 6 color stops (each with its own position); "+ Add color stop" and a per-stop ✕ button (once past 2 stops) manage the list. The canvas's pixel bounds are outlined in the preview. Canvas width/height set the actual final screenshot's pixel size (not just the live preview, up to 8000px) — saved as a /* flow-render-canvas: WxH */ marker at the top of the theme, which the normal (non-edit) command reads automatically when that theme is selected with -s. -W/-H on the command line override it.
  • "Window shine" overlays a gradient on top of the window's own content, for faking a glossy highlight — enable it, then pick linear (with an angle) or radial (with a center X/Y position), and up to 6 color stops. Unlike the canvas gradient, each stop has its own opacity slider, since a shine only reads as a highlight with stops that fade to fully transparent rather than a solid color.
  • "+ Add layer" adds a freeform text layer (up to 4), each a sibling of the window rather than nested inside it, so it doesn't inherit the window's own rotation — with its own Jinja2 template (e.g. {{ plugin.Name }}), a choice of font family (several web-safe options, or the theme's default), font size, color with its own opacity slider, weight, plus the same translate/rotate/scale/opacity/shadow controls as elements. The template stays live in the saved CSS: reusing the theme against a different plugin later updates the text automatically. "🗑 Remove layer" deactivates the currently selected layer, freeing its slot for reuse.
  • "Save" writes src/flow_render/static/<name>.css, ready to use immediately with -s <name>.css on the normal (non-edit) command.
  • "📷 Capture screenshot" runs the real screenshot pipeline against whatever you're currently editing (saved or not) and writes it to the normal per-user output directory — no need to save first or leave the editor.

Themes

example/config.json rendered against the default styling, the two bundled Windows 11 mockups, and every theme generated from the current Flow Launcher release (see static/themes/ below). Regenerate these with:

uv run python scripts/generate_theme_screenshots.py

Default (no theme)

win11-dark.css

win11-light.css

themes/blurblack.css

themes/blurblack-darker.css

themes/blurwhite.css

themes/circle-system-dark.css

themes/circle-system-light.css

themes/cyan-dark.css

themes/darker.css

themes/darker-glass.css

themes/discord-dark.css

themes/dracula.css

themes/gray.css

themes/league.css

themes/midnight.css

themes/nord-darker.css

themes/pink.css

themes/slimlight.css

themes/sublime.css

themes/ubuntu.css

themes/win10system-dark.css

themes/win10system-light.css

themes/win11light-dark.css

themes/win11light-light.css

Config format

See example/config.json:

{
    "keyword": "pm",
    "query": "install Steam Search",
    "icon": "data:image/png;base64,...",
    "max_results": 1,
    "selection": 0,
    "css": null,
    "query_suggestion": "",
    "results": [
        {
            "title": "Steam Search by Garulf",
            "subtitle": "Search and launch your Steam Games",
            "icon": "data:image/png;base64,..."
        }
    ]
}
  • icon accepts a data URI or a path relative to the config file — relative paths are resolved and inlined automatically.
  • selection is the index of the highlighted row.
  • When keyword and query are both empty, the window renders as Flow Launcher does before anything is typed: the "Type here to search" placeholder, a clock and the search glyph, with no results (any results in the config are ignored; see --empty). The optional clock field sets the time shown; leave it out to use the current time.
  • A query with no results collapses the window to just the query box.
  • query_suggestion is auto-filled, when left empty, with the selected result's title — but only if the typed query is a case-insensitive prefix of it. The untyped remainder renders as grayed-out ghost text after the cursor (the usual autocomplete look); no match means no suggestion.
  • max_results caps how many entries from results actually get rendered (any beyond it are dropped from the screenshot). When results has more entries than max_results, a cosmetic scrollbar thumb is drawn on the results list, sized to the visible/total ratio, to suggest more results exist below (this tool renders a single static screenshot, so it's a visual cue only, not an actually scrollable list). With -p/-u, up to 20 of the plugin's results are kept for this purpose even though only max_results are actually shown.
  • css names a stylesheet (or a list of stylesheets, e.g. ["win11-dark.css", "ad-neon.css"]) to inline on top of the default style.css. Each is applied in order, so later entries win the cascade over earlier ones. Each is looked up relative to the working directory first, then in the bundled static/, where several variants ship (hero.css, hero1.css … hero4.css, style.css, win11-light.css, win11-dark.css — the last two mimic Flow Launcher's "Windows 11" theme). Templates resolve the same way, so a local base.html overrides the bundled one.
  • For product-page hero shots, hero-win11-light.css and hero-win11-dark.css render the launcher centered on a mock Windows 11 desktop — taskbar, start button, pinned icons, and a couple of blank cascaded windows behind it — using the real win11-light.css/win11-dark.css launcher chrome. hero-win11-accent.css is the same desktop mockup with a saturated accent wallpaper instead, on the default launcher theme.
  • Generated Flow Launcher themes live in the bundled static/themes/ — use them with "css": "themes/dracula.css" (or themes/win11light-dark.css, etc.). Regenerate them from the current Flow Launcher release with make themes, which refreshes the vendored XAML in themes/xaml/ and rewrites the CSS.

Metadata

Release files for flow-render 0.15.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for flow-render 0.15.0
File Size Uploaded
flow_render-0.15.0.tar.gz 1.7 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for flow-render 0.15.0
File Interpreter ABI Platform
flow_render-0.15.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.8 MB

Release files / flow_render-0.15.0.tar.gz

Download URL flow_render-0.15.0.tar.gz
Size 1.7 MB
Tags Source
SHA-256 checksum
How to use checksums
0cf853006e07f67a31a70833ee0a0013b7f783de76bd5eadeecccf66f95f72a2
BLAKE2b-256 checksum
How to use checksums
f86ca0b57165dd627183e2b3ff8ad3cb3af4763f82209cbba9a49b16fd9453c1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 3, 2026.

Transparency log

Release files / flow_render-0.15.0-py3-none-any.whl

Download URL flow_render-0.15.0-py3-none-any.whl
Size 116.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1835a04c91b74179e18746737c999a25c492d99e9d4caffd766601c18ced0ae8
BLAKE2b-256 checksum
How to use checksums
0cb2d7a44d180d44ab2a3a1413cc2625fc8d2d27f3d58b0642c8886e7cabeb95
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 3, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.15.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page