xnano.grids
xnano.grids
¶
xnano.grids
Build declarative layouts from named Field values. Grids can nest,
share state, react to hooks, and use the same layout on terminal and web.
Classes:
-
GridSettings–Rendering, layout, and frame settings for a
BaseGridsubclass. -
BaseGrid–Declarative layout container for a terminal-based UI.
GridSettings
¶
Bases: TypedDict
Rendering, layout, and frame settings for a BaseGrid subclass.
Attributes:
-
foreground(NotRequired[ColorLike]) –Foreground color of rendered content.
-
background(NotRequired[ColorLike]) –Background color of the grid frame.
-
direction(NotRequired[Direction]) –Direction used to lay out fields.
-
gap(NotRequired[int]) –Cells between fields.
-
border(NotRequired[Border]) –Border style around the grid.
-
border_sides(NotRequired[list[Side]]) –Border sides to display.
-
border_color(NotRequired[ColorLike]) –Color of the border.
-
title(NotRequired[str]) –Text displayed in the grid frame.
-
title_position(NotRequired[FrameTitlePosition]) –Alignment of the frame title.
-
padding(NotRequired[PaddingLike]) –Space between the frame and its content.
-
bold(NotRequired[bool]) –Whether content is bold.
-
dim(NotRequired[bool]) –Whether content is dimmed.
-
italic(NotRequired[bool]) –Whether content is italic.
-
underline(NotRequired[bool]) –Whether content is underlined.
-
slow_blink(NotRequired[bool]) –Whether content blinks slowly.
-
rapid_blink(NotRequired[bool]) –Whether content blinks rapidly.
-
reversed(NotRequired[bool]) –Whether foreground and background are reversed.
-
strict(NotRequired[bool]) –Whether field assignments are validated.
foreground
instance-attribute
¶
foreground: NotRequired[ColorLike]
The foreground color of the grid's content.
background
instance-attribute
¶
background: NotRequired[ColorLike]
The background color of the grid's frame area.
direction
instance-attribute
¶
direction: NotRequired[Direction]
The direction in which content within this grid should be laid out.
border
instance-attribute
¶
border: NotRequired[Border]
The border style to be applied onto the outer frame of the grid.
border_sides
instance-attribute
¶
border_sides: NotRequired[list[Side]]
The sides of the border to be applied onto the outer frame of the grid.
title
instance-attribute
¶
title: NotRequired[str]
The title to be displayed around the outer frame of the grid.
title_position
instance-attribute
¶
title_position: NotRequired[FrameTitlePosition]
The position of the title within the outer frame of the grid.
padding
instance-attribute
¶
padding: NotRequired[PaddingLike]
The padding to be applied around the content area of the grid.
italic
instance-attribute
¶
italic: NotRequired[bool]
Whether the grid should be rendered in italic.
underline
instance-attribute
¶
underline: NotRequired[bool]
Whether the grid should be rendered in underline.
slow_blink
instance-attribute
¶
slow_blink: NotRequired[bool]
Whether the grid should be rendered in slow blink.
rapid_blink
instance-attribute
¶
rapid_blink: NotRequired[bool]
Whether the grid should be rendered in rapid blink.
reversed
instance-attribute
¶
reversed: NotRequired[bool]
Whether the grid should be rendered in reversed color.
strict
instance-attribute
¶
strict: NotRequired[bool]
When True (the default), all field values are validated against their
type annotations during grid construction.
BaseGrid
¶
Bases: AbstractInterface
Declarative layout container for a terminal-based UI.
BaseGrid-scoped settings may be declared on the class header
(class Dashboard(BaseGrid, direction="horizontal", gap=1): ...),
in a class-level grid_settings dict, or both — values in
grid_settings override matching header kwargs.
Attributes:
-
grid_settings(GridSettings) –Class-level layout and frame configuration.
-
visible(bool) –Whether the grid is rendered.
-
z(int) –Layering order for overlapping grids.
-
columns(int) –Columns available during the current frame.
-
rows(int) –Rows available during the current frame.
Examples:
Layout fields render content; state=True fields hold app data.
Nested BaseGrid subclasses compose larger layouts:
from xnano import BaseGrid, Field, Terminal
class Sidebar(BaseGrid, direction="vertical"):
nav: str = Field(default="Home", border="rounded", height="1fr")
class App(BaseGrid, direction="horizontal", gap=1):
sidebar: Sidebar = Field(default_factory=Sidebar, width="25%")
content: str = Field(default="Main area", width="1fr")
selected: int = Field(default=0, state=True)
Terminal().run(App())
Event hooks register handlers on the grid class. Use @on_click to
scope mouse handlers to a layout field's region:
from xnano import BaseGrid, Context, Field, Terminal, hooks
class Counter(BaseGrid, direction="vertical", gap=1):
label: str = Field(default="Count: 0", height=1)
body: str = Field(default="Click me", height="1fr")
count: int = Field(default=0, state=True)
@hooks.on_keyboard("up")
def increment(self) -> None:
self.count += 1
self.label = f"Count: {self.count}"
@hooks.on_keyboard("down")
def decrement(self) -> None:
self.count -= 1
self.label = f"Count: {self.count}"
@hooks.on_click("body")
def on_body(self, ctx: Context) -> None:
self.body = "Clicked!"
@hooks.on_tick(1000)
def reset_body(self) -> None:
self.body = "Click me"
Terminal().run(Counter())
Methods:
-
__post_init__–Called at the end of the generated
__init__. Override to run post-construction logic. -
grid_post_init–Called exactly once, when this grid is first attached to a live
-
grid_render–Called each frame before layout.
-
grid_render_extra_small–Refine layout when the viewport is extra small (< 40 cols).
-
grid_render_small–Refine layout when the viewport is small (40–79 cols).
-
grid_render_medium–Refine layout when the viewport is medium (80–119 cols).
-
grid_render_large–Refine layout when the viewport is large (120–159 cols).
-
grid_render_extra_large–Refine layout when the viewport is extra large (>= 160 cols).
-
grid_play_effect–Run a visual effect on one or more layout field areas.
-
grid_effect–Run a visual effect on one or more layout field areas.
-
grid_field_position–Return the parent-relative slide offset for a layout field.
-
grid_set_field–Set a layout field's runtime value and/or per-instance field metadata.
-
grid_update_field–Update a layout field's style/layout attributes, live, in-place.
-
grid_set_frame–Set this grid instance's outer frame (chrome + background fill).
-
grid_set_background–Fill this grid instance's whole area with
background. -
grid_schedule_update–Apply an update on the UI thread before the next frame.
-
set_frame–Deprecated alias for
grid_set_frame. -
set_background–Deprecated alias for
grid_set_background. -
schedule_update–Deprecated alias for
grid_schedule_update. -
grid_get_field_state–Return tracked state for a field.
-
grid_mark_field_dirty–Mark a field as changed.
-
get_field_state–Deprecated alias for
grid_get_field_state. -
mark_field_dirty–Deprecated alias for
grid_mark_field_dirty.
Source code in xnano/grids.py
grid_settings
class-attribute
¶
grid_settings: GridSettings = {}
Class-level grid configuration, like Pydantic's model_config.
__xnano_grid__
class-attribute
¶
__xnano_grid__: bool = True
Marks this class as a grid, for cheap identity checks.
Faster and clearer than duck-typing _grid_fields, and usable from
layers that must not import BaseGrid — see is_grid.
visible
class-attribute
instance-attribute
¶
visible: bool = True
Whether this grid is rendered in the live session.
columns
class-attribute
instance-attribute
¶
columns: int = 0
Terminal columns available to this grid — set by the session each frame.
rows
class-attribute
instance-attribute
¶
rows: int = 0
Terminal rows available to this grid — set by the session each frame.
grid_focused
property
¶
grid_focused: bool
Whether any of this grid's fields currently holds field focus.
Live alongside per-component focused: derived from the same
per-frame focus flags, so self.grid_focused in a hook and
@on_field("focused") both read the current state.
__post_init__
¶
grid_post_init
¶
Called exactly once, when this grid is first attached to a live terminal — after its own hooks are bound, before its first paint.
Unlike __post_init__ (construction time, no terminal attached
yet), ctx/ctx.state are live here. Override with either
signature — the extra parameter is optional, matching every other
@on_* hook, dispatched by arity at runtime (see _call_hook):
def grid_post_init(self) -> None: ...
def grid_post_init(self, ctx: Context[MyState]) -> None: ...
A static type checker sees the one-parameter form as an invalid
override (this base declares zero); that's a known false positive
for this flexible-arity hook pattern — add
# ty: ignore[invalid-method-override] on the override line.
Source code in xnano/grids.py
grid_render
¶
Called each frame before layout.
Override to refresh field values every frame. Initial values can be set
with Field(default=...), default_factory, or __post_init__.
Override with either signature — the extra Context parameter is
optional, dispatched by arity like grid_post_init:
def grid_render(self) -> None: ...
def grid_render(self, ctx: Context[MyState]) -> None: ...
A static type checker sees the one-parameter form as an invalid
override; add # ty: ignore[invalid-method-override] on the
override line.
Source code in xnano/grids.py
grid_render_extra_small
¶
Refine layout when the viewport is extra small (< 40 cols).
Optional responsive counterpart to :meth:grid_render. Runs when
the window enters this size tier — on the first render and on
later resizes that cross into it — not every frame, so a
per-frame @on_tick mutation is not overwritten by a size hook
resetting the same field. Keep shared per-frame logic in
grid_render and size-specific setup here. Overriding any
grid_render_* variant opts the class into breakpoint dispatch;
a class that overrides none pays no per-frame cost.
Source code in xnano/grids.py
grid_render_small
¶
Refine layout when the viewport is small (40–79 cols).
See :meth:grid_render_extra_small.
grid_render_medium
¶
Refine layout when the viewport is medium (80–119 cols).
See :meth:grid_render_extra_small.
grid_render_large
¶
Refine layout when the viewport is large (120–159 cols).
See :meth:grid_render_extra_small.
grid_render_extra_large
¶
Refine layout when the viewport is extra large (>= 160 cols).
See :meth:grid_render_extra_small.
grid_play_effect
¶
grid_play_effect(
effect: AbstractEffect,
*,
fields: list[str] | None = None
) -> bool
grid_play_effect(
effect: KnownEffectKind,
*,
duration_ms: int = 300,
color: ColorLike | None = None,
background: ColorLike | None = None,
direction: EffectMotion | None = None,
gradient_length: int | None = None,
randomness: int | None = None,
interpolation: EffectInterpolation | None = None,
effects: Sequence[AbstractEffect] | None = None,
child: AbstractEffect | None = None,
times: int | None = None,
fields: list[str] | None = None,
key: str | None = None
) -> bool
grid_play_effect(
effect: KnownEffectKind | AbstractEffect,
*,
duration_ms: int = 300,
color: ColorLike | None = None,
background: ColorLike | None = None,
direction: EffectMotion | None = None,
gradient_length: int | None = None,
randomness: int | None = None,
interpolation: EffectInterpolation | None = None,
effects: Sequence[AbstractEffect] | None = None,
child: AbstractEffect | None = None,
times: int | None = None,
fields: list[str] | None = None,
key: str | None = None
) -> bool
Run a visual effect on one or more layout field areas.
Each layout field is tagged with its name as an effect key during
rendering, so effects can target field content rects on the frame
after grid_render.
Pass a custom AbstractEffect subclass or
provide a known effect kind with typed keyword arguments.
Parameters:
-
effect(KnownEffectKind | AbstractEffect) –A built effect instance or a known effect kind string.
-
duration_ms(int, default:300) –Duration of the effect in milliseconds.
-
color(ColorLike | None, default:None) –Foreground or accent color for color-driven effects.
-
background(ColorLike | None, default:None) –Background color for two-color effects.
-
direction(EffectMotion | None, default:None) –Motion direction for slide and sweep effects.
-
gradient_length(int | None, default:None) –Gradient length for slide and sweep effects.
-
randomness(int | None, default:None) –Randomness for slide and sweep effects.
-
interpolation(EffectInterpolation | None, default:None) –Interpolation curve for the effect.
-
effects(Sequence[AbstractEffect] | None, default:None) –Child effects for sequence and parallel composition.
-
child(AbstractEffect | None, default:None) –Child effect for repeat and delay composition.
-
times(int | None, default:None) –Repeat count for repeat effects.
-
fields(list[str] | None, default:None) –Layout field names to target. When omitted or empty, no effect is started.
-
key(str | None, default:None) –Identity used to de-duplicate this effect per target field — see
AbstractEffect.key. Only meaningful wheneffectis a known-kind string; ignored wheneffectis already a builtAbstractEffectinstance (setkeyon the instance itself in that case).
Returns:
Source code in xnano/grids.py
1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 | |
grid_effect
¶
grid_effect(
effect: KnownEffectKind | AbstractEffect,
*,
duration_ms: int | None = None,
color: ColorLike | None = None,
background: ColorLike | None = None,
direction: EffectMotion | None = None,
gradient_length: int | None = None,
randomness: int | None = None,
interpolation: EffectInterpolation | None = None,
effects: Sequence[AbstractEffect] | None = None,
child: AbstractEffect | None = None,
times: int | None = None,
fields: list[str] | None = None,
key: str | None = None
) -> "EffectHandle"
Run a visual effect on one or more layout field areas.
Returns an EffectHandle: truthy when at least one field was
targeted, with .active and .cancel(), and usable as a
context manager that cancels the effect on exit.
Starting an effect never blocks. duration_ms is the animation
length handed to the renderer, not a sleep — the effect advances
one frame at a time, so hooks keep running while it plays. Omit it
inside a with block and the effect repeats until the block
exits.
Parameters:
-
effect(KnownEffectKind | AbstractEffect) –A built effect instance or a known effect kind string.
-
duration_ms(int | None, default:None) –Animation length in milliseconds. Defaults to 300; omitted inside a
withblock the effect repeats until exit. -
color(ColorLike | None, default:None) –Foreground or accent color for color-driven effects.
-
background(ColorLike | None, default:None) –Background color for two-color effects.
-
direction(EffectMotion | None, default:None) –Motion direction for slide and sweep effects.
-
gradient_length(int | None, default:None) –Gradient length for slide and sweep effects.
-
randomness(int | None, default:None) –Randomness for slide and sweep effects.
-
interpolation(EffectInterpolation | None, default:None) –Interpolation curve for the effect.
-
effects(Sequence[AbstractEffect] | None, default:None) –Child effects for sequence and parallel composition.
-
child(AbstractEffect | None, default:None) –Child effect for repeat and delay composition.
-
times(int | None, default:None) –Repeat count for repeat effects.
-
fields(list[str] | None, default:None) –Layout field names to target. When omitted or empty, no effect is started.
-
key(str | None, default:None) –Identity used to de-duplicate this effect per target field — see
AbstractEffect.key. Calling with the samekeyevery tick replaces the running effect rather than stacking new ones.
Returns:
-
'EffectHandle'–A handle for the started effect.
Examples:
self.grid_effect("fade", fields=["body"])
with self.grid_effect("pulse", fields=["body"]):
do_slow_work()
Source code in xnano/grids.py
1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 | |
grid_field_position
¶
grid_set_field
¶
grid_set_field(
name: str,
value: Any = UNSET,
*,
position: tuple[int, int] | None = None,
strict: bool = UNSET,
slide: Sequence[Axis] | None = UNSET,
visible: bool | None = UNSET,
wireframe: bool | None = UNSET,
foreground: ColorLike | None = UNSET,
background: ColorLike | None = UNSET,
fill: bool | None = UNSET,
width: SizingLike | None = UNSET,
height: SizingLike | None = UNSET,
gap: int | None = UNSET,
direction: Direction | None = UNSET,
horizontal_align: Alignment | None = UNSET,
vertical_align: VerticalAlignment | None = UNSET,
border: Border | None = UNSET,
border_sides: Sequence[Side] | None = UNSET,
border_color: ColorLike | None = UNSET,
title: str | None = UNSET,
title_position: FrameTitlePosition | None = UNSET,
padding: PaddingLike | None = UNSET,
margin: PaddingLike | None = UNSET,
z: int | None = UNSET,
modifiers: Sequence[CharacterModifier] | None = UNSET,
class_name: ClassNameLike | None = UNSET,
bold: bool = UNSET,
dim: bool = UNSET,
italic: bool = UNSET,
underline: bool = UNSET,
slow_blink: bool = UNSET,
rapid_blink: bool = UNSET,
reversed: bool = UNSET,
color: ColorLike | None = UNSET,
align: Alignment | None = UNSET
) -> None
Set a layout field's runtime value and/or per-instance field metadata.
Cannot be used on state fields. default, default_factory,
init, and state cannot be changed at runtime.
For frequent, value-free style ticks (e.g. from on_tick or an
effect callback), prefer grid_update_field — it skips the
value/position handling this method carries for the general case.
color is a deprecated alias for foreground.
Source code in xnano/grids.py
1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 | |
grid_update_field
¶
grid_update_field(
name: str,
*,
slide: Sequence[Axis] | None = UNSET,
visible: bool | None = UNSET,
wireframe: bool | None = UNSET,
foreground: ColorLike | None = UNSET,
background: ColorLike | None = UNSET,
fill: bool | None = UNSET,
width: SizingLike | None = UNSET,
height: SizingLike | None = UNSET,
gap: int | None = UNSET,
direction: Direction | None = UNSET,
horizontal_align: Alignment | None = UNSET,
vertical_align: VerticalAlignment | None = UNSET,
border: Border | None = UNSET,
border_sides: Sequence[Side] | None = UNSET,
border_color: ColorLike | None = UNSET,
title: str | None = UNSET,
title_position: FrameTitlePosition | None = UNSET,
padding: PaddingLike | None = UNSET,
margin: PaddingLike | None = UNSET,
z: int | None = UNSET,
modifiers: Sequence[CharacterModifier] | None = UNSET,
class_name: ClassNameLike | None = UNSET,
bold: bool = UNSET,
dim: bool = UNSET,
italic: bool = UNSET,
underline: bool = UNSET,
slow_blink: bool = UNSET,
rapid_blink: bool = UNSET,
reversed: bool = UNSET,
color: ColorLike | None = UNSET,
align: Alignment | None = UNSET
) -> None
Update a layout field's style/layout attributes, live, in-place.
A narrower sibling of grid_set_field for frequent, value-free
attribute changes — pulsing a border color from on_tick,
toggling a modifier from an effect callback. It has no value=
or position= parameters at all, so a per-frame style tick never
pays for that method's value-validation branch. Optional style
patches never raise: a missing or state-only name is a no-op
(no try/except needed), though unknown keyword arguments
still raise as a programming error.
color is a deprecated alias for foreground.
Source code in xnano/grids.py
1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 | |
grid_set_frame
¶
grid_set_frame(
frame: Frame | None = UNSET,
*,
background: ColorLike | None = UNSET,
border: Border | None = UNSET,
border_color: ColorLike | None = UNSET,
border_sides: Sequence[Side] | None = UNSET,
title: str | None = UNSET,
title_position: FrameTitlePosition | None = UNSET,
padding: PaddingLike | None = UNSET
) -> None
Set this grid instance's outer frame (chrome + background fill).
The public replacement for mutating the private _grid_frame.
Pass a whole frame to replace it outright, or individual keyword
arguments to patch the current frame in place — a bare
background fills the grid's whole area with no border required,
so nested grids can be themed without any chrome. Pass frame=None
to clear the frame.
Source code in xnano/grids.py
grid_set_background
¶
grid_set_background(background: ColorLike | None) -> None
Fill this grid instance's whole area with background.
Shorthand for grid_set_frame(background=...) — the path for
theming a nested grid, replacing private _grid_frame mutation.
Source code in xnano/grids.py
grid_schedule_update
¶
grid_schedule_update(
callback: Callable[[], Any] | None = None,
*,
field: str | None = None
) -> None
Apply an update on the UI thread before the next frame.
The thread-safe way to reflect background work in the UI: pass a
callback to run on the runtime thread (so it can mutate this grid
without racing the renderer), and/or a field to mark dirty. Safe
to call from any thread; a no-op when no runtime is active.
Source code in xnano/grids.py
set_frame
¶
set_background
¶
set_background(background: ColorLike | None) -> None
Deprecated alias for grid_set_background.
schedule_update
¶
Deprecated alias for grid_schedule_update.
Source code in xnano/grids.py
grid_get_field_state
¶
grid_get_field_state(name: str) -> FieldState | None
Return tracked state for a field.
Parameters:
-
name(str) –Field attribute name.
Returns:
-
FieldState | None–Its state, or
Nonefor an unknown field.
Source code in xnano/core/interface.py
grid_mark_field_dirty
¶
grid_mark_field_dirty(name: str) -> None
Mark a field as changed.
Parameters:
-
name(str) –Field attribute name.
Source code in xnano/core/interface.py
get_field_state
¶
get_field_state(name: str) -> FieldState | None
Deprecated alias for grid_get_field_state.