A modern interactive terminal picker for creating interactive command-line selection interfaces
Project description
Pyckify ๐ฏ
A modern, feature-rich Python library for creating interactive command-line selection interfaces. Pyckify (pick-it-for-you) offers an enhanced selection experience with support for multiselect, grouping, filtering, search, and rich styling.
Features ๐
- ๐จ Rich terminal UI with 8 built-in themes, live-switchable with
t - โจ Single and multi-selection modes
- ๐ Fuzzy search with ranked results and optional score display
- ๐ท๏ธ Option grouping, tagging, and tag-filter mode (
#key) - โจ๏ธ Keyboard shortcuts with smart conflict resolution
- ๐ฏ Custom filtering and 4 sort modes (
skey cycles) - ๐ Option descriptions, icons, per-option Rich style overrides
- ๐๏ธ Preview pane (bottom or right) for long-form option detail
- ๐ Clipboard copy (
ckey) - โก Smooth scrolling, Page Up/Down, Home/End navigation
- ๐ญ Disabled options and visual
Separatorsupport - ๐ช Windows/Unix compatible
- ๐ป Multiple multiselect indicator styles:
circle,check,star,radio - ๐งฐ Helper utilities:
make_options(),from_dict()
Installation ๐ฆ
pip install pyckify
Quick Start ๐ฎ
Basic Usage
from pyckify import Pyck, Option
# Simple string options
options = ["Red", "Blue", "Green", "Yellow"]
selected, index = Pyck(options, title="Choose a color")
print(f"Selected color: {selected}")
# Using Option objects
options = [
Option("๐ Apple", description="Fresh from the garden"),
Option("๐ Banana", description="Rich in potassium"),
Option("๐ Orange", description="Vitamin C boost")
]
selected, index = Pyck(options, title="Choose a fruit")
print(f"Selected fruit: {selected.label}")
Advanced Usage ๐ง
Multi-select with Constraints
from pyckify import Pyck, Option
options = [
Option("Python", description="General-purpose language"),
Option("JavaScript", description="Web development"),
Option("Rust", description="Systems programming"),
Option("Go", description="Cloud infrastructure")
]
result = Pyck(
options=options,
title="Select Programming Languages",
subtitle="Choose 2-3 languages for your project",
multiselect=True,
minSelectionCount=2,
maxSelectionCount=3,
separateValues=True # Returns a PickResult object
)
if result:
print("\nSelected languages:")
for lang in result.values:
print(f"- {lang.label}: {lang.description}")
Grouped Options with Icons and Shortcuts
options = [
# Development Tools
Option("๐ VS Code",
description="Popular code editor",
group="Development Tools",
shortcut="v",
tags=["editor", "free"]),
Option("โก PyCharm",
description="Python IDE",
group="Development Tools",
shortcut="p",
tags=["ide", "paid"]),
# Version Control
Option("๐บ GitHub",
description="Code hosting platform",
group="Version Control",
shortcut="g",
tags=["git", "cloud"]),
Option("๐ฆ GitLab",
description="DevOps platform",
group="Version Control",
shortcut="l",
tags=["git", "cloud"])
]
result = Pyck(
options=options,
title="Development Stack",
subtitle="Select your tools",
multiselect=True,
group_by="group",
show_shortcuts=True
)
Advanced Grouped Options with Objects
options = (
[Option(f" ๐๏ธ {video}", group="๐๏ธ Video Tracks", value=video) for video in videos] +
[Option(f" ๐ {audio}", group="๐ Audio Tracks", value=audio) for audio in audios] +
[Option(f" ๐ฌ {subtitle}", group="๐ฌ Subtitle Tracks", value=subtitle) for subtitle in subtitles]
)
result = Pyck(
options=options,
group_by="group",
multiselect=True,
minSelectionCount=1,
separateValues=True,
)
results = [value.value for option in result for value in option if isinstance(value, Option)]
Output:
โโ navigate โข space select โข a select all โข enter confirm โข / search โข esc clear filters/quit
โ More options above
๐๏ธ Video Tracks
๐๏ธ VIDEO: BnGFobSd | avc1.4d401f | SDR | 480x360 | 901 kbps | 29.970 FPS
๐๏ธ VIDEO: 6EFRMq5M | avc1.4d401f | SDR | 480x360 | 494 kbps | 29.970 FPS
๐ Audio Tracks
๐ AUDIO: KuHayhsL | AAC | 2.0 | 128 kbps | yue
๐ AUDIO: eXSUTLgz | AAC | 2.0 | 128 kbps | en
โ ๐ AUDIO: QyEd8Mp6 | AAC | 2.0 | 128 kbps | el
โ More options below
Selected: 0 (minimum: 1)
Fuzzy Search with Ranked Results
result = Pyck(
options=options,
title="Language Picker",
fuzzy=True, # Enable fuzzy (ranked) search โ default True
show_fuzzy_score=True, # Show match score next to each option
separateValues=True,
)
Press / to enter search mode. Results are automatically ranked by match quality. Set confirm_on_single=True to auto-confirm when the search narrows to a single match.
Preview Pane
options = [
Option(
"๐ Python",
description="General-purpose scripting",
preview=(
"Python is a high-level, dynamically typed language prized for\n"
"its readability and vast ecosystem. Ideal for data science,\n"
"web backends, automation, and AI/ML workloads."
),
),
]
result = Pyck(
options=options,
title="Language Deep-Dive",
show_preview=True,
preview_position="bottom", # or "right"
separateValues=True,
)
Themes
result = Pyck(
options=options,
title="Themed Picker",
theme="dracula", # Set a theme at creation time
)
Press t at runtime to cycle through all available themes live. Available themes: default, dracula, nord, monokai, catppuccin, solarized, one_dark, gruvbox.
You can also switch themes programmatically:
from pyckify import set_theme
set_theme("catppuccin")
Multiselect Indicator Styles
result = Pyck(
options=options,
multiselect=True,
multiselect_indicator="radio", # "circle" | "check" | "star" | "radio"
)
Sort Modes
Press s to cycle through sort modes at runtime: original โ aโz โ zโa โ selected-first.
Tag Filtering
Press # to enter tag-filter mode and narrow options by tag. Tags are displayed inline when show_tags=True (default).
options = [
Option("Apple", tags=["fruit", "sweet"]),
Option("Lemon", tags=["fruit", "citrus"]),
Option("Carrot", tags=["vegetable"]),
]
result = Pyck(options=options, show_tags=True)
# Press # then type "citrus" to filter
Custom Filtering
from dataclasses import dataclass
from pyckify import Pyck, Option
@dataclass
class Language:
name: str
type: str
year: int
options = [
Option(f"๐ {lang.name}",
description=f"Created in {lang.year}",
value=lang,
tags=[lang.type])
for lang in [
Language("Go", "compiled", 2009),
Language("Rust", "compiled", 2010),
Language("Zig", "compiled", 2016),
]
]
def modern_languages(option: Option) -> bool:
return option.value.year >= 2010
result = Pyck(
options=options,
title="Modern Languages",
multiselect=True,
filter_fn=modern_languages,
)
Disabled Options and Separators
from pyckify import Pyck, Option, Separator
options = [
Separator("โโ Recommended โโโโโโโโโโโโโโโโโโ"),
Option("โจ Premium Plan", description="All features", style="bold green", tags=["paid"]),
Option("๐ Enterprise Plan", description="Custom SLA", style="bold cyan", tags=["paid"]),
Separator("โโ Legacy (unavailable) โโโโโโโโโ"),
Option("๐ Pro v1", description="Discontinued", enabled=False, tags=["legacy"]),
Separator("โโ Free tiers โโโโโโโโโโโโโโโโโโโโ"),
Option("โญ Hobbyist", description="500 req/day", tags=["free"]),
]
result = Pyck(
options=options,
title="Subscription Plans",
subtitle="Disabled options are shown but not selectable"
)
Helper Utilities
Build Option lists from plain dicts or any iterable:
from pyckify.options import from_dict, make_options
# From a list of dicts
options = from_dict([
{"label": "AWS S3", "description": "Object storage", "tags": ["storage"]},
{"label": "GCS", "description": "Google Cloud Storage", "tags": ["storage"]},
{"label": "Cloudflare R2","description": "Zero-egress S3", "tags": ["storage"]},
])
# From any iterable
options = make_options([1, 2, 3], label_fn=lambda x: f"Item {x}")
Per-Option Rich Style Overrides
Option("โจ Premium", style="bold green")
Option("๐ Enterprise", style="bold cyan")
Any valid Rich style string is accepted, overriding the active theme for that row only.
Clipboard Copy
Press c to copy the currently focused option's label to the system clipboard. Works on Windows, macOS, and Linux (requires xclip, xsel, or wl-copy).
API Reference ๐
Pyck() Function
The main function for creating selection interfaces.
def Pyck(
options: Sequence[OPTION_T],
title: Optional[str] = None,
subtitle: Optional[str] = None,
indicator: str = "โ",
defaultIndex: int = 0,
multiselect: bool = False,
minSelectionCount: int = 0,
maxSelectionCount: Optional[int] = None,
filter_fn: Optional[Callable[[OPTION_T], bool]] = None,
show_shortcuts: bool = True,
group_by: Optional[str] = None,
separateValues: bool = False,
theme: str = "default",
fuzzy: bool = True,
show_preview: bool = False,
preview_position: str = "bottom",
show_border: bool = False,
multiselect_indicator: str = "circle",
confirm_on_single: bool = False,
allow_empty: bool = False,
show_tags: bool = True,
show_fuzzy_score: bool = False,
max_visible: Optional[int] = None,
page_size: Optional[int] = None,
jump_on_shortcut_select: bool = False,
) -> Union[PickResult, List[PICK_RETURN_T], PICK_RETURN_T, None]
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
options |
Sequence |
โ | Items to display (strings or Option objects) |
title |
str |
None |
Header text |
subtitle |
str |
None |
Subheader text |
indicator |
str |
"โ" |
Cursor indicator symbol |
defaultIndex |
int |
0 |
Starting cursor position |
multiselect |
bool |
False |
Allow multiple selections |
minSelectionCount |
int |
0 |
Minimum required selections (multiselect) |
maxSelectionCount |
int |
None |
Maximum allowed selections (multiselect) |
filter_fn |
Callable |
None |
Custom predicate to hide options |
show_shortcuts |
bool |
True |
Render [key] shortcut badges |
group_by |
str |
None |
Option attribute to group by (e.g. "group") |
separateValues |
bool |
False |
Return a PickResult instead of raw tuples |
theme |
str |
"default" |
Built-in theme name |
fuzzy |
bool |
True |
Enable fuzzy (ranked) search |
show_preview |
bool |
False |
Display Option.preview in a pane |
preview_position |
str |
"bottom" |
"bottom" or "right" |
show_border |
bool |
False |
Wrap the picker in a Rich panel border |
multiselect_indicator |
str |
"circle" |
"circle", "check", "star", or "radio" |
confirm_on_single |
bool |
False |
Auto-confirm when search yields exactly one result |
allow_empty |
bool |
False |
Allow confirming with zero selections in multiselect |
show_tags |
bool |
True |
Render option tags inline |
show_fuzzy_score |
bool |
False |
Show fuzzy match score next to each option |
max_visible |
int |
None |
Override auto-detected visible row count |
page_size |
int |
None |
Rows scrolled per PgUp/PgDn (default: page height โ 1) |
jump_on_shortcut_select |
bool |
False |
Also toggle selection when jumping via shortcut in multiselect mode |
Returns None on Esc, a PickResult when separateValues=True, otherwise a (value, index) tuple or list of tuples.
Option Class
@dataclass
class Option:
label: str
value: Union[object, str, Any] = None
description: Optional[str] = None
enabled: bool = True
shortcut: Optional[str] = None
icon: Optional[str] = None
group: Optional[str] = None
tags: List[str] = field(default_factory=list)
preview: Optional[str] = None # Long-form text shown in preview pane
style: Optional[str] = None # Per-row Rich style string override
metadata: Dict[str, Any] = field(default_factory=dict) # App data bag
Attributes
| Attribute | Description |
|---|---|
label |
Display text shown in the list |
value |
Arbitrary payload returned on selection (defaults to label) |
description |
Short helper text rendered beside the label |
enabled |
When False, shown but cannot be selected |
shortcut |
Single character that jumps to this option |
icon |
Emoji or ASCII prefix prepended to the label |
group |
Group name used when group_by is set |
tags |
Free-form tags for display and tag-filter mode |
preview |
Long-form text shown in the optional preview pane |
style |
Rich style string to override the active theme for this row |
metadata |
Arbitrary dict for application-specific data |
Separator Class
@dataclass
class Separator(Option):
def __init__(self, label: str = "โ" * 30, description: Optional[str] = None):
super().__init__(label, description=description, enabled=False)
A non-selectable visual divider. Can be placed anywhere in the options list.
PickResult
class PickResult(NamedTuple):
values: Union[List[Any], Any]
indices: Union[List[int], int]
Returned when separateValues=True. Provides is_multi, as_list, and index_list convenience properties.
Helper Functions
from pyckify.options import from_dict, make_options
# Build Options from a list of dicts (keys match Option fields)
options = from_dict([{"label": "Alpha", "tags": ["a"]}, {"label": "Beta"}])
# Wrap any iterable in Option instances
options = make_options([1, 2, 3], label_fn=lambda x: f"Item {x}")
Keyboard Controls โจ๏ธ
| Key | Action |
|---|---|
โ / โ |
Navigate options |
PgUp / PgDn |
Scroll one page |
Home / End |
Jump to top / bottom |
Enter |
Confirm selection |
Space |
Toggle selection (multiselect) |
a |
Select / deselect all visible options |
i |
Invert selection among visible options |
/ |
Enable search |
# |
Filter by tag |
s |
Cycle sort mode (original โ aโz โ zโa โ selected-first) |
t |
Cycle theme live |
c |
Copy focused label to clipboard |
? |
Toggle help overlay |
Esc |
Clear active filter, or quit |
Shortcut priority: Option shortcuts always take precedence over global keys. For example, if an option has
shortcut="a", pressingajumps to it rather than triggering select-all.
Theming ๐จ
Eight built-in themes are available. Pass theme= to Pyck() or press t at runtime to cycle live.
# Available theme names
"default" | "dracula" | "nord" | "monokai" | "catppuccin" | "solarized" | "one_dark" | "gruvbox"
Each theme defines styles for every UI element:
custom_theme = {
"title": Style(bold=True, color="dark_orange"),
"subtitle": Style(italic=True, color="cyan"),
"indicator": Style(bold=True, color="bright_yellow"),
"selected": Style(bold=True, color="green"),
"active": Style(bold=True, color="white", bgcolor="blue"),
"active_selected": Style(bold=True, color="green", bgcolor="blue"),
"disabled": Style(dim=True, color="grey70"),
"description": Style(italic=True, color="bright_blue"),
"group_header": Style(bold=True, color="dark_orange"),
"shortcut": Style(bold=True, color="red"),
"tag": Style(italic=True, color="magenta"),
"search_match": Style(bold=True, color="white", bgcolor="dark_orange"),
# ... and more
}
Switch themes programmatically at any time:
from pyckify import set_theme
set_theme("gruvbox")
Examples ๐
Run the full feature showcase interactively:
python examples.py
The showcase covers: basic single-select, multiselect with min/max constraints, grouped options with shortcuts, fuzzy search and sort, preview pane, theme switching, separators with per-option styles, from_dict/make_options helpers, and confirm_on_single.
What's New
radioindicator style โ newmultiselect_indicatorvalue usingโ/โฏsymbolsjump_on_shortcut_selectโ in multiselect mode, pressing a shortcut key now optionally toggles that option's selection in addition to jumping the cursor- 4 new themes โ
catppuccin,solarized,one_dark,gruvbox confirm_on_singleโ auto-confirms when a search narrows to exactly one visible resultallow_emptyโ permits confirming with zero selections in multiselect modeshow_preview/preview_positionโ side or bottom preview pane usingOption.previewfuzzy/show_fuzzy_scoreโ ranked fuzzy search with optional score displayshow_tags/ tag-filter mode โ display tags inline and filter by tag with#show_borderโ wrap the picker in a Rich panel bordermultiselect_indicatorโ choose betweencircle,check,star,radiopage_size/max_visibleโ fine-grained control over scroll behaviours/t/ckeys โ sort cycling, live theme switching, clipboard copyfrom_dict()/make_options()โ convenience helpers inpyckify.optionsOption.previewโ long-form text for the preview paneOption.styleโ per-row Rich style string overrideOption.metadataโ arbitrary app data bag on each optioninvertSelectionโ now inverts only among currently visible (filtered) options- Shortcut conflict resolution โ option shortcuts always take priority over global keys
Contributing ๐ค
Contributions are welcome! Please feel free to submit a Pull Request.
License ๐
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments ๐
Author โ๏ธ
ReiDoBrega (@ReiDoBrega)
Made with โค๏ธ using Python
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
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 pyckify-0.1.3-py3-none-any.whl.
File metadata
- Download URL: pyckify-0.1.3-py3-none-any.whl
- Upload date:
- Size: 23.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7cfeb580bf30f5ae4aec135f39151f2ea9c904ca090ca7f62abf6bb87f1ffe6f
|
|
| MD5 |
8636c32c6c4eaa927c97465f1a267618
|
|
| BLAKE2b-256 |
e261ee041dabc2da32e514237b1fae9289ee9b698bc60c03fdd8d6e6bd6a45ae
|