Skip to content

Releases

3.7.0

Choose a tag to compare

@Kyostenas Kyostenas released this 14 Nov 00:55
bd82c8c

Added

  • ScrollView follows the focus (follow_focus=True, the default):
    when a component inside it takes the focus, its rows are scrolled into
    view; scrolling by hand afterwards is left alone. focus_rows(width)
    gives the rows of the focused component.
  • Menu and Sidebar keep to their height: with more items than fit,
    a window around the current item with ▲ n más / ▼ n más lines (click
    them to page); PgUp/PgDn move a page and the wheel scrolls. The sidebar
    title stays on top.
  • Responsive Form grid: min_column_width drops to fewer columns when
    they would be narrower; FormCell(field, span=, min_width=) and
    Form.set_layout() give a field several columns, more when it would be
    narrower than its minimum; grid_columns(width).
  • DataTable(fill=True) takes its whole slot with the status line at
    the bottom; visible_rows is how many rows the last frame showed.
  • GridView: a flexablelib.Grid as a component with an accessible
    name (title / a11y_label) and role, and audit() for itself and
    every component inside.
  • StatusLine: a one-line status bar with role status, a symbol per
    kind besides the colour, and screen-reader announcements on change.

Changed

  • VStack without weights gives weight 1 to children that fill their
    slot (expand: DataTable(fill=True), ScrollView, Sidebar, Tabs),
    so they share the rows left over; before, every child kept its own
    height.
  • Menu with max_visible (or a height) now shows the ▲ / ▼ lines.

v3.6.0

Choose a tag to compare

@Kyostenas Kyostenas released this 14 Nov 00:35
ca95e52

Added

  • SignatureInput: a signature pad drawn with the mouse (press and
    drag) or the keyboard (arrows move the pen, Space puts it down and lifts
    it, x erases, Backspace undoes, c clears). value is the list of
    strokes; to_text(), to_bitmap(scale) and to_png(scale) export it.
  • Stopwatch: a stopwatch input for forms -- start / stop (Space or
    Enter), laps (l), reset (r); value is the elapsed
    datetime.timedelta, laps the split times, on_lap(split).
  • VStack: children one below the other sharing the stack's height by
    weights, within min_heights / max_heights; when they do not fit it
    scrolls and follows the focus (the child holding the focused component
    comes into view; Tab reaches the ones out of view). Wheel and PgUp/PgDn
    scroll it, with an optional scroll bar.
  • CatalogGrid pictures: items take an image (path, URL, bytes or
    bitmap) and image_alt; image_height, image_method and
    image_headers control them. Enter or a click on the current card sets
    selected / value.
  • Image downloads with headers: Image(headers=),
    ImageViewer(headers=), CatalogGrid(image_headers=) and
    _images.load(url, headers=) send e.g. an Authorization bearer token.
    Authorization, Proxy-Authorization and Cookie are not sent on to
    redirects, and no header value appears in errors.
  • ValidationSummary from a list: messages, (field, message) pairs,
    dicts with field / message or exceptions, besides the
    {field: message} dict. With fields (a Form or inputs) or
    on_select it takes the focus and Enter moves the focus to the field.
    entries and messages list them.
  • EditableTable(name=) and its value (a copy of the rows), so a
    Form collects it.

Changed

  • Form.values() and Form.fields() reach nested fields: inputs inside
    containers (Stack, VStack, Tabs -- hidden tabs too --, Wizard
    steps, Accordion sections, grids, cards) and named EditableTables,
    not only the top-level ones. Validation and "Enter moves to the next
    field" reach them too.
  • ScanInput keeps the code it read (keep_value=True, the new
    default): value and a Form still have it after the read, the next
    read replaces it as soon as it starts typing, and Enter on the shown code
    does not read it twice. keep_value=False restores the old behaviour of
    clearing the field after every read. last_code is the latest code.
  • Focus without colour: a focused empty DataTable, an Image drawn as
    text (alt text, [alt], the warning) and the ImageViewer info line now
    show a ▸ marker (> without colour or in ASCII mode) and reverse
    video, not only a colour. A focused CatalogGrid card always has the
    ▸ marker besides its heavy border.
  • Image download errors give the HTTP status or the reason
    (no se pudo descargar (HTTP 401)) instead of the exception text.

v3.5.5

Choose a tag to compare

@Kyostenas Kyostenas released this 14 Nov 00:23
9339196

Fixed

  • Line breaks in Markdown, reStructuredText, AsciiDoc and LaTeX exports:
    every break compose() and the readers honour (\r\n, a lone \r,
    \u2028, \u2029, vertical tab, form feed, \x85, \x1c-\x1e) now
    becomes a space, as \n already did. A cell with a Windows line ending
    split its Markdown row in two, so from_markdown() read back extra rows
    and shifted every column after it; the RST grid came out ragged.
  • Fitting a float column to max_width no longer pads its shorter
    numbers with zeros: -0.5 printed as -0.50000000 when another number
    in the column set the decimal budget.
  • A number written with an exponent (6.01e+16) counts whole towards
    how narrow its column can go. Only its integer digits were counted, so
    the reduction was booked against a column that could not give it back
    and the other columns kept their decimals while the table stayed too
    wide.

3.5.4

Choose a tag to compare

@Kyostenas Kyostenas released this 13 Nov 16:26
39bf6af

Fixed

  • Kitty keyboard reports with out-of-range code points (CSI 97;1;99999999u,
    a shifted key above U+10FFFF) no longer raise ValueError from
    KeyParser.feed(): the invalid text or shifted code is ignored and the
    key is reported as usual.
  • Escape followed by a bracketed paste in the same read: the paste text
    no longer starts with \x1b[200~.
  • Input split across reads parses like the whole input: ESC ESC waits
    for the next byte, so ESC ESC [A read in pieces is still alt+up
    (a lone double Escape still gives two escape keys after the pause).
  • MoneyInput clicks put the cursor under the clicked character (the
    currency symbol was counted as text, so the cursor landed one place to
    the right); clicking an empty field and then pressing ctrl+left no
    longer raises IndexError.
  • TextInput (and every input built on it) keeps a cursor_pos set
    out of range from outside within the text instead of raising on word
    moves.
  • CoordinatesInput renders at widths 1 and 2 (latitude above
    longitude) instead of raising ValueError.
  • An empty Table with show_index=True renders like without the index
    instead of raising TypeError.

Changed

  • Key and MouseEvent keep all their fields through copy, deepcopy
    and pickle (a MouseEvent could not be copied at all).

3.5.3

Choose a tag to compare

@Kyostenas Kyostenas released this 13 Nov 00:18
29618bc

Fixed

  • Index labels with a row_filter (or sort_by) and hidden empty rows
    (show_empty_rows=False): the visible rows are numbered 0, 1, 2 as
    without a filter, instead of 0, 2, 4 (the hidden empty rows used to take
    a number). to_records, to_csv, paginate and stream agree.
  • stream() index column is as wide as in compose() when rows are
    hidden by row_filter or show_empty_rows=False: it was sized for every
    row, so the streamed table came out one column wider.
  • Contrast checks (parse_color, contrast_ratio, Theme.contrast())
    understand every colour the core draws: colour:N, hex without #
    (ff8800), and greyNN as the xterm grey really shown. A theme using
    colour:N reported no ratio, or 1:1 for black on colour:226.
  • Colour tokens with non-ASCII digits ('²', '٣') raise
    ColourError like any unknown colour, instead of a bare ValueError or
    being read as a palette index.

Changed

  • Hiding empty rows no longer re-scans every row once per row while
    numbering the index (quadratic in the row count).

PR #80.

3.5.2

Choose a tag to compare

@Kyostenas Kyostenas released this 12 Nov 22:45
a1a1a20

Fixed

  • Dialogs shown with app.push() (Modal, ConfirmDialog,
    PromptDialog, Popover) now close with Escape, their buttons and
    close(), as when shown with open(app). close() looks at the app's
    layers instead of a flag only open() set.
  • A Form inside a modal closes with Escape from any field, with or
    without on_cancel. When the form's on_cancel (or any code) calls
    app.pop(), the dialog's is_open goes back to False and on_close runs
    once.
  • Text with \r\n or \r line endings (for example the output of
    Table.to_csv()): every line ending counts as a new line, so no \r
    reaches the terminal and frames, grids and cards keep their borders.

3.5.1

Choose a tag to compare

@Kyostenas Kyostenas released this 12 Nov 20:46
302d583

Added

  • Linux aarch64 (arm64) wheels for flexablelib and
    flexablelib-speedups, glibc (manylinux) and musl (musllinux/Alpine),
    CPython 3.8–3.14, built and tested on native arm64 runners.
  • macOS Intel (x86_64) wheels for both projects, CPython 3.8–3.14.
  • Every wheel is checked on its own architecture by tools/wheel_check.py:
    the C extension loads and matches the pure Python code (text measurement,
    frame primitives, picture resize/quantize/sixel/blocks) on random input.

Changed

  • 49 binary wheels per extension project (was 28): manylinux and musllinux
    for x86_64 and aarch64, Windows x64, macOS arm64 and x86_64.

3.5.0 — Pictures in the terminal

Choose a tag to compare

@Kyostenas Kyostenas released this 12 Nov 19:59
e5468b6

Added

  • Pictures in the terminal (flexablelib-components, see
    docs/IMAGES.md): Image and ImageViewer, and FilePreview draws
    image files (and the first page of PDFs and SVGs when pdftoppm,
    PyMuPDF, rsvg-convert or cairosvg is available). Automatic fallback
    chain: kitty graphics protocol (Unicode placeholders on kitty 0.28+,
    Ghostty and through tmux passthrough), iTerm2 inline images,
    sixel, Unicode blocks (half blocks, quadrants, sextants, braille in
    truecolor / 256 / 16 colours or mono), ASCII, then an OSC 8 link that
    opens the picture in the browser (BROWSER, xdg-open, gio, open,
    start, wslview), and with no browser and no display a warning
    (⚠ / !) with the file name and the reason -- nothing is opened.
    fit (contain, cover, fill, none), proportions from the cell
    size in pixels, alt, caption, background, animate, focusable
    (Enter / o / click open it).
  • Graphics detection (flexablelib_tui.graphics, detect_graphics,
    GraphicsInfo): environment first (kitty, Ghostty, WezTerm, iTerm2, VS
    Code, Konsole, mintty, Warp, rio, foot, mlterm, contour, Windows
    Terminal, Terminal.app, tmux client features and passthrough, GNU
    screen), then one short query of the terminal (kitty APC query,
    CSI 16 t / CSI 14 t cell and window pixels, TIOCGWINSZ,
    XTSMGRAPHICS, XTVERSION, DA1), cached; App asks through its terminal
    and replays keys typed meanwhile. FLEXABLELIB_IMAGE=kitty|iterm|sixel| blocks|half|quadrant|sextant|braille|ascii|link|none forces a method,
    FLEXABLELIB_NO_BROWSER=1 says there is no browser.
  • Image decoding without dependencies (flexablelib._images): PNG
    (every colour type and depth, transparency, Adam7), GIF (frames,
    disposal, interlace), BMP (1–32 bit, bit fields), PPM/PGM/PBM; Pillow
    (flexablelib[images]) adds JPEG, WebP, TIFF and the rest. URLs with a
    timeout and a size cap.
  • C acceleration in flexablelib-speedups for resizing (area
    average), median-cut quantization, the sixel encoder, PNG unfiltering
    and the block/braille conversion, byte-identical to the Python fallback
    (29–87× faster; tools/bench_images.py). FLEXABLELIB_IMAGES=python
    forces Python.
  • Extras images (Pillow) -- included in all -- and svg (cairosvg).

Changed

  • Frames carry pictures as an invisible zero-width mark that becomes the
    picture sequence when the frame is written (str(), print, Live,
    App): widths, layouts and the frame diff are unchanged. Live redraws a
    picture when a line it covers changes and deletes kitty pictures that
    left the screen.
  • Card and Grid without colour keep the invisible component and picture
    marks (clicks and pictures inside layouts work with NO_COLOR).
  • App reaches components inside a Card body (focus, keys, clicks).
  • The accessibility audit reports no alt text for images.

flexablelib 3.4.0

Choose a tag to compare

@Kyostenas Kyostenas released this 12 Nov 18:22
2c4aad8

Added

  • Keyboard input, complete (flexablelib-tui, see docs/INPUT.md):
    one key name whatever the terminal sends -- xterm/VT100/VT220 (CSI, SS3,
    8-bit CSI), rxvt/urxvt, the Linux console, Alt as an ESC prefix, F1–F24
    (F35 with kitty) with any modifier, Ctrl/Alt/Shift/Meta/Super/Hyper in a
    fixed order (ctrl+alt+shift+f12), the kitty keyboard protocol
    (Ctrl+I vs Tab, Ctrl+Enter, keypad, press/repeat/release), xterm
    modifyOtherKeys, bracketed paste (even split across reads), terminal
    focus in/out and terminal answers (DA, DECRQM, XTVERSION, kitty flags).
    Key carries char, event, mouse, data and raw;
    normalize_key, key_label.
  • Windows Console API input (wincon): ReadConsoleInputW records
    with virtual-key codes and modifiers (AltGr, F13–F24, surrogate pairs,
    mouse, wheel, resize, focus), virtual-terminal input and the msvcrt
    fallback (Terminal(windows='records' | 'vt' | 'msvcrt')). CI injects
    records into a real console on windows-latest.
  • Capability detection (detect_capabilities, capabilities.probe):
    colour level, Unicode, size and terminal from the environment, then a
    probe of the terminal itself; Terminal(keyboard='auto') turns on the
    kitty protocol or modifyOtherKeys when available and restores everything
    on exit.
  • Key map (KeyMap): remap keys, name actions (focus_next, quit,
    help, read_screen ...), describe, conflicts, save/load JSON;
    App(keymap=...).
  • Mouse in every component: App(mouse=True) (or 'hover') knows
    which component drew each cell and focuses and passes clicks to it --
    buttons, checkboxes, switches, radios, tabs, menus, sidebar, pagination,
    sort controls, calendars and date pickers, selects, autocomplete, file
    entries, trees, accordions, carousels, links, alerts, DataTable rows,
    checkbox column and headers, catalog cards. The wheel scrolls what is
    under the pointer; drags move Split dividers and scroll bars, with
    capture until release. Component.on_mouse(event) in local coordinates;
    _Themed.hot() registers clickable spots.
  • Accessibility (flexablelib_tui.accessibility, see
    docs/ACCESSIBILITY.md): settings from the environment
    (FLEXABLELIB_SCREEN_READER, _REDUCED_MOTION, _ASCII,
    _FOCUS_MARKER, _THEME, _LANG, _A11Y) or App(...) arguments;
    screen-reader mode (linear text with role, label, value, state and
    description; announcements of focus, changes, dialogs and notifications
    to stdout, stderr, a file or a callback; Ctrl+R reads the screen);
    roles, accessible names, values and states for every component;
    focus visible without colour (text markers); themes high-contrast,
    high-contrast-light, deuteranopia, protanopia, tritanopia,
    monochrome with WCAG contrast (Theme.contrast(), contrast_ratio)
    and colour-blindness simulation (simulate_cvd, delta_e);
    App(theme=...); static spinners and skeletons with reduced motion;
    status with symbol and word (status_symbol), chart markers per series;
    ASCII fallback for frames; App(min_size=...); F1 help screen built from
    the bindings; tab_index; audit() checks role, label and visible focus.
  • Folios (see docs/FOLIOS.md): Table.id_columns ('auto', a list,
    or None as before) and Table.id_names print identifiers -- folios,
    IDs, codes, SKUs, postal codes, years -- without thousands separators
    while quantities and money keep them; flexablelib.is_id_column and
    ID_COLUMN_NAMES; to_excel follows. DataTable column type
    'id' / 'number' / 'money', id_columns, id_names.
  • C: ascii_fold (8× faster) and extract_marks (click zones, 2× faster)
    in flexablelib-speedups, with Python fallbacks and parity tests.
  • examples/keys.py: live key and mouse detector. examples/admin.py
    takes --theme, --screen-reader, --ascii, --reduced-motion,
    --focus-marker and click:X:Y keys.

Changed

  • DataTable no longer puts thousands separators in identifier columns
    by name (id_columns=False restores 3.3).
  • The default selection style is bold as well as coloured; without colour,
    focus and selection use text markers ([>Guardar<], > before the
    current item).
  • On TERM=dumb the app prints linear text and switches no terminal
    modes (no paste, focus or mouse escape sequences).
  • FORCE_COLOR=0 turns colour off. Colour specs accept attributes after
    the background (black on cyan underline).

flexablelib 3.3.1

Choose a tag to compare

@Kyostenas Kyostenas released this 12 Nov 16:50
174fe63

Fixed

  • Tabs no longer push tables and cards past their width. A tab measures
    nothing, so a cell holding one was sized and wrapped as narrower than the
    spaces it became: the table came out wider than max_width (or
    render(width)), and in card mode the raw tab displaced the frame.
    Cells are expanded before they are measured, headers are trimmed on their
    expanded width with overflow='truncate', and wrap_to_width expands
    tabs itself, so cards, grids, widgets and components get it too.
  • A newline in a card title, or in a title set into a border
    (title_position='border'), split the frame in two; the title now
    goes on one line. Multi-line table titles and captions are aligned line
    by line instead of only the first line.
  • max_width now counts the panel (two columns) and shadow
    (one): squeezing, expand_to_window and too_narrow_message sized
    the columns for the full width and then drew the panel outside it.
  • from_csv and from_markdown drop a leading UTF-8 byte-order mark
    (Excel's "CSV UTF-8"): it was kept as the first character of the first
    header, and a Markdown table gained an extra empty column.
  • A brace format template (custom_format={'price': '{:,.2f}'},
    float_format='$ {:,.2f}') no longer aborts the whole render on a text
    cell in the column; the cell prints as it is, as it already did with a
    plain spec. A template that needs more than one value raises a
    ValueError that says so.

Added

  • flexablelib.text_width.expand_tabs(text, tabsize=8): tab expansion
    that counts terminal columns (wide characters two, escape sequences
    none).

pip install -U flexablelib==3.3.1 (extras [tui,components,speed] in lockstep).