diff --git a/src/gui_2.py b/src/gui_2.py index 2c3eade2..974151e7 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -5333,8 +5333,11 @@ def render_operations_hub(app: App) -> None: if exp: render_external_tools_panel(app) imgui.separator(); imgui.text("") - try: render_external_editor_panel(app) - except Exception as e: imgui.text_colored(theme.get_color("status_error"), f"Error: {str(e)}") + ext_panel_result = _render_operations_hub_external_editor_panel_result(app) + if not ext_panel_result.ok: + if not hasattr(app, '_last_request_errors'): app._last_request_errors = [] + app._last_request_errors.append(("_render_operations_hub_external_editor_panel", ext_panel_result.errors[0])) + imgui.text_colored(theme.get_color("status_error"), f"Error: {ext_panel_result.errors[0].message}") with imscope.tab_item("Workspace Layouts") as (exp, _): if exp: imgui.text("Experimental: Auto-switch layout by Tier") @@ -7960,6 +7963,31 @@ def _render_context_batch_actions_preview_result(app: "App") -> Result[str]: original=e, )]) + +def _render_operations_hub_external_editor_panel_result(app: "App") -> Result[bool]: + """Drain-aware variant of L5380 render_operations_hub External Tools tab. + + Extracts the render_external_editor_panel call try/except from + render_operations_hub External Tools tab into a Result-returning helper. + On success, returns Result(data=True). On exception, converts the + exception to ErrorInfo and returns Result(data=False, errors=[ErrorInfo]). + + The legacy wrapper drains errors to app._last_request_errors (per FR-BC-4 + event-handler drain pattern; data plane attribute). + + [C: src/gui_2.py:render_operations_hub (L5380 legacy wrapper)] + """ + try: + render_external_editor_panel(app) + return Result(data=True) + except Exception as e: + return Result(data=False, errors=[ErrorInfo( + kind=ErrorKind.INTERNAL, + message=f"Render external editor panel from operations hub failed: {e}", + source="gui_2._render_operations_hub_external_editor_panel_result", + original=e, + )]) + #endregion: Phase 5 Event Handler Result Helpers #endregion: MMA diff --git a/tests/test_gui_2_result.py b/tests/test_gui_2_result.py index 243410d8..1c624790 100644 --- a/tests/test_gui_2_result.py +++ b/tests/test_gui_2_result.py @@ -1107,3 +1107,38 @@ def test_phase_5_l3582_render_context_batch_actions_preview_result_failure(): assert err.source == "gui_2._render_context_batch_actions_preview_result" assert "generate failed" in err.message assert "Error" in result.data + + +def test_phase_5_l5380_render_operations_hub_external_editor_panel_result_success(): + """ + L5380 _render_operations_hub_external_editor_panel_result returns Result.ok=True on success. + + The helper wraps the render_external_editor_panel call within render_operations_hub + External Tools tab try/except. On success, returns Result(data=True). + """ + from src import gui_2 + from unittest.mock import MagicMock, patch + app = MagicMock() + with patch.object(gui_2, "render_external_editor_panel"): + result = gui_2._render_operations_hub_external_editor_panel_result(app) + assert result.ok, f"Expected ok=True on success, got errors: {result.errors}" + assert result.data is True + + +def test_phase_5_l5380_render_operations_hub_external_editor_panel_result_failure(): + """ + L5380 _render_operations_hub_external_editor_panel_result returns Result.ok=False on failure. + + When render_external_editor_panel raises, the helper converts the exception + to ErrorInfo and returns Result(data=False, errors=[ErrorInfo]). + """ + from src import gui_2 + from unittest.mock import MagicMock, patch + app = MagicMock() + with patch.object(gui_2, "render_external_editor_panel", side_effect=RuntimeError("ext editor render blew up")): + result = gui_2._render_operations_hub_external_editor_panel_result(app) + assert not result.ok, f"Expected ok=False on failure, got data: {result.data}" + assert result.errors, "Expected at least one error on failure" + err = result.errors[0] + assert err.source == "gui_2._render_operations_hub_external_editor_panel_result" + assert "ext editor render blew up" in err.message