spacr.qt.widgets.console_panel
==============================

.. py:module:: spacr.qt.widgets.console_panel

.. autoapi-nested-parse::

   ConsolePanel — merged pipeline console + AI chat panel.

   One vertical scrolling area shows both pipeline stdout AND AI chat
   messages, separated by dark-gray "topic" bars ("Mask", "Measure",
   "spaCR AI", …). Below the scroll sits an input row where the user
   can type at any time; a switch on the left decides whether the
   message goes to the AI or is ignored.

   Public API
   ----------
   * begin_topic(label)          — insert a dark-gray divider bar
                                   (used at the start of every pipeline
                                   run and every time we switch to/from
                                   the AI)
   * append_stdout(text)         — append pipeline output; if the last
                                   entry isn't already a stdout block it
                                   starts a new one
   * append_error(traceback)     — same as stdout but red-tinted
   * open_error_flow(tb, app)    — inject the AI-explainer prompt for a
                                   traceback and stream the reply into
                                   a fresh spaCR-AI section
   * clear()                     — wipe every entry

   Streaming state
   ---------------
   The panel owns the AI thread+worker itself so state stays coherent
   even as the user switches between pipeline apps.





Module Contents
---------------

.. py:class:: ConsolePanel(active_app_label: str = '', parent=None)

   Bases: :py:obj:`PySide6.QtWidgets.QWidget`


   Merged pipeline stdout + AI chat panel.

   Owns the AI stream thread so provider switches and app changes
   do not orphan a running subprocess. See the module docstring for
   the full public surface.

   :ivar ai_stream_finished: emitted when an AI stream ends (ok or
       error) so the parent screen can flip its Cancel button back.


   .. py:attribute:: ai_stream_finished


   .. py:method:: set_active_app(label: str) -> None

      Set the label used in the next auto-inserted topic divider.



   .. py:method:: begin_topic(label: str) -> None

      Insert a divider bar labeled `label` (e.g. 'Mask'). Callers
      can force a new section this way. AI content NEVER uses this
      — AI replies flow inline in the same stdout block.



   .. py:method:: append_stdout(text: str) -> None

      Append pipeline output. Opens a fresh stdout block (with a
      topic divider) at the very first stdout of a session; opens a
      divider-less block after a bubble breaks the flow.



   .. py:method:: append_error(tb: str) -> None

      Append a red-tinted error block, prefixed with an ERROR topic bar.

      :param tb: traceback text; empty strings are ignored.



   .. py:method:: clear() -> None

      Wipe every entry (topic bars, stdout blocks, chat bubbles).



   .. py:method:: set_ai_active(on: bool) -> None

      Enable/disable AI routing for Enter-submits from the input.



   .. py:method:: set_ai_provider(provider_name: Optional[str]) -> None

      Select the provider used for AI submissions, or None to unset.



   .. py:method:: cancel_ai() -> None

      Public — AppScreen calls this if the user cancels a stream.



   .. py:method:: is_ai_streaming() -> bool

      Return True while an AI response is being streamed.



   .. py:method:: shutdown() -> None

      Cancel any active stream and block until its QThread has
      exited. Must be called before the panel (or its parent window)
      is destroyed — otherwise Python drops the last reference to
      the running QThread and Qt aborts with:
      `QThread: Destroyed while thread '' is still running`.

      The cancel path kills the CLI subprocess directly so the
      stream reader unblocks immediately; we then wait for the
      worker's run() to return and the QThread to quit normally.



   .. py:method:: closeEvent(event) -> None

      Ensure the AI thread is drained before Qt destroys the panel.



   .. py:method:: open_error_flow(traceback_text: str, active_app: str = '') -> None

      Send a traceback to the AI explainer and stream the reply inline.

      :param traceback_text: raw traceback captured from the pipeline.
      :param active_app: optional app label used in the framing prompt.



