synodic-client 0.0.1.dev25__tar.gz → 0.0.1.dev27__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 (57) hide show
  1. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/PKG-INFO +4 -3
  2. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/pyproject.toml +11 -5
  3. synodic_client-0.0.1.dev27/synodic_client/_version.py +1 -0
  4. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/synodic_client/application/bootstrap.py +8 -0
  5. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/synodic_client/application/icon.py +0 -2
  6. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/synodic_client/application/qt.py +22 -7
  7. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/synodic_client/application/screen/__init__.py +4 -1
  8. synodic_client-0.0.1.dev27/synodic_client/application/screen/action_card.py +789 -0
  9. synodic_client-0.0.1.dev27/synodic_client/application/screen/card.py +148 -0
  10. synodic_client-0.0.1.dev27/synodic_client/application/screen/install.py +1271 -0
  11. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/synodic_client/application/screen/log_panel.py +5 -15
  12. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/synodic_client/application/screen/screen.py +274 -145
  13. synodic_client-0.0.1.dev27/synodic_client/application/screen/spinner.py +101 -0
  14. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/synodic_client/application/screen/tray.py +42 -8
  15. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/synodic_client/application/theme.py +133 -2
  16. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/synodic_client/client.py +1 -0
  17. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/synodic_client/config.py +18 -0
  18. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/synodic_client/resolution.py +16 -0
  19. synodic_client-0.0.1.dev27/synodic_client/startup.py +91 -0
  20. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/synodic_client/updater.py +10 -1
  21. synodic_client-0.0.1.dev27/tests/unit/qt/test_action_card.py +928 -0
  22. synodic_client-0.0.1.dev27/tests/unit/qt/test_install_preview.py +881 -0
  23. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/tests/unit/qt/test_log_panel.py +1 -1
  24. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/tests/unit/test_config.py +24 -0
  25. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/tests/unit/test_resolution.py +23 -0
  26. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/tests/unit/test_updater.py +0 -103
  27. synodic_client-0.0.1.dev27/tests/unit/windows/test_startup.py +190 -0
  28. synodic_client-0.0.1.dev25/synodic_client/_version.py +0 -1
  29. synodic_client-0.0.1.dev25/synodic_client/application/screen/install.py +0 -914
  30. synodic_client-0.0.1.dev25/tests/unit/qt/test_install_preview.py +0 -485
  31. synodic_client-0.0.1.dev25/tests/unit/test_install_preview.py +0 -491
  32. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/LICENSE.md +0 -0
  33. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/README.md +0 -0
  34. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/synodic_client/__init__.py +0 -0
  35. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/synodic_client/__main__.py +0 -0
  36. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/synodic_client/application/__init__.py +0 -0
  37. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/synodic_client/application/instance.py +0 -0
  38. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/synodic_client/application/uri.py +0 -0
  39. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/synodic_client/cli.py +0 -0
  40. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/synodic_client/logging.py +0 -0
  41. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/synodic_client/protocol.py +0 -0
  42. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/synodic_client/py.typed +0 -0
  43. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/tests/__init__.py +0 -0
  44. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/tests/conftest.py +0 -0
  45. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/tests/unit/__init__.py +0 -0
  46. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/tests/unit/qt/__init__.py +0 -0
  47. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/tests/unit/qt/conftest.py +0 -0
  48. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/tests/unit/qt/test_logging.py +0 -0
  49. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/tests/unit/test_cli.py +0 -0
  50. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/tests/unit/test_client_updater.py +0 -0
  51. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/tests/unit/test_client_version.py +0 -0
  52. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/tests/unit/test_examples.py +0 -0
  53. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/tests/unit/test_install.py +0 -0
  54. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/tests/unit/test_uri.py +0 -0
  55. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/tests/unit/windows/__init__.py +0 -0
  56. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/tests/unit/windows/conftest.py +0 -0
  57. {synodic_client-0.0.1.dev25 → synodic_client-0.0.1.dev27}/tests/unit/windows/test_protocol.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: synodic_client
3
- Version: 0.0.1.dev25
3
+ Version: 0.0.1.dev27
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
@@ -8,9 +8,10 @@ Project-URL: repository, https://github.com/synodic/synodic-client
8
8
  Requires-Python: <3.15,>=3.14
9
9
  Requires-Dist: pyside6>=6.10.2
10
10
  Requires-Dist: packaging>=26.0
11
- Requires-Dist: porringer>=0.2.1.dev20
11
+ Requires-Dist: porringer>=0.2.1.dev49
12
+ Requires-Dist: qasync>=0.28.0
12
13
  Requires-Dist: velopack>=0.0.1369.dev7516
13
- Requires-Dist: typer>=0.23.1
14
+ Requires-Dist: typer>=0.24.0
14
15
  Description-Content-Type: text/markdown
15
16
 
16
17
  # Synodic Client
@@ -10,11 +10,12 @@ requires-python = ">=3.14, <3.15"
10
10
  dependencies = [
11
11
  "pyside6>=6.10.2",
12
12
  "packaging>=26.0",
13
- "porringer>=0.2.1.dev20",
13
+ "porringer>=0.2.1.dev49",
14
+ "qasync>=0.28.0",
14
15
  "velopack>=0.0.1369.dev7516",
15
- "typer>=0.23.1",
16
+ "typer>=0.24.0",
16
17
  ]
17
- version = "0.0.1.dev25"
18
+ version = "0.0.1.dev27"
18
19
 
19
20
  [project.license]
20
21
  text = "LGPL-3.0-or-later"
@@ -34,8 +35,8 @@ build = [
34
35
  "pyinstaller>=6.19.0",
35
36
  ]
36
37
  lint = [
37
- "ruff>=0.15.1",
38
- "pyrefly>=0.52.0",
38
+ "ruff>=0.15.2",
39
+ "pyrefly>=0.53.0",
39
40
  ]
40
41
  test = [
41
42
  "pytest>=9.0.2",
@@ -127,6 +128,11 @@ data = [
127
128
  { path = "data/**/*", relative-to = "data/" },
128
129
  ]
129
130
 
131
+ [tool.pdm.dev-dependencies]
132
+ dev = [
133
+ "-e file:///d:/Projects/Synodic/porringer#egg=porringer",
134
+ ]
135
+
130
136
  [build-system]
131
137
  build-backend = "pdm.backend"
132
138
  requires = [
@@ -0,0 +1 @@
1
+ __version__ = '0.0.1.dev27'
@@ -18,6 +18,8 @@ import sys
18
18
  from synodic_client.config import set_dev_mode
19
19
  from synodic_client.logging import configure_logging
20
20
  from synodic_client.protocol import register_protocol
21
+ from synodic_client.resolution import resolve_auto_start, resolve_config
22
+ from synodic_client.startup import register_startup, remove_startup
21
23
  from synodic_client.updater import initialize_velopack
22
24
 
23
25
  _PROTOCOL_SCHEME = 'synodic'
@@ -32,6 +34,12 @@ initialize_velopack()
32
34
  if not _dev_mode:
33
35
  register_protocol(sys.executable)
34
36
 
37
+ _config = resolve_config()
38
+ if resolve_auto_start(_config):
39
+ register_startup(sys.executable)
40
+ else:
41
+ remove_startup()
42
+
35
43
  # Heavy imports happen here — PySide6, porringer, etc.
36
44
  from synodic_client.application.qt import application # noqa: E402
37
45
 
@@ -4,8 +4,6 @@ Loads the icon once from package resources and caches the ``QIcon``
4
4
  so every caller shares the same instance.
5
5
  """
6
6
 
7
- from __future__ import annotations
8
-
9
7
  from PySide6.QtGui import QIcon, QPixmap
10
8
 
11
9
  from synodic_client.client import Client
@@ -1,5 +1,6 @@
1
1
  """GUI entry point for the Synodic Client application."""
2
2
 
3
+ import asyncio
3
4
  import ctypes
4
5
  import logging
5
6
  import signal
@@ -7,6 +8,7 @@ import sys
7
8
  import types
8
9
  from collections.abc import Callable
9
10
 
11
+ import qasync
10
12
  from porringer.api import API
11
13
  from porringer.schema import LocalConfiguration
12
14
  from PySide6.QtCore import Qt, QTimer
@@ -22,7 +24,8 @@ from synodic_client.client import Client
22
24
  from synodic_client.config import GlobalConfiguration, set_dev_mode
23
25
  from synodic_client.logging import configure_logging
24
26
  from synodic_client.protocol import register_protocol
25
- from synodic_client.resolution import resolve_config, resolve_update_config
27
+ from synodic_client.resolution import resolve_auto_start, resolve_config, resolve_update_config
28
+ from synodic_client.startup import register_startup, remove_startup
26
29
  from synodic_client.updater import initialize_velopack
27
30
 
28
31
 
@@ -48,8 +51,6 @@ def _init_services(logger: logging.Logger) -> tuple[Client, API, GlobalConfigura
48
51
  update_config.repo_url,
49
52
  )
50
53
 
51
- porringer.plugin.list()
52
-
53
54
  return client, porringer, config
54
55
 
55
56
 
@@ -135,6 +136,12 @@ def application(*, uri: str | None = None, dev_mode: bool = False) -> None:
135
136
  initialize_velopack()
136
137
  register_protocol(sys.executable)
137
138
 
139
+ startup_config = resolve_config()
140
+ if resolve_auto_start(startup_config):
141
+ register_startup(sys.executable)
142
+ else:
143
+ remove_startup()
144
+
138
145
  if uri:
139
146
  logger.info('Received URI: %s', uri)
140
147
 
@@ -142,6 +149,9 @@ def application(*, uri: str | None = None, dev_mode: bool = False) -> None:
142
149
 
143
150
  app = _init_app()
144
151
 
152
+ loop = qasync.QEventLoop(app)
153
+ asyncio.set_event_loop(loop)
154
+
145
155
  instance = SingleInstance(app)
146
156
  if instance.try_send_to_existing(uri or ''):
147
157
  logger.info('Another instance is already running, exiting')
@@ -156,7 +166,11 @@ def application(*, uri: str | None = None, dev_mode: bool = False) -> None:
156
166
 
157
167
  def _handle_install_uri(manifest_url: str) -> None:
158
168
  logger.info('Opening install preview for: %s', manifest_url)
159
- window = InstallPreviewWindow(porringer, manifest_url)
169
+ window = InstallPreviewWindow(
170
+ porringer,
171
+ manifest_url,
172
+ config=config,
173
+ )
160
174
  _install_windows.append(window)
161
175
  window.show()
162
176
  window.raise_()
@@ -168,9 +182,10 @@ def application(*, uri: str | None = None, dev_mode: bool = False) -> None:
168
182
  if uri:
169
183
  _process_uri(uri, _handle_install_uri)
170
184
 
171
- # sys.exit ensures proper cleanup and exit code propagation
172
- # Leading underscore indicates references kept alive intentionally until exec() returns
173
- sys.exit(app.exec())
185
+ # qasync integrates the asyncio event loop with Qt's event loop,
186
+ # enabling async/await usage in the GUI layer without dedicated threads.
187
+ with loop:
188
+ loop.run_forever()
174
189
 
175
190
 
176
191
  _PROTOCOL_SCHEME = 'synodic'
@@ -6,7 +6,8 @@ execution log panel live here to avoid circular imports.
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
- from porringer.schema import PluginKind, SkipReason
9
+ from porringer.schema import SkipReason
10
+ from porringer.schema.plugin import PluginKind
10
11
 
11
12
  ACTION_KIND_LABELS: dict[PluginKind | None, str] = {
12
13
  PluginKind.PACKAGE: 'Package',
@@ -42,7 +43,9 @@ def plugin_kind_group_label(kind: PluginKind) -> str:
42
43
 
43
44
  SKIP_REASON_LABELS: dict[SkipReason, str] = {
44
45
  SkipReason.ALREADY_INSTALLED: 'Already installed',
46
+ SkipReason.ALREADY_LATEST: 'Already latest',
45
47
  SkipReason.NO_PROJECT_DIRECTORY: 'No project directory',
48
+ SkipReason.UPDATE_AVAILABLE: 'Update available',
46
49
  }
47
50
 
48
51