Coverage for src/prosemark/freewriting/ports/tui_adapter.py: 100%
81 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-09-24 18:08 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-09-24 18:08 +0000
1"""TUI adapter port interfaces for freewriting interface.
3This module defines the port interfaces for the terminal user interface
4components of the freewriting feature.
5"""
7from __future__ import annotations
9from abc import ABC, abstractmethod
10from dataclasses import dataclass
11from typing import TYPE_CHECKING, Any
13if TYPE_CHECKING: # pragma: no cover
14 from collections.abc import Callable
16 from prosemark.freewriting.domain.models import FreewriteSession, SessionConfig
17 from prosemark.freewriting.ports.freewrite_service import FreewriteServicePort
20@dataclass(frozen=True)
21class UIState:
22 """Current state of the TUI interface."""
24 session: FreewriteSession | None
25 input_text: str
26 display_lines: list[str]
27 word_count: int
28 elapsed_time: int
29 time_remaining: int | None
30 progress_percent: float | None
31 error_message: str | None
32 is_paused: bool
35@dataclass(frozen=True)
36class TUIConfig:
37 """Configuration for TUI appearance and behavior."""
39 theme: str
40 content_height_percent: int = 80
41 input_height_percent: int = 20
42 show_word_count: bool = True
43 show_timer: bool = True
44 auto_scroll: bool = True
45 max_display_lines: int = 1000
48class TUIAdapterPort(ABC):
49 """Port interface for TUI operations.
51 This port defines the contract for main TUI operations such as
52 session management and content handling.
53 """
55 @property
56 @abstractmethod
57 def freewrite_service(self) -> FreewriteServicePort:
58 """Freewrite service instance for session operations.
60 Returns:
61 The freewrite service instance used by this TUI adapter.
63 """
65 @abstractmethod
66 def initialize_session(self, config: SessionConfig) -> FreewriteSession:
67 """Initialize a new freewriting session.
69 Args:
70 config: Session configuration from CLI.
72 Returns:
73 Created session object.
75 Raises:
76 ValidationError: If configuration is invalid.
78 """
80 @abstractmethod
81 def handle_input_submission(self, session: FreewriteSession, input_text: str) -> FreewriteSession:
82 """Handle user pressing ENTER in input box.
84 Args:
85 session: Current session state.
86 input_text: Text from input box.
88 Returns:
89 Updated session after content is appended.
91 Raises:
92 FileSystemError: If save operation fails.
94 """
96 @abstractmethod
97 def get_display_content(self, session: FreewriteSession, max_lines: int) -> list[str]:
98 """Get content lines to display in content area.
100 Args:
101 session: Current session.
102 max_lines: Maximum lines to return (for bottom of file view).
104 Returns:
105 List of content lines for display.
107 """
109 @abstractmethod
110 def calculate_progress(self, session: FreewriteSession) -> dict[str, Any]:
111 """Calculate session progress metrics.
113 Args:
114 session: Current session.
116 Returns:
117 Dictionary with progress information:
118 - word_count: int
119 - elapsed_time: int
120 - time_remaining: Optional[int]
121 - progress_percent: Optional[float]
122 - goals_met: Dict[str, bool]
124 """
126 @abstractmethod
127 def handle_error(self, error: Exception, session: FreewriteSession) -> UIState:
128 """Handle errors during session operations.
130 Args:
131 error: The exception that occurred.
132 session: Current session state.
134 Returns:
135 Updated UI state with error information.
137 """
140class TUIEventPort(ABC):
141 """Port interface for TUI event handling.
143 This port defines the contract for handling various TUI events
144 such as user input and session state changes.
145 """
147 @abstractmethod
148 def on_input_change(self, callback: Callable[[str], None]) -> None:
149 """Register callback for input text changes.
151 Args:
152 callback: Function to call when input changes.
154 """
156 @abstractmethod
157 def on_input_submit(self, callback: Callable[[str], None]) -> None:
158 """Register callback for input submission (ENTER key).
160 Args:
161 callback: Function to call when input is submitted.
163 """
165 @abstractmethod
166 def on_session_pause(self, callback: Callable[[], None]) -> None:
167 """Register callback for session pause events.
169 Args:
170 callback: Function to call when session is paused.
172 """
174 @abstractmethod
175 def on_session_resume(self, callback: Callable[[], None]) -> None:
176 """Register callback for session resume events.
178 Args:
179 callback: Function to call when session is resumed.
181 """
183 @abstractmethod
184 def on_session_exit(self, callback: Callable[[], None]) -> None:
185 """Register callback for session exit events.
187 Args:
188 callback: Function to call when session exits.
190 """
193class TUIDisplayPort(ABC):
194 """Port interface for TUI display operations.
196 This port defines the contract for updating the TUI display
197 components such as content area and status display.
198 """
200 @abstractmethod
201 def update_content_area(self, lines: list[str]) -> None:
202 """Update the main content display area.
204 Args:
205 lines: Content lines to display.
207 """
209 @abstractmethod
210 def update_stats_display(self, stats: dict[str, Any]) -> None:
211 """Update statistics display (word count, timer, etc.).
213 Args:
214 stats: Statistics to display.
216 """
218 @abstractmethod
219 def clear_input_area(self) -> None:
220 """Clear the input text box."""
222 @abstractmethod
223 def show_error_message(self, message: str) -> None:
224 """Display error message to user.
226 Args:
227 message: Error message to show.
229 """
231 @abstractmethod
232 def hide_error_message(self) -> None:
233 """Hide any currently displayed error message."""
235 @abstractmethod
236 def set_theme(self, theme_name: str) -> None:
237 """Apply UI theme.
239 Args:
240 theme_name: Name of theme to apply.
242 """
245# UI Event Data Classes
246@dataclass(frozen=True)
247class InputSubmittedEvent:
248 """Event fired when user submits input."""
250 content: str
251 timestamp: str
254@dataclass(frozen=True)
255class SessionProgressEvent:
256 """Event fired when session progress updates."""
258 word_count: int
259 elapsed_time: int
260 progress_percent: float | None
263@dataclass(frozen=True)
264class ErrorEvent:
265 """Event fired when an error occurs."""
267 error_type: str
268 message: str
269 recoverable: bool
272@dataclass(frozen=True)
273class SessionCompletedEvent:
274 """Event fired when session completes."""
276 final_word_count: int
277 total_time: int
278 output_file: str