exlab_wizard.ui.pages.main#

Main window (Frontend Spec §3).

Layout:

  • Left drawer – search box + filter chips + tree.

  • Right pane – tabs (Details / Problems) + detail / problems view.

  • Header toolbar – New Project / New Run / New Test Run / Settings / Refresh.

  • Bottom status bar – Sync / Validator / LIMS segments.

Pre-Phase-13 the tray and window subprocess plumbing isn’t wired here; this page is invoked from the FastAPI app’s NiceGUI mount point (GET /).

Functions

chip_state_to_tree_filters(state[, search])

Translate a chip group state into a TreeFilters.

count_search_results(nodes)

Count the project + run rows surfaced under the equipment roots.

density_card_class(density)

Return the Files-card CSS class for the current row density (§4.8).

problems_badge_text(state)

Return the count text shown on the Problems tab.

render_file_explorer_page(*, ...[, ...])

Render the rebuilt three-region file-explorer main window.

setup_incomplete_banner_props([next_action])

Banner content for the setup-incomplete state (§3.1.4).

Classes

MainPageState([setup_incomplete, ...])

Render state for the main page.

class exlab_wizard.ui.pages.main.MainPageState(setup_incomplete=False, setup_next_action=None, selected_node=None, chip_state=<factory>, active_tab='details', problems_count_hard=0, problems_count_soft=0, operations_count=0, operations_input_required=0, creation_in_flight=False, orchestrator_enabled=True, staging_dock=None, right_pane_collapsed=False, folder_feed_path=None, selected_node_kind=None, expand_state=<factory>, selected_node_is_received=False, selected_file_path=None, selected_file=None, search_query='', density='', validator_state='normal', lims_state='normal', staging_state='normal')[source]#

Bases: object

Render state for the main page.

GUI/Orchestrator Redesign §4: the legacy two-tab Details / Problems layout coexists with a new three-region file-explorer renderer (render_file_explorer_page); the v1 fields stay so existing flows keep working until Phase 9 / 10 retire them.

Parameters:
active_tab: str = 'details'#
chip_state: ChipState#
creation_in_flight: bool = False#
density: str = ''#
expand_state: dict[str, bool]#
folder_feed_path: str | None = None#
lims_state: str = 'normal'#
operations_count: int = 0#
operations_input_required: int = 0#
orchestrator_enabled: bool = True#
problems_count_hard: int = 0#
problems_count_soft: int = 0#
right_pane_collapsed: bool = False#
search_query: str = ''#
selected_file: dict[str, Any] | None = None#
selected_file_path: str | None = None#
selected_node: str | None = None#
selected_node_is_received: bool = False#

True when the selected tree node is received equipment (decision 1): the three creation buttons (New Project / New Run / New Test Run) are disabled while this is True.

selected_node_kind: str | None = None#
setup_incomplete: bool = False#
setup_next_action: str | None = None#
staging_dock: StagingDockState | None = None#
staging_state: str = 'normal'#
validator_state: str = 'normal'#
exlab_wizard.ui.pages.main.chip_state_to_tree_filters(state, search='')[source]#

Translate a chip group state into a TreeFilters.

Parameters:
Return type:

TreeFilters

exlab_wizard.ui.pages.main.count_search_results(nodes)[source]#

Count the project + run rows surfaced under the equipment roots.

build_nodes() always emits every equipment row – even one with no matching children – so the equipment tally is not a useful “did the search find anything” signal. This sums the project + run rows instead: the number the search-result pill shows, and whose 0 value drives the no-matches state (Phase 5 / OQ-2). Pure so it is testable without NiceGUI.

Parameters:

nodes (list[TreeNode])

Return type:

int

exlab_wizard.ui.pages.main.density_card_class(density)[source]#

Return the Files-card CSS class for the current row density (§4.8).

"compact" -> "exlab-density-compact" (the theme rule tightens the file-row vertical padding to --sp-1); anything else -> "" (the default comfortable --sp-2 inherited from Tailwind .p-2). Pure so it is testable without NiceGUI.

Parameters:

density (str)

Return type:

str

exlab_wizard.ui.pages.main.problems_badge_text(state)[source]#

Return the count text shown on the Problems tab.

Frontend §3.2: hard count primary, soft count secondary (e.g. 3 + 12).

Parameters:

state (MainPageState)

Return type:

str

exlab_wizard.ui.pages.main.render_file_explorer_page(*, on_open_new_project, on_open_new_run, on_open_new_test_run, on_open_add_equipment, on_open_settings, on_refresh, on_select_node, on_open_operations=None, on_navigate_breadcrumb=None, on_toggle_right_pane=None, on_run_staging_action=None, on_tree_context_action=None, on_file_context_action=None, on_select_file=None, on_refresh_folder=None, on_search=None, on_toggle_density=None, state=None, hierarchy=None, file_list_entries=None, metadata_payload=None, tree_expand_all=False)[source]#

Render the rebuilt three-region file-explorer main window.

GUI/Orchestrator Redesign §4. Header toolbar + breadcrumb + splitter (tree | live file list | metadata/problems pane) + footer status bar. The render function stays free of session-store / API deps: state is injected; the caller wires the callbacks.

file_list_entries and metadata_payload carry the data the centre and right panes display when a node is selected; both are optional so unit tests can render the empty-state. The mount layer sources them from the live FolderFeed payload and the per-node metadata builder respectively.

tree_expand_all forwards to build_tree()’s expand_all kwarg – only set by e2e tests that need every node visible in the DOM up front.

Parameters:
Return type:

Any

exlab_wizard.ui.pages.main.setup_incomplete_banner_props(next_action=None)[source]#

Banner content for the setup-incomplete state (§3.1.4).

next_action is the §4.9.3 next-action discriminator (e.g. configure_rclone_remote). When supplied it tailors the subline so the operator knows exactly which section to open; the rclone-only NAS migration (2026-05-26) added the rclone-remote variant.

Parameters:

next_action (str | None)

Return type:

dict[str, str]