xnano.components.options
xnano.components.options
¶
xnano.components.options
Display and search a list of choices with keyboard selection.
Classes:
-
Option–A labeled choice with optional distinct value and disabled flag.
-
Options–Always-visible, selectable choice list.
Functions:
-
get_fuzzy_match–Score
candidateagainstqueryas a fuzzy subsequence.
Attributes:
-
OptionsDirection(TypeAlias) –Visual order of option rows in the list.
-
FilterMode(TypeAlias) –How
querynarrows the visible items. -
AcceptPolicy(TypeAlias) –How
resolve_submissionreconciles typed text with the highlighted row. -
OptionItem(TypeAlias) –A single entry accepted by
Options.items.
OptionsDirection
module-attribute
¶
Visual order of option rows in the list.
FilterMode
module-attribute
¶
FilterMode: TypeAlias = Union[
Literal["fuzzy", "prefix", "none"],
bool,
Callable[[str, str], bool],
]
How query narrows the visible items.
"fuzzy"(orTrue): subsequence fuzzy match with scoring."prefix": keep items whose text starts with the query."none"(orFalse): show every item; the query is ignored, so an external driver can supply items that are already filtered.- a callable
(query, item_text) -> bool: keep items it returns truthy for.
AcceptPolicy
module-attribute
¶
How resolve_submission reconciles typed text with the highlighted row.
"replace": accept the highlighted item's text."extend": keep exactly what the user typed."if_prefix_only": keep the selection while the typed text is still an incomplete prefix of it; otherwise honor the typed text (so trailing args such as"/vibe hungry 7"are not dropped).
OptionItem
module-attribute
¶
OptionItem: TypeAlias = 'str | Text | Option'
A single entry accepted by Options.items.
Option
dataclass
¶
A labeled choice with optional distinct value and disabled flag.
Pass plain strings or Text values for simple choices. Use Option
when the stored value differs from its label or the choice is disabled.
Attributes:
-
label(str | Any) –Display text (plain string or styled
Textleaf). -
value(Any) –Stored value; defaults to the plain label text when
None. -
disabled(bool) –When
True, movement skips this entry and it cannot become the active selection viaselect.
Methods:
-
get_label_text–Return the plain-text form of
label. -
get_value–Return the stored value, falling back to the plain label.
value
class-attribute
instance-attribute
¶
value: Any = None
Stored value; defaults to the plain label text when None.
disabled
class-attribute
instance-attribute
¶
disabled: bool = False
When True, movement skips this entry.
get_label_text
¶
get_label_text() -> str
Return the plain-text form of label.
Source code in xnano/components/options.py
Options
dataclass
¶
Options(
items: Sequence[OptionItem] = (),
query: str = "",
filter: FilterMode = "fuzzy",
accept: AcceptPolicy = "replace",
searchable: bool = False,
selected: int = 0,
direction: OptionsDirection = "top_to_bottom",
foreground: ColorLike | None = None,
background: ColorLike | None = None,
highlight_color: ColorLike = "black",
highlight_background: ColorLike = "white",
highlight_symbol: str = "> ",
hovered: int | None = None,
hover_symbol: str = "· ",
match_color: ColorLike | None = "cyan",
repeat_highlight_symbol: bool = False,
focusable: bool = True,
passthrough: Sequence[str] = (),
*,
visible: bool = True,
z: int = 0,
fit_content: bool = True
)
Bases: Component
Always-visible, selectable choice list.
Up/down move the selection (skipping disabled entries) and value
reads the selected item:
class Picker(BaseGrid):
themes: Options = Field(
default=Options(items=THEME_NAMES),
)
@on_keyboard("enter")
def _choose(self, ctx: Context) -> None:
apply_theme(self.themes.value)
A plain list is not a search box: typing-to-filter is opt-in. Set
searchable=True to turn the list into a fuzzy filter where printable
keys edit query and matched characters are emphasized in
match_color — a focused searchable list intentionally consumes those
keys, so leave it off for a list that only browses, or the keys never
reach your command hooks. Either way you can drive query reactively
from another field.
Example
Options(items=("small", "medium", "large"), selected=1)
Attributes:
-
items(Sequence[OptionItem]) –Entries to pick from (strings,
Text, orOption). -
query(str) –Filter text edited by typing when
searchable. -
filter(FilterMode) –Whether
queryfuzzy-filters the visible items. -
searchable(bool) –Whether typing while focused edits
query(opt-in; defaults toFalseso a plain list does not swallow keys). -
selected(int) –Selection index within the filtered view.
-
direction(OptionsDirection) –Visual order of option rows.
-
foreground(ColorLike | None) –Default foreground color for unselected rows (deprecated alias:
color). -
background(ColorLike | None) –Default background color for unselected rows.
-
highlight_color(ColorLike) –Foreground of the selected row.
-
highlight_background(ColorLike) –Background of the selected row.
-
highlight_symbol(str) –Symbol prepended to the selected row.
-
hovered(int | None) –Row under the pointer (filtered-view index), or
None. -
hover_symbol(str) –Dim symbol prepended to the hovered row.
-
match_color(ColorLike | None) –Emphasis color for characters matched by
query. -
repeat_highlight_symbol(bool) –When
True, every row shows the highlight symbol (selected row still uses highlight style). -
focusable(bool) –Whether this Options participates in field focus.
-
passthrough(Sequence[str]) –Key bindings never captured while focused.
Methods:
-
select_value–Select the visible item whose stored value equals
value. -
resolve_submission–Reconcile typed composer text with the selection per
accept. -
move–Move
selectedbydelta, skipping disabled entries. -
select–Set
selectedtoindexwithin the filtered view. -
clear_query–Clear the filter query and reset selection to the first row.
-
handle_keyboard–Apply a keyboard event while this Options is focused.
-
compose–Compose interface-neutral Content for this Options.
-
after_render–Record the painted area so hover can map a pointer to a row.
-
handle_hover–Mark the row under the pointer as hovered (mouse hover).
-
component_post_init–Initialize subclass state after dataclass fields are assigned.
-
get_frame–Optional frame/panel chrome around composed content.
-
get_size–Return the preferred cell size of this component.
-
before_render–Called before rendering; returns the effective render area.
-
compose_extra_small–Compose content when the viewport is extra small (< 40 cols).
-
compose_small–Compose content when the viewport is small (40–79 cols).
-
compose_medium–Compose content when the viewport is medium (80–119 cols).
-
compose_large–Compose content when the viewport is large (120–159 cols).
-
compose_extra_large–Compose content when the viewport is extra large (>= 160 cols).
-
handle_paste–Optional paste handler while focused.
items
class-attribute
instance-attribute
¶
items: Sequence[OptionItem] = ()
Entries to pick from (plain strings, Text, or Option).
query
class-attribute
instance-attribute
¶
query: str = ''
Filter text. Edited by typing while focused when searchable;
assign it from a hook to filter reactively.
filter
class-attribute
instance-attribute
¶
filter: FilterMode = 'fuzzy'
How query narrows the visible items — see FilterMode.
"fuzzy"/True fuzzy-match, "prefix" prefix-match, "none"/
False show everything (for externally filtered items), or a
(query, item_text) -> bool callable.
accept
class-attribute
instance-attribute
¶
accept: AcceptPolicy = 'replace'
How resolve_submission reconciles typed text with the selection.
searchable
class-attribute
instance-attribute
¶
searchable: bool = False
Whether typing while focused edits query directly.
Off by default: a plain Options browses with the arrow keys and lets
every other key reach app hooks. Turn it on to make the list a fuzzy
search box that consumes printable keys while focused.
selected
class-attribute
instance-attribute
¶
selected: int = 0
Selection index within the filtered view.
direction
class-attribute
instance-attribute
¶
direction: OptionsDirection = 'top_to_bottom'
Visual order of option rows in the list.
foreground
class-attribute
instance-attribute
¶
foreground: ColorLike | None = None
Default foreground color for unselected rows (deprecated alias:
color).
background
class-attribute
instance-attribute
¶
background: ColorLike | None = None
Default background color for unselected rows.
highlight_color
class-attribute
instance-attribute
¶
highlight_color: ColorLike = 'black'
Foreground of the selected row.
highlight_background
class-attribute
instance-attribute
¶
highlight_background: ColorLike = 'white'
Background of the selected row.
highlight_symbol
class-attribute
instance-attribute
¶
highlight_symbol: str = '> '
Symbol prepended to the selected row.
hovered
class-attribute
instance-attribute
¶
hovered: int | None = None
Row under the pointer (filtered-view index), or None.
Set by :meth:handle_hover when mouse events are enabled; drawn as a
dim hover_symbol so it reads as distinct from the selection.
hover_symbol
class-attribute
instance-attribute
¶
hover_symbol: str = '· '
Symbol prepended to the hovered row (dim), distinct from selection.
match_color
class-attribute
instance-attribute
¶
match_color: ColorLike | None = 'cyan'
Emphasis color for characters matched by query.
repeat_highlight_symbol
class-attribute
instance-attribute
¶
repeat_highlight_symbol: bool = False
When True, every row shows the highlight symbol.
focusable
class-attribute
instance-attribute
¶
focusable: bool = True
Whether this Options participates in field focus (tab order).
passthrough
class-attribute
instance-attribute
¶
Key bindings this list never captures while focused.
owns_cursor
class-attribute
instance-attribute
¶
owns_cursor: bool = dataclasses.field(
default=True, init=False
)
Whether selection replaces the hardware caret.
filtered
property
¶
Indices into items currently visible, in display order.
Use this with visible_items to inspect the current filtered view.
visible_items
property
¶
Plain text of the currently visible items, in display order.
selected_item
property
¶
selected_item: OptionItem | None
The selected item object, or None when the view is empty.
selected_value
property
¶
selected_value: Any | None
Stable stored value of the selection (alias of value).
Use with select_value to preserve a selection across item
rebuilds by value/id rather than by fragile filtered index.
selected_label
property
¶
selected_label: str
Plain display text of the selected item, or "" when empty.
visible
class-attribute
instance-attribute
¶
visible: bool = dataclasses.field(
default=True, kw_only=True
)
Whether this component paints at all.
z
class-attribute
instance-attribute
¶
z: int = dataclasses.field(default=0, kw_only=True)
Stacking order among sibling content.
fit_content
class-attribute
instance-attribute
¶
fit_content: bool = dataclasses.field(
default=True, kw_only=True
)
When True, paint at natural size inside a larger slot.
select_value
¶
Select the visible item whose stored value equals value.
Returns True when a match is found. The stable way to keep a
selection pinned across item rebuilds: store the value, rebuild
items, then re-select by value.
Source code in xnano/components/options.py
resolve_submission
¶
Reconcile typed composer text with the selection per accept.
Removes the userland "did they Tab-complete this row or type past
it?" logic. See AcceptPolicy for the per-policy behavior.
Source code in xnano/components/options.py
move
¶
move(delta: int) -> None
Move selected by delta, skipping disabled entries.
Movement stays within the currently filtered choices.
Parameters:
-
delta(int) –Steps to move; negative moves toward the start.
Source code in xnano/components/options.py
select
¶
select(index: int) -> None
Set selected to index within the filtered view.
Disabled entries are rejected; the selection is left unchanged
when index points at a disabled item. Out-of-range indices
are clamped.
Parameters:
-
index(int) –Target index in the filtered view.
Source code in xnano/components/options.py
clear_query
¶
handle_keyboard
¶
handle_keyboard(keyboard: 'KeyboardEventData') -> bool
Apply a keyboard event while this Options is focused.
Up/down move the selection (skipping disabled); Home/End jump
to the first/last enabled entry. Printable characters and
backspace edit query when searchable. Enter, tab,
escape, and passthrough bindings are never consumed so hooks
and focus navigation see them.
Parameters:
-
keyboard('KeyboardEventData') –The keyboard event payload.
Returns:
-
bool–Truewhen the event was consumed.
Source code in xnano/components/options.py
compose
¶
compose(ctx: 'ComponentRenderContext') -> Any
Compose interface-neutral Content for this Options.
Parameters:
-
ctx('ComponentRenderContext') –Render-time scope for this paint.
Returns:
Source code in xnano/components/options.py
handle_hover
¶
Mark the row under the pointer as hovered (mouse hover).
Hover is a distinct indicator from the selection: the hovered row
is set here and drawn with a dim hover_symbol. Moving off the
rows clears it. Returns whether the hovered row changed.
Source code in xnano/components/options.py
component_post_init
¶
Initialize subclass state after dataclass fields are assigned.
Override this method instead of __post_init__.
get_size
¶
get_size(ctx: ComponentRenderContext[StateT]) -> Size
before_render
¶
before_render(
ctx: ComponentRenderContext[StateT], area: "Area"
) -> "Area"
compose_extra_small
¶
compose_extra_small(
ctx: ComponentRenderContext[StateT],
) -> Content | None
Compose content when the viewport is extra small (< 40 cols).
Optional responsive counterpart to :meth:compose. When
overridden, it is used instead of compose while the window
is in this size tier. Overriding any compose_* variant opts the
component into breakpoint dispatch; a component that overrides none
pays no per-frame cost.
Source code in xnano/components/component.py
compose_small
¶
compose_small(
ctx: ComponentRenderContext[StateT],
) -> Content | None
Compose content when the viewport is small (40–79 cols).
See :meth:compose_extra_small.
compose_medium
¶
compose_medium(
ctx: ComponentRenderContext[StateT],
) -> Content | None
Compose content when the viewport is medium (80–119 cols).
See :meth:compose_extra_small.
compose_large
¶
compose_large(
ctx: ComponentRenderContext[StateT],
) -> Content | None
Compose content when the viewport is large (120–159 cols).
See :meth:compose_extra_small.
compose_extra_large
¶
compose_extra_large(
ctx: ComponentRenderContext[StateT],
) -> Content | None
Compose content when the viewport is extra large (>= 160 cols).
See :meth:compose_extra_small.
Source code in xnano/components/component.py
get_fuzzy_match
¶
Score candidate against query as a fuzzy subsequence.
Case-insensitive. Consecutive matches and word-start matches score higher; shorter candidates win ties.
Parameters:
Returns:
-
tuple[int, tuple[int, ...]] | None–(score, matched_indices)when every query character appears -
tuple[int, tuple[int, ...]] | None–in order, otherwise
None. An empty query matches with score -
tuple[int, tuple[int, ...]] | None–zero.