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.
How it works
- A
Config(from a JSON file, or built by running a real plugin) describes the search bar and its results. renderer.pyrenderstemplates/base.htmlwith Jinja2 into a temporaryoutput.html.screenshot.pyloads that file in headless Chromium and screenshots it with a transparent background.image.pycrops to the smallest bounding box and writesoutput_<timestamp>_<id>.pngto 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 |
-s, --css |
Stylesheet(s) to render with, e.g. win11-dark.css or win11-dark.css ad-neon.css (only applies with -p/-u; 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.
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.Hotkeykeep 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-*.csshero 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/-Hon 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>.csson 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,..."
}
]
}
iconaccepts a data URI or a path relative to the config file — relative paths are resolved and inlined automatically.selectionis the index of the highlighted row.query_suggestionis auto-filled, when left empty, with the selected result's title — but only if the typedqueryis 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_resultscaps how many entries fromresultsactually get rendered (any beyond it are dropped from the screenshot). Whenresultshas more entries thanmax_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 onlymax_resultsare actually shown.cssnames a stylesheet (or a list of stylesheets, e.g.["win11-dark.css", "ad-neon.css"]) to inline on top of the defaultstyle.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 bundledstatic/, 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 localbase.htmloverrides the bundled one.- For product-page hero shots,
hero-win11-light.cssandhero-win11-dark.cssrender 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 realwin11-light.css/win11-dark.csslauncher chrome.hero-win11-accent.cssis 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"(orthemes/win11light-dark.css, etc.). Regenerate them from the current Flow Launcher release withmake themes, which refreshes the vendored XAML inthemes/xaml/and rewrites the CSS.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file flow_render-0.14.2.tar.gz.
File metadata
- Download URL: flow_render-0.14.2.tar.gz
- Upload date:
- Size: 1.2 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bf6636b9479d86c742d445ab6fd48a81396354404165acff24e2b51511aee30c
|
|
| MD5 |
ddb2c36b72b1c26e53c02a70ac59b364
|
|
| BLAKE2b-256 |
e8cdabac1c303330bd0978b3ff1785ceb0981c17fdb88d87311c95f497c97b29
|
Provenance
The following attestation bundles were made for flow_render-0.14.2.tar.gz:
Publisher:
release.yml on Garulf/flow-render
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
flow_render-0.14.2.tar.gz -
Subject digest:
bf6636b9479d86c742d445ab6fd48a81396354404165acff24e2b51511aee30c - Sigstore transparency entry: 2337921403
- Sigstore integration time:
-
Permalink:
Garulf/flow-render@d651a679a22ec73c876e433e25406db438355e81 -
Branch / Tag:
refs/tags/v0.14.2 - Owner: https://github.com/Garulf
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d651a679a22ec73c876e433e25406db438355e81 -
Trigger Event:
push
-
Statement type:
File details
Details for the file flow_render-0.14.2-py3-none-any.whl.
File metadata
- Download URL: flow_render-0.14.2-py3-none-any.whl
- Upload date:
- Size: 113.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
da05eecf2a426d88c6a4f656297e4027c3d424e661d53ac680069e4b95010181
|
|
| MD5 |
cc038dccc6634ab531b03c9d3b15929f
|
|
| BLAKE2b-256 |
64b772ad174d30534e8625f5477eff992cef7cf2636ae02f41ac5ac04b80d83b
|
Provenance
The following attestation bundles were made for flow_render-0.14.2-py3-none-any.whl:
Publisher:
release.yml on Garulf/flow-render
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
flow_render-0.14.2-py3-none-any.whl -
Subject digest:
da05eecf2a426d88c6a4f656297e4027c3d424e661d53ac680069e4b95010181 - Sigstore transparency entry: 2337921429
- Sigstore integration time:
-
Permalink:
Garulf/flow-render@d651a679a22ec73c876e433e25406db438355e81 -
Branch / Tag:
refs/tags/v0.14.2 - Owner: https://github.com/Garulf
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d651a679a22ec73c876e433e25406db438355e81 -
Trigger Event:
push
-
Statement type: