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
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# AGENTS.md

This repository doesn't contain any agent specific instructions other than its README.md and its linked resources.
This repository doesn't contain any agent specific instructions other than its [README.md](README.md) and its linked resources.
8 changes: 4 additions & 4 deletions pdm.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ requires-python = ">=3.14, <3.15"
dependencies = [
"pyside6>=6.10.2",
"packaging>=26.0",
"porringer>=0.2.1.dev51",
"porringer>=0.2.1.dev52",
"qasync>=0.28.0",
"velopack>=0.0.1442.dev64255",
"typer>=0.24.1",
Expand Down
18 changes: 17 additions & 1 deletion synodic_client/application/screen/action_card.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,9 @@ def _build_top_row(self) -> QHBoxLayout:

self._package_label = QLabel()
self._package_label.setStyleSheet(ACTION_CARD_PACKAGE_STYLE)
self._package_label.setTextInteractionFlags(
Qt.TextInteractionFlag.TextSelectableByMouse,
)
top.addWidget(self._package_label)

top.addStretch()
Expand Down Expand Up @@ -327,6 +330,9 @@ def _build_description_row(self) -> QLabel:
self._desc_label = QLabel()
self._desc_label.setStyleSheet(ACTION_CARD_DESC_STYLE)
self._desc_label.setWordWrap(True)
self._desc_label.setTextInteractionFlags(
Qt.TextInteractionFlag.TextSelectableByMouse,
)
return self._desc_label

def _build_command_row(self) -> QWidget:
Expand Down Expand Up @@ -377,9 +383,13 @@ def mousePressEvent(self, event: object) -> None: # noqa: N802
"""Toggle the inline log body on click."""
if self._is_skeleton or not hasattr(self, '_log_output'):
return
# Don't toggle the log when clicking the copy button
# Don't toggle the log when clicking interactive child widgets
if hasattr(self, '_copy_btn') and self._copy_btn.underMouse():
return
if hasattr(self, '_package_label') and self._package_label.underMouse():
return
if hasattr(self, '_desc_label') and self._desc_label.underMouse():
return
self._toggle_log()

def _toggle_log(self) -> None:
Expand Down Expand Up @@ -560,6 +570,12 @@ def set_check_result(self, result: SetupActionResult) -> None:
self._status_label.setText(label)
self._status_label.setStyleSheet(ACTION_CARD_STATUS_NEEDED)

# Surface diagnostic detail (e.g. SCM URL mismatch) as a tooltip
if result.message:
self._status_label.setToolTip(result.message)
else:
self._status_label.setToolTip('')

# Version column
self._check_available_version = result.available_version
if result.installed_version and result.available_version:
Expand Down
18 changes: 17 additions & 1 deletion synodic_client/application/screen/screen.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
normalize_manifest_key,
)
from synodic_client.application.screen.spinner import SpinnerWidget
from synodic_client.application.screen.update_banner import UpdateBanner
from synodic_client.application.theme import (
CARD_SPACING,
COMPACT_MARGINS,
Expand Down Expand Up @@ -815,6 +816,9 @@ def __init__(
self.setMinimumSize(*MAIN_WINDOW_MIN_SIZE)
self.setWindowIcon(app_icon())

# Update banner — always available, starts hidden.
self._update_banner = UpdateBanner(self)

@property
def porringer(self) -> API | None:
"""Return the porringer API instance, if available."""
Expand All @@ -825,6 +829,11 @@ def plugins_view(self) -> PluginsView | None:
"""Return the plugins view, if initialised."""
return self._plugins_view

@property
def update_banner(self) -> UpdateBanner:
"""Return the update banner widget."""
return self._update_banner

def show(self) -> None:
"""Show the window, initializing UI lazily on first show."""
if self._tabs is None and self._porringer is not None and self._config is not None:
Expand All @@ -843,7 +852,14 @@ def show(self) -> None:
gear_btn.clicked.connect(self.settings_requested.emit)
self._tabs.setCornerWidget(gear_btn)

self.setCentralWidget(self._tabs)
# Container: banner above tabs
container = QWidget(self)
container_layout = QVBoxLayout(container)
container_layout.setContentsMargins(0, 0, 0, 0)
container_layout.setSpacing(0)
container_layout.addWidget(self._update_banner)
container_layout.addWidget(self._tabs)
self.setCentralWidget(container)

# Paint the window immediately, then refresh data asynchronously
super().show()
Expand Down
5 changes: 5 additions & 0 deletions synodic_client/application/screen/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,11 @@ def set_update_status(self, text: str) -> None:
"""Set the inline status text next to the *Check for Updates* button."""
self._update_status_label.setText(text)

def set_checking(self) -> None:
"""Enter the *checking* state — disable button and show status."""
self._check_updates_btn.setEnabled(False)
self._update_status_label.setText('Checking\u2026')

def reset_check_updates_button(self) -> None:
"""Re-enable the *Check for Updates* button after a check completes."""
self._check_updates_btn.setEnabled(True)
Expand Down
Loading
Loading