Explorer TUI¶
The interactive terminal user interface for discovering, browsing, and
querying Layers data. The lairs.tui package is a Textual application
with three surfaces: an Explore screen over the discovery index, a
type-aware Browse screen over a local repository, and a Query workbench
over materialized Parquet views. The pure-Python query engine is usable
on its own, without the terminal stack. For usage see
Guides > The explorer TUI.
Application¶
run_tui launches the full application, and QueryEngine is the
DuckDB-backed query engine that the Query screen drives. The query
result models (QueryResult, QueryRow) and errors (QueryError,
CqlError) round out the public surface of the package.
lairs.tui ¶
Interactive terminal UI for exploring and querying Layers data.
The TUI is a colorful Textual application with three surfaces: an Explore screen
that browses and filters the discovery index, a Browse screen that explores
every record in a local repository through type-aware views, and a Query screen
that runs powerful searches over materialized data through three query modes
(DuckDB SQL, a KWIC concordance, and a CQL token-pattern language). The
pure-Python query engine in :mod:lairs.tui.query is usable on its own;
:func:run_tui launches the full application.
CqlError ¶
Bases: QueryError
Raised when a CQL token-pattern fails to parse against the schema.
QueryEngine ¶
A DuckDB-backed query engine over materialized corpus views.
| PARAMETER | DESCRIPTION |
|---|---|
connection
|
The DuckDB connection holding the registered views.
TYPE:
|
tables
|
The registered view names, in display order.
TYPE:
|
open
classmethod
¶
open(data_dir: Path) -> QueryEngine
Open a directory of Parquet views as a queryable engine.
Every *.parquet file in data_dir is registered as a DuckDB view
named after its file stem, so expressions.parquet becomes the
expressions view.
| PARAMETER | DESCRIPTION |
|---|---|
data_dir
|
A directory of materialized Parquet views.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
QueryEngine
|
An engine over the views found in the directory. |
| RAISES | DESCRIPTION |
|---|---|
QueryError
|
When the directory does not exist or holds no Parquet views. |
columns ¶
Return the column names of a registered view.
| PARAMETER | DESCRIPTION |
|---|---|
table
|
A registered view name.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
tuple of str
|
The view's column names, or an empty tuple when it is unknown. |
schema ¶
Return every view with its columns, for the schema browser.
| RETURNS | DESCRIPTION |
|---|---|
tuple of (str, tuple of str)
|
Pairs of view name and its column names, in display order. |
run_sql ¶
run_sql(
sql: str, *, limit: int = _DEFAULT_ROW_LIMIT
) -> QueryResult
Execute read-only DuckDB SQL and return a bounded result.
| PARAMETER | DESCRIPTION |
|---|---|
sql
|
The SQL statement to run.
TYPE:
|
limit
|
The maximum number of rows to return; one extra row is fetched to detect truncation.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
QueryResult
|
The columns, rows, count, and truncation flag. |
| RAISES | DESCRIPTION |
|---|---|
QueryError
|
When the statement is empty or DuckDB rejects it. |
concordance ¶
concordance(
pattern: str,
*,
table: str = "expressions",
column: str = "text",
window: int = _DEFAULT_KWIC_WINDOW,
ignore_case: bool = True,
limit: int = _DEFAULT_ROW_LIMIT,
) -> QueryResult
Run a keyword-in-context (KWIC) search over a text column.
Each match of the regular expression pattern yields a row of the
source identifier, the left context window, the matched text, and the
right context window, so hits read as a classic concordance.
| PARAMETER | DESCRIPTION |
|---|---|
pattern
|
A Python regular expression to search for.
TYPE:
|
table
|
The view holding the text column.
TYPE:
|
column
|
The text column to search.
TYPE:
|
window
|
The number of context characters to show on each side of a match.
TYPE:
|
ignore_case
|
Whether to match case-insensitively.
TYPE:
|
limit
|
The maximum number of concordance lines to return.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
QueryResult
|
Columns |
| RAISES | DESCRIPTION |
|---|---|
QueryError
|
When the table or column is unknown, or the pattern is invalid. |
cql ¶
cql(
query: str,
*,
table: str = "annotations",
limit: int = _DEFAULT_ROW_LIMIT,
) -> QueryResult
Compile and run a CQL token-pattern over the annotations table.
A CQL query is a sequence of bracketed token constraints, for example
[label="DET"] [label="NOUN"] or [subkind="pos" & label="VERB"].
Each constraint matches an annotation attribute (= exact, !=
negated, ~ regular expression); an empty [] matches any token.
Adjacent blocks are joined on consecutive token_index within the same
layer, so the pattern matches token sequences. Each block matches exactly
one token; repetition quantifiers (*, +, ?, {...}) are not
supported and a pattern that uses one raises :class:CqlError.
| PARAMETER | DESCRIPTION |
|---|---|
query
|
The CQL token-pattern.
TYPE:
|
table
|
The token-aligned annotations view to query.
TYPE:
|
limit
|
The maximum number of matches to return.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
QueryResult
|
Columns |
| RAISES | DESCRIPTION |
|---|---|
CqlError
|
When the pattern is empty, malformed, or references unknown columns. |
QueryError
|
When DuckDB rejects the compiled statement. |
QueryError ¶
Bases: Exception
Raised when a query fails to execute or compile.
QueryResult ¶
Bases: Model
A tabular query result with its columns, rows, and run metadata.
| ATTRIBUTE | DESCRIPTION |
|---|---|
columns |
The result column names.
TYPE:
|
rows |
The result rows, each a tuple of stringified cells.
TYPE:
|
row_count |
The number of rows returned (after any display truncation).
TYPE:
|
truncated |
Whether more rows matched than were returned.
TYPE:
|
elapsed_ms |
Wall-clock execution time in milliseconds.
TYPE:
|
QueryRow ¶
Bases: Model
One result row as a tuple of stringified cells.
| ATTRIBUTE | DESCRIPTION |
|---|---|
cells |
The row's cell values, rendered to strings for display.
TYPE:
|
run_tui ¶
run_tui(
*,
index_path: str | None = None,
data_path: str | None = None,
repo_path: str | None = None,
) -> None
Launch the Layers explorer TUI.
| PARAMETER | DESCRIPTION |
|---|---|
index_path
|
Filesystem path to a discovery index directory to open on the Explore screen. When omitted the Explore screen starts empty.
TYPE:
|
data_path
|
Filesystem path to a directory of materialized Parquet views to open on
the Query screen. When omitted, a repository given by
TYPE:
|
repo_path
|
Filesystem path to a local Repository to open on the Browse screen. When
given without
TYPE:
|
Visualizations¶
Text-mode renderers that turn Layers records into terminal-friendly views: interlinear token tags, CoNLL-U grids, dependency trees, brat-style span overlays, judgment distributions, tier timelines, alignment bitexts, and the anchor and syntax helpers they share.
lairs.tui.viz ¶
Model-driven visualizers for Layers annotations, judgments, and structure.
Everything here dispatches on the lexicon's own type system, not on any dataset:
annotation kind/subkind/anchor, judgment taskType, graph nodeType/
edgeType. The four reference corpora only exercise some paths; a conforming
record of any provenance renders through the same dispatch, with custom and
URI-valued slots degrading to a faithful generic view.
Renderings are monospace text (wrapped in fenced code blocks by the caller's view layer) following established text-mode conventions: CoNLL-U token grids, indented and arc dependency trees, bracketed constituency trees, brat-style span lanes, ELAN-style tier timelines, Penman-ish graph edges, and sparkline / diverging-bar / number-line judgment distributions.
Token ¶
One token with its surface form and offsets within an expression.
Syntax ¶
Syntax(
tokens: list[Token],
columns: list[str],
tags: dict[str, dict[int, str]],
deps: dict[int, tuple[int, str]],
tokenization: str,
)
The token-aligned annotations assembled over one tokenization.
feature_map ¶
feature_map(value: JsonValue) -> dict[str, str]
Flatten a featureMap ({entries:[{key,value}]}) to a dict.
anchor_token_indexes ¶
anchor_token_indexes(anchor: JsonValue) -> list[int]
Return the token indexes an anchor references.
Handles tokenRef (single) and tokenRefSequence (possibly
non-contiguous); other anchor variants reference no tokens.
anchor_tokenization ¶
anchor_tokenization(anchor: JsonValue) -> str
Return the tokenization id a token anchor refers to, or "".
anchor_byte_span ¶
anchor_byte_span(
anchor: JsonValue,
) -> tuple[int, int] | None
Return the (byteStart, byteEnd) of a text or page anchor, or None.
anchor_time_span ¶
anchor_time_span(
anchor: JsonValue,
) -> tuple[int, int] | None
Return the (start_ms, end_ms) of a temporal anchor, or None.
anchor_summary ¶
anchor_summary(anchor: JsonValue) -> str
Return a one-line human descriptor of any anchor variant.
tokenizations_of ¶
tokenizations_of(
browser: RepoBrowser, expr_uri: str
) -> dict[str, list[Token]]
Return tokenizationId -> ordered tokens for an expression.
Reads every segmentation whose expression is this expression; a token's
surface comes from its text when present, else from slicing the
expression text by the token's textSpan byte offsets.
layers_of ¶
layers_of(
browser: RepoBrowser, expr_uri: str
) -> list[tuple[str, Mapping[str, JsonValue]]]
Return the annotation layers anchored to an expression.
sparkline ¶
Render a sequence of magnitudes as an eight-level block sparkline.
hbar ¶
Render a horizontal bar of value scaled against peak.
segmentation_tokens ¶
segmentation_tokens(
browser: RepoBrowser, data: Mapping[str, JsonValue]
) -> str
Render a segmentation as a readable token table per tokenization.
assemble_syntax ¶
assemble_syntax(
browser: RepoBrowser, expr_uri: str
) -> Syntax | None
Assemble token-tag and dependency layers over an expression's tokens.
Returns None when the expression has no tokenization to anchor into.
single_dep_syntax ¶
single_dep_syntax(
browser: RepoBrowser,
expr_uri: str,
layer: Mapping[str, JsonValue],
) -> Syntax | None
Assemble a Syntax holding just one dependency layer's relations.
token_tag_interlinear ¶
token_tag_interlinear(
browser: RepoBrowser,
expr_uri: str,
layer: Mapping[str, JsonValue],
) -> str
Render a single token-tag layer as an interlinear strip over the tokens.
conllu_grid ¶
conllu_grid(syntax: Syntax) -> str
Render the token-aligned tags and dependencies as a CoNLL-U style grid.
dependency_tree ¶
dependency_tree(syntax: Syntax) -> str
Render dependencies as an indented head-to-dependent tree.
interlinear ¶
interlinear(
tokens: list[Token],
rows: list[tuple[str, dict[int, str]]],
) -> str
Render token-tag layers as interlinear rows beneath each token.
rows is ordered (row_label, {tokenIndex: value}) pairs; columns align
on token index and wrap to a readable width, the classic Leipzig layout.
span_overlay ¶
Render labeled character spans as brat-style underlines over the text.
spans is (charStart, charEnd, label); overlapping spans stack onto
separate lanes so nesting stays legible.
span_layer_overlay ¶
span_layer_overlay(
browser: RepoBrowser,
expr_uri: str,
layer: Mapping[str, JsonValue],
) -> str
Render a span-kind layer as labeled underlines over the expression text.
Token-anchored spans resolve to character ranges through the tokenization; byte-anchored spans convert directly. Dataset-agnostic over the anchor union.
tier_timeline ¶
tier_timeline(
tiers: list[tuple[str, list[tuple[int, int, str]]]],
*,
ms_per_col: int = 200,
) -> str
Render time-aligned tiers as an ELAN-style partitur grid.
tiers is (tier_name, [(startMs, endMs, label)]); a shared time ruler
sits on top and each tier is a lane whose boundaries align by column.
document_tags ¶
document_tags(anns: list[Mapping[str, JsonValue]]) -> str
Render document-level annotations as labeled chips.
Confidence is the lexicon's integer score scaled 0-1000 (1000 = maximum) and is shown as a percentage of that range.
graph_edges ¶
graph_edges(
nodes: Mapping[str, Mapping[str, JsonValue]],
edges: list[Mapping[str, JsonValue]],
) -> str
Render graph edges as a source --type--> target adjacency list.
Node references resolve to a readable label through nodes (keyed by
AT-URI) when present, else fall back to the ref's most specific id.
Record views¶
Model-driven view dispatch over the lexicon's own type system: the views available for a record, the columns for a record list, and the rendering of a record through a single focused view at a time.
lairs.tui.views ¶
Type-aware rendering of Layers records for the Browse tab.
:func:render_record dispatches on a record's collection NSID to a Markdown view
that fits the data: an ontology renders as a type hierarchy, an experiment as a
design plus its response sets, a graph as relation cards, a lexicon collection as
its entries, an annotation layer as annotations over text, and so on. Records
without a bespoke view fall back to a clean key-value rendering, so every record
type is viewable.
Everything reads the record's raw JSON (JsonValue) rather than typed
attributes, so the helpers stay concretely typed. :data:LIST_COLUMNS and
:func:summarize drive the records list with type-appropriate columns.
record_views ¶
record_views(
browser: RepoBrowser,
nsid: str,
uri: str,
data: Mapping[str, JsonValue],
) -> list[tuple[str, Callable[[], str]]]
Return the ordered (label, render) views available for a record.
Each view is a single focused visualization; the caller flips between them.
Only views with content for this record are offered, so the panel stays
uncluttered. A Detail view is always last so every field stays reachable.
view_modes ¶
view_modes(
browser: RepoBrowser,
nsid: str,
uri: str,
data: Mapping[str, JsonValue],
) -> list[str]
Return the labels of the views available for a record.
render_view ¶
render_view(
browser: RepoBrowser,
nsid: str,
uri: str,
data: Mapping[str, JsonValue],
mode: str,
) -> str
Render one named view of a record, falling back to the first view.
render_record ¶
render_record(
browser: RepoBrowser,
nsid: str,
uri: str,
data: Mapping[str, JsonValue],
) -> str
Render a record's first (default) view.
columns_for ¶
Return the records-list columns for a record type, or a generic default.
summarize ¶
summarize(
nsid: str, uri: str, data: Mapping[str, JsonValue]
) -> tuple[str, ...]
Return a one-row summary of a record matching :func:columns_for.
| PARAMETER | DESCRIPTION |
|---|---|
nsid
|
The record's collection NSID.
TYPE:
|
uri
|
The record's AT-URI, used by the generic summary.
TYPE:
|
data
|
The record's raw JSON.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
tuple of str
|
The cell values for the record's row. |
Record registry¶
The map from pub.layers.* NSIDs to generated record models, with the
namespace and short-label helpers that group records in the Browse type
tree.
lairs.tui.registry ¶
The record-type registry: a map from collection NSID to its model class.
The browser needs to decode any pub.layers.* record into a typed model, and
to present record types in a stable, readable order grouped by namespace. This
module is the single source of both. RECORD_MODELS is exhaustive over the
record (not method or permission-set) lexicons; a test cross-checks it against
the generated modules so it cannot silently drift.
Screen panes¶
The three composable panes that make up the application: an ExplorePane
over the discovery index, a BrowsePane over a local repository, and a
QueryPane over the materialized views.
lairs.tui.screens ¶
Screens and panes for the lairs explorer TUI.
BrowsePane ¶
Bases: Horizontal
A repository record browser with a type tree, list, and detail view.
| PARAMETER | DESCRIPTION |
|---|---|
repo_path
|
Filesystem path to a local Repository, or
TYPE:
|
on_tree_node_selected ¶
Load the records of the selected type into the records table.
on_input_changed ¶
Re-filter the records table as the filter box changes.
on_data_table_row_highlighted ¶
Render the highlighted record in the detail panel.
action_cycle_view ¶
Flip to the next (or previous) view of the current record.
ExplorePane ¶
Bases: Horizontal
A discovery-index browser with facet filters and a detail card.
| PARAMETER | DESCRIPTION |
|---|---|
index_path
|
Filesystem path to a discovery index directory, or
TYPE:
|
QueryPane ¶
Bases: Horizontal
A three-mode query workbench over a materialized corpus.
| PARAMETER | DESCRIPTION |
|---|---|
data_path
|
Filesystem path to a directory of materialized Parquet views, or
TYPE:
|
on_unmount ¶
Close the DuckDB connection held by the query engine, if any.
The pane owns the engine for its lifetime; closing on unmount releases the DuckDB connection rather than leaking it until process exit.
on_radio_set_changed ¶
Seed the starter query when the mode changes.
on_button_pressed ¶
Run the query when the Run button is pressed.
on_tree_node_selected ¶
Insert a selected table or column name at the editor cursor.
A separating space is added when the cursor does not already sit at a token boundary, so an identifier never fuses with the preceding text; the cursor follows the insertion.
action_run ¶
Execute the editor's text in the current mode and show the result.