Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 89 additions & 4 deletions src/parxy_cli/tui/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
ResultsViewer,
Footer,
WelcomeContainer,
WorkspaceViewer,
find_processed_files,
)

if TYPE_CHECKING:
Expand Down Expand Up @@ -57,6 +59,11 @@ def commands(self) -> tuple[CommandType, ...]:
app.action_toggle_sidebar,
'Toggle sidebar visibility to expand main area',
),
(
'view: View processed files',
app.action_view_processed,
'View pre-processed results for the selected file',
),
(
'file: Refresh file tree',
app.action_refresh,
Expand Down Expand Up @@ -115,6 +122,7 @@ class ParxyTUI(App):
Binding('ctrl+n', 'new_parse', 'New parse', key_display='Ctrl+N'),
Binding('ctrl+s', 'start_parse', 'Start parsing', key_display='Ctrl+S'),
Binding('ctrl+b', 'toggle_sidebar', 'Toggle sidebar', key_display='Ctrl+B'),
Binding('ctrl+v', 'view_processed', 'View processed', key_display='Ctrl+V'),
Binding(
'ctrl+c',
'request_quit',
Expand Down Expand Up @@ -147,6 +155,9 @@ def compose(self) -> ComposeResult:
# Results viewer (initially hidden)
yield ResultsViewer(self.results, id='results-viewer')

# Workspace viewer placeholder (initially hidden, will be replaced dynamically)
yield Container(id='workspace-viewer')

yield Footer()

# # Sidebar (right side, 1/3 width)
Expand All @@ -166,7 +177,17 @@ def on_file_tree_selector_file_selected(
"""Handle file selection from the tree selector widget."""
self.current_file = event.path
status_bar = self.query_one('#status-bar', Static)
status_bar.update(f'File selected: {self.current_file.name}')

# Check if there are processed files for this selection
processed_files = find_processed_files(self.workspace, self.current_file.name)
if processed_files:
driver_names = ', '.join(info.driver_name for info in processed_files)
status_bar.update(
f'File: {self.current_file.name} | '
f'Processed: {driver_names} (Ctrl+V to view)'
)
else:
status_bar.update(f'File selected: {self.current_file.name}')

def on_button_pressed(self, event: Button.Pressed) -> None:
"""Handle button presses."""
Expand Down Expand Up @@ -284,9 +305,10 @@ def action_start_parse(self) -> None:

def action_new_parse(self) -> None:
"""Start a new parse (return to parser selection)."""
# Show welcome container and hide results viewer
# Show welcome container and hide results viewer and workspace viewer
self.query_one('#welcome-container').display = True
self.query_one('#results-viewer').display = False
self.query_one('#workspace-viewer').display = False

# Clear results but keep file selection
self.results.clear()
Expand All @@ -298,9 +320,20 @@ def action_new_parse(self) -> None:
# Update status
status_bar = self.query_one('#status-bar', Static)
if self.current_file:
status_bar.update(
f'Ready for new parse. File selected: {self.current_file.name}'
# Check for processed files
processed_files = find_processed_files(
self.workspace, self.current_file.name
)
if processed_files:
driver_names = ', '.join(info.driver_name for info in processed_files)
status_bar.update(
f'File: {self.current_file.name} | '
f'Processed: {driver_names} (Ctrl+V to view)'
)
else:
status_bar.update(
f'Ready for new parse. File selected: {self.current_file.name}'
)
else:
status_bar.update('Ready for new parse. Select a file to begin.')

Expand Down Expand Up @@ -341,6 +374,58 @@ def action_request_quit(self) -> None:
f'Press Ctrl+C again within {self._ctrl_c_window:.0f}s to quit, or use Ctrl+Q'
)

def action_view_processed(self) -> None:
"""View pre-processed results for the selected file."""
status_bar = self.query_one('#status-bar', Static)

if not self.current_file:
status_bar.update('Error: Please select a file first!')
return

# Check if there are processed files
processed_files = find_processed_files(self.workspace, self.current_file.name)

if not processed_files:
status_bar.update(
f'No processed results found for {self.current_file.name}. '
'Parse the file first with Ctrl+S.'
)
return

# Run the async view method
self.run_worker(self.show_workspace_viewer())

async def show_workspace_viewer(self) -> None:
"""Show the workspace viewer for the current file."""
if not self.current_file:
return

status_bar = self.query_one('#status-bar', Static)
status_bar.update(f'Loading processed results for {self.current_file.name}...')

# Hide welcome container and results viewer
self.query_one('#welcome-container').display = False
self.query_one('#results-viewer').display = False

# Remove old workspace viewer and create new one
old_viewer = self.query_one('#workspace-viewer', Container)
await old_viewer.remove()

# Create and mount new workspace viewer
main_area = self.query_one('#main-area', Vertical)
new_viewer = WorkspaceViewer(
self.workspace,
self.current_file.name,
id='workspace-viewer',
)
new_viewer.display = True
await main_area.mount(new_viewer, before='Footer')

# Update status
processed_files = find_processed_files(self.workspace, self.current_file.name)
driver_names = ', '.join(info.driver_name for info in processed_files)
status_bar.update(f'Viewing {self.current_file.name} - Drivers: {driver_names}')


def run_tui(workspace: Path):
"""Run the TUI application."""
Expand Down
85 changes: 64 additions & 21 deletions src/parxy_cli/tui/app.tcss
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,17 @@



Screen {
layout: grid;
grid-size: 2 1;
grid-columns: 2fr 1fr;
}

/* When sidebar is hidden, use single column layout */
Screen.sidebar-hidden {
grid-size: 1 1;
grid-columns: 1fr;
}
Screen {
layout: grid;
grid-size: 2 1;
grid-columns: 2fr 1fr;
}
/* When sidebar is hidden, use single column layout */
Screen.sidebar-hidden {
grid-size: 1 1;
grid-columns: 1fr;
}



Expand Down Expand Up @@ -80,16 +80,16 @@ Footer {
}


#welcome-container {
height: auto;
width: 100%;
}

#results-viewer {
height: 1fr;
width: 100%;
display: none;
}
#welcome-container {
height: auto;
width: 100%;
}
#results-viewer {
height: 1fr;
width: 100%;
display: none;
}

#status-container {
height: auto;
Expand Down Expand Up @@ -223,4 +223,47 @@ Checkbox {
width: 100%;
height: 100%;
padding: 1;
}

/* Workspace viewer */
#workspace-viewer {
height: 1fr;
width: 100%;
display: none;
}

#workspace-viewer-container {
height: 100%;
width: 100%;
layout: vertical;
}

#driver-comparison-table {
height: auto;
max-height: 12;
margin-bottom: 1;
}

#driver-tabs {
height: 1fr;
}

#no-results-message {
padding: 2;
text-align: center;
color: $text-muted;
}

.subsection-title {
padding: 1 0;
}

/* Workspace summary */
#workspace-summary-container {
height: 100%;
width: 100%;
}

#workspace-summary-table {
height: 1fr;
}
10 changes: 10 additions & 0 deletions src/parxy_cli/tui/widgets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
from parxy_cli.tui.widgets.welcome_container import WelcomeContainer
from parxy_cli.tui.widgets.footer import Footer
from parxy_cli.tui.widgets.logo import Logo
from parxy_cli.tui.widgets.workspace_viewer import (
WorkspaceViewer,
WorkspaceSummary,
ProcessedFileInfo,
find_processed_files,
)

__all__ = [
'FileTreeSelector',
Expand All @@ -28,4 +34,8 @@
'WelcomeContainer',
'Footer',
'Logo',
'WorkspaceViewer',
'WorkspaceSummary',
'ProcessedFileInfo',
'find_processed_files',
]
12 changes: 7 additions & 5 deletions src/parxy_cli/tui/widgets/logo.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
from textual.widget import Widget

LOGO_LINES = [
'█▀▀▀█ █▀▀▀█ █▀▀▀█ █▄ ▄█ █░░░█',
'█░░░█ █▀▀▀█ █▀▀█▀ ▄▄▀▄▄ ▀▀█▀▀',
'█▀▀▀▀ ▀ ▀ ▀ ▀ ▀ ▀ ▀ ',
'╔═══╗ ╔═══╗ ╔═══╗ ╦ ╦ ╦ ╦',
'║ ║ ║ ║ ║ ║ ╚═╤═╝ ║ ║',
'╬═══╝ ╬═══╬ ╬═══╝ │ ╚═╦═╝',
'║ ║ ║ ║ \\ ╔═╧═╗ ║ ',
'║ ║ ║ ║ \\ ║ ║ ║ ',
]


Expand All @@ -14,8 +16,8 @@ class Logo(Widget):

DEFAULT_CSS = """
Logo {
width: 30;
height: 3;
width: 33;
height: 5;
}
"""

Expand Down
Loading