synodic-client 0.0.1.dev83__tar.gz → 0.0.1.dev84__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/PKG-INFO +1 -1
  2. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/pyproject.toml +1 -1
  3. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/screen/tray.py +26 -49
  4. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/LICENSE.md +0 -0
  5. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/README.md +0 -0
  6. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/__init__.py +0 -0
  7. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/__main__.py +0 -0
  8. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/__init__.py +0 -0
  9. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/bootstrap.py +0 -0
  10. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/config_store.py +0 -0
  11. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/data.py +0 -0
  12. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/debug.py +0 -0
  13. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/icon.py +0 -0
  14. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/init.py +0 -0
  15. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/instance.py +0 -0
  16. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/package_state.py +0 -0
  17. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/qt.py +0 -0
  18. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/schema.py +0 -0
  19. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/screen/__init__.py +0 -0
  20. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/screen/action_card.py +0 -0
  21. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/screen/card.py +0 -0
  22. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/screen/install.py +0 -0
  23. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/screen/install_workers.py +0 -0
  24. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/screen/log_panel.py +0 -0
  25. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/screen/plugin_row.py +0 -0
  26. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/screen/projects.py +0 -0
  27. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/screen/schema.py +0 -0
  28. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/screen/screen.py +0 -0
  29. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/screen/settings.py +0 -0
  30. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/screen/sidebar.py +0 -0
  31. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/screen/spinner.py +0 -0
  32. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/screen/tool_update_controller.py +0 -0
  33. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/screen/update_banner.py +0 -0
  34. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/screen/wsl.py +0 -0
  35. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/theme.py +0 -0
  36. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/update_controller.py +0 -0
  37. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/update_model.py +0 -0
  38. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/application/uri.py +0 -0
  39. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/cli/__init__.py +0 -0
  40. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/cli/config.py +0 -0
  41. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/cli/context.py +0 -0
  42. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/cli/debug.py +0 -0
  43. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/cli/install.py +0 -0
  44. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/cli/output.py +0 -0
  45. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/cli/project.py +0 -0
  46. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/cli/tool.py +0 -0
  47. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/cli/update.py +0 -0
  48. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/client.py +0 -0
  49. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/config.py +0 -0
  50. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/logging.py +0 -0
  51. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/operations/__init__.py +0 -0
  52. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/operations/bootstrap.py +0 -0
  53. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/operations/config.py +0 -0
  54. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/operations/install.py +0 -0
  55. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/operations/project.py +0 -0
  56. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/operations/schema.py +0 -0
  57. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/operations/tool.py +0 -0
  58. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/operations/update.py +0 -0
  59. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/protocol.py +0 -0
  60. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/py.typed +0 -0
  61. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/resolution.py +0 -0
  62. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/schema.py +0 -0
  63. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/startup.py +0 -0
  64. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/subprocess_patch.py +0 -0
  65. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/synodic_client/updater.py +0 -0
  66. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/__init__.py +0 -0
  67. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/conftest.py +0 -0
  68. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/__init__.py +0 -0
  69. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/operations/__init__.py +0 -0
  70. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/operations/test_config.py +0 -0
  71. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/operations/test_install.py +0 -0
  72. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/operations/test_install_plan.py +0 -0
  73. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/operations/test_project.py +0 -0
  74. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/operations/test_tool.py +0 -0
  75. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/operations/test_update.py +0 -0
  76. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/qt/__init__.py +0 -0
  77. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/qt/conftest.py +0 -0
  78. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/qt/test_action_card.py +0 -0
  79. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/qt/test_gather_packages.py +0 -0
  80. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/qt/test_install_preview.py +0 -0
  81. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/qt/test_log_panel.py +0 -0
  82. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/qt/test_logging.py +0 -0
  83. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/qt/test_package_state.py +0 -0
  84. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/qt/test_preview_model.py +0 -0
  85. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/qt/test_settings.py +0 -0
  86. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/qt/test_sidebar.py +0 -0
  87. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/qt/test_tray_window_show.py +0 -0
  88. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/qt/test_update_banner.py +0 -0
  89. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/qt/test_update_controller.py +0 -0
  90. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/qt/test_update_feedback.py +0 -0
  91. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/test_bootstrap.py +0 -0
  92. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/test_cli.py +0 -0
  93. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/test_client_updater.py +0 -0
  94. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/test_client_version.py +0 -0
  95. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/test_config.py +0 -0
  96. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/test_examples.py +0 -0
  97. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/test_init.py +0 -0
  98. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/test_install.py +0 -0
  99. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/test_resolution.py +0 -0
  100. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/test_updater.py +0 -0
  101. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/test_uri.py +0 -0
  102. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/test_workers.py +0 -0
  103. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/windows/__init__.py +0 -0
  104. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/windows/conftest.py +0 -0
  105. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/windows/test_protocol.py +0 -0
  106. {synodic_client-0.0.1.dev83 → synodic_client-0.0.1.dev84}/tests/unit/windows/test_startup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: synodic_client
3
- Version: 0.0.1.dev83
3
+ Version: 0.0.1.dev84
4
4
  Author-Email: Synodic Software <contact@synodic.software>
5
5
  License: LGPL-3.0-or-later
6
6
  Project-URL: homepage, https://github.com/synodic/synodic-client
@@ -15,7 +15,7 @@ dependencies = [
15
15
  "velopack>=0.0.1535.dev45597",
16
16
  "typer>=0.24.1",
17
17
  ]
18
- version = "0.0.1.dev83"
18
+ version = "0.0.1.dev84"
19
19
 
20
20
  [project.license]
21
21
  text = "LGPL-3.0-or-later"
@@ -4,7 +4,7 @@ import logging
4
4
  from typing import TYPE_CHECKING
5
5
 
6
6
  from PySide6.QtCore import QTimer
7
- from PySide6.QtGui import QAction, QCursor
7
+ from PySide6.QtGui import QAction
8
8
  from PySide6.QtWidgets import (
9
9
  QApplication,
10
10
  QMainWindow,
@@ -54,13 +54,15 @@ class TrayScreen:
54
54
  self.tray.setIcon(app_icon())
55
55
  self.tray.activated.connect(self._on_tray_activated)
56
56
 
57
- # At early Windows login the notification area may not be ready.
58
- # Retry with back-off so the icon eventually appears.
59
- self._tray_retry_count = 0
60
- self._tray_retry_timer: QTimer | None = None
61
- self._show_tray_icon()
57
+ self._build_menu()
58
+ self.tray.setContextMenu(self._menu)
62
59
 
63
- self._build_menu(app, window)
60
+ # At early Windows login the notification area may not be ready.
61
+ # Poll until the tray is available, then show the icon.
62
+ self._tray_poll = QTimer()
63
+ self._tray_poll.setInterval(2000)
64
+ self._tray_poll.timeout.connect(self._try_show_tray_icon)
65
+ self._try_show_tray_icon()
64
66
 
65
67
  # Settings window (created once, shown/hidden on demand)
66
68
  self._settings_window = SettingsWindow(
@@ -109,13 +111,13 @@ class TrayScreen:
109
111
  # Connect ToolsView signals - deferred because ToolsView is created lazily
110
112
  window.tools_view_created.connect(self._tool_orchestrator.connect_tools_view)
111
113
 
112
- def _build_menu(self, app: QApplication, window: MainWindow) -> None:
114
+ def _build_menu(self) -> None:
113
115
  """Build the tray context menu."""
114
116
  self._menu = QMenu()
115
117
 
116
118
  self._open_action = QAction('Open', self._menu)
117
119
  self._menu.addAction(self._open_action)
118
- self._open_action.triggered.connect(window.show)
120
+ self._open_action.triggered.connect(self._show_window)
119
121
 
120
122
  self._menu.addSeparator()
121
123
 
@@ -131,55 +133,27 @@ class TrayScreen:
131
133
 
132
134
  self._menu.aboutToShow.connect(lambda: logger.debug('Tray context menu about to show'))
133
135
 
134
- # Maximum number of tray-visibility retries at startup.
135
- _TRAY_MAX_RETRIES = 5
136
- # Delay between retries in milliseconds.
137
- _TRAY_RETRY_DELAY_MS = 2000
138
-
139
- def _show_tray_icon(self) -> None:
140
- """Show the tray icon, retrying if the system tray is not ready."""
136
+ def _try_show_tray_icon(self) -> None:
137
+ """Show the tray icon once the system tray is available."""
141
138
  if QSystemTrayIcon.isSystemTrayAvailable():
139
+ self._tray_poll.stop()
142
140
  self.tray.setVisible(True)
143
141
  logger.debug('System tray icon shown')
144
- return
145
-
146
- if self._tray_retry_count < self._TRAY_MAX_RETRIES:
147
- self._tray_retry_count += 1
148
- logger.warning(
149
- 'System tray not available, retrying (%d/%d)',
150
- self._tray_retry_count,
151
- self._TRAY_MAX_RETRIES,
152
- )
153
- self._tray_retry_timer = QTimer()
154
- self._tray_retry_timer.setSingleShot(True)
155
- self._tray_retry_timer.timeout.connect(self._show_tray_icon)
156
- self._tray_retry_timer.start(self._TRAY_RETRY_DELAY_MS)
157
- else:
158
- # Exhausted retries — show anyway as a best-effort fallback.
159
- logger.warning(
160
- 'System tray still not available after %d retries, forcing visibility',
161
- self._TRAY_MAX_RETRIES,
162
- )
163
- self.tray.setVisible(True)
164
-
165
- # Delay before showing the context menu, in milliseconds.
166
- # Absorbs residual mouse-up events from touchpad two-finger taps
167
- # that would otherwise land on a menu item (typically "Quit").
168
- _MENU_POPUP_DELAY_MS = 80
142
+ elif not self._tray_poll.isActive():
143
+ logger.warning('System tray not available, polling until ready')
144
+ self._tray_poll.start()
169
145
 
170
146
  def _on_tray_activated(self, reason: QSystemTrayIcon.ActivationReason) -> None:
171
147
  """Handle tray icon activation."""
172
148
  logger.debug('Tray activated: reason=%s', reason.name)
173
149
  if reason == QSystemTrayIcon.ActivationReason.DoubleClick:
174
- self._window.show()
175
- self._window.raise_()
176
- self._window.activateWindow()
177
- elif reason == QSystemTrayIcon.ActivationReason.Context:
178
- QTimer.singleShot(self._MENU_POPUP_DELAY_MS, self._show_tray_menu)
150
+ self._show_window()
179
151
 
180
- def _show_tray_menu(self) -> None:
181
- """Show the tray context menu at the current cursor position."""
182
- self._menu.exec(QCursor.pos())
152
+ def _show_window(self) -> None:
153
+ """Show, raise, and focus the main window."""
154
+ self._window.show()
155
+ self._window.raise_()
156
+ self._window.activateWindow()
183
157
 
184
158
  def _on_quit_triggered(self) -> None:
185
159
  """Handle the Quit menu action."""
@@ -189,6 +163,8 @@ class TrayScreen:
189
163
  def _show_settings(self) -> None:
190
164
  """Show the settings window."""
191
165
  self._settings_window.show()
166
+ self._settings_window.raise_()
167
+ self._settings_window.activateWindow()
192
168
 
193
169
  @staticmethod
194
170
  def _is_user_active() -> bool:
@@ -222,6 +198,7 @@ class TrayScreen:
222
198
 
223
199
  def shutdown(self) -> None:
224
200
  """Stop all timers and cancel in-flight tasks for a clean exit."""
201
+ self._tray_poll.stop()
225
202
  self._update_controller.shutdown()
226
203
  self._tool_orchestrator.shutdown()
227
204
  logger.info('TrayScreen shut down')