susops 3.0.0rc3.dev6__tar.gz → 3.0.0rc3.dev8__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 (96) hide show
  1. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/PKG-INFO +1 -1
  2. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/pyproject.toml +7 -2
  3. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/core/rpc_server.py +1 -0
  4. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/facade.py +9 -0
  5. susops-3.0.0rc3.dev8/src/susops/tui/app.tcss +63 -0
  6. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/tui/screens/dashboard.py +24 -4
  7. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops.egg-info/PKG-INFO +1 -1
  8. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops.egg-info/SOURCES.txt +1 -0
  9. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/LICENSE +0 -0
  10. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/README.md +0 -0
  11. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/setup.cfg +0 -0
  12. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/__init__.py +0 -0
  13. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icon.png +0 -0
  14. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/colored_glasses/dark/error.svg +0 -0
  15. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/colored_glasses/dark/running.svg +0 -0
  16. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/colored_glasses/dark/stopped.svg +0 -0
  17. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/colored_glasses/dark/stopped_partially.svg +0 -0
  18. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/colored_glasses/light/error.svg +0 -0
  19. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/colored_glasses/light/running.svg +0 -0
  20. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/colored_glasses/light/stopped.svg +0 -0
  21. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/colored_glasses/light/stopped_partially.svg +0 -0
  22. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/colored_s/dark/error.svg +0 -0
  23. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/colored_s/dark/running.svg +0 -0
  24. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/colored_s/dark/stopped.svg +0 -0
  25. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/colored_s/dark/stopped_partially.svg +0 -0
  26. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/colored_s/light/error.svg +0 -0
  27. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/colored_s/light/running.svg +0 -0
  28. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/colored_s/light/stopped.svg +0 -0
  29. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/colored_s/light/stopped_partially.svg +0 -0
  30. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/gear/dark/error.svg +0 -0
  31. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/gear/dark/running.svg +0 -0
  32. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/gear/dark/stopped.svg +0 -0
  33. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/gear/dark/stopped_partially.svg +0 -0
  34. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/gear/light/error.svg +0 -0
  35. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/gear/light/running.svg +0 -0
  36. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/gear/light/stopped.svg +0 -0
  37. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/gear/light/stopped_partially.svg +0 -0
  38. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/status/error.svg +0 -0
  39. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/status/running.svg +0 -0
  40. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/status/stopped.svg +0 -0
  41. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/assets/icons/status/stopped_partially.svg +0 -0
  42. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/client.py +0 -0
  43. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/core/__init__.py +0 -0
  44. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/core/browsers.py +0 -0
  45. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/core/config.py +0 -0
  46. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/core/log_style.py +0 -0
  47. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/core/pac.py +0 -0
  48. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/core/ports.py +0 -0
  49. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/core/process.py +0 -0
  50. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/core/rpc_protocol.py +0 -0
  51. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/core/services_daemon.py +0 -0
  52. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/core/share.py +0 -0
  53. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/core/socat.py +0 -0
  54. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/core/ssh.py +0 -0
  55. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/core/ssh_config.py +0 -0
  56. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/core/status.py +0 -0
  57. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/core/types.py +0 -0
  58. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/tray/__init__.py +0 -0
  59. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/tray/base.py +0 -0
  60. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/tray/linux.py +0 -0
  61. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/tray/mac.py +0 -0
  62. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/tui/__init__.py +0 -0
  63. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/tui/__main__.py +0 -0
  64. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/tui/app.py +0 -0
  65. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/tui/cli.py +0 -0
  66. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/tui/screens/__init__.py +0 -0
  67. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/tui/screens/connections.py +0 -0
  68. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/tui/screens/shares.py +0 -0
  69. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/tui/widgets/__init__.py +0 -0
  70. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/tui/widgets/connection_card.py +0 -0
  71. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops/version.py +0 -0
  72. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops.egg-info/dependency_links.txt +0 -0
  73. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops.egg-info/entry_points.txt +0 -0
  74. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops.egg-info/requires.txt +0 -0
  75. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/src/susops.egg-info/top_level.txt +0 -0
  76. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/tests/test_bw_totals.py +0 -0
  77. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/tests/test_cli.py +0 -0
  78. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/tests/test_client.py +0 -0
  79. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/tests/test_config.py +0 -0
  80. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/tests/test_conftest_smoke.py +0 -0
  81. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/tests/test_facade.py +0 -0
  82. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/tests/test_openapi.py +0 -0
  83. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/tests/test_pac.py +0 -0
  84. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/tests/test_packaging.py +0 -0
  85. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/tests/test_process.py +0 -0
  86. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/tests/test_rpc_protocol.py +0 -0
  87. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/tests/test_rpc_server.py +0 -0
  88. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/tests/test_scripts.py +0 -0
  89. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/tests/test_services_daemon.py +0 -0
  90. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/tests/test_share.py +0 -0
  91. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/tests/test_share_aiohttp.py +0 -0
  92. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/tests/test_socat.py +0 -0
  93. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/tests/test_ssh.py +0 -0
  94. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/tests/test_status.py +0 -0
  95. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/tests/test_update_homebrew_sha.py +0 -0
  96. {susops-3.0.0rc3.dev6 → susops-3.0.0rc3.dev8}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: susops
3
- Version: 3.0.0rc3.dev6
3
+ Version: 3.0.0rc3.dev8
4
4
  Summary: SusOps — SSH SOCKS5 proxy manager with PAC server, Textual TUI, and system tray apps
5
5
  License: MIT
6
6
  Requires-Python: >=3.11
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "susops"
7
- version = "3.0.0-rc3.dev6"
7
+ version = "3.0.0-rc3.dev8"
8
8
  description = "SusOps — SSH SOCKS5 proxy manager with PAC server, Textual TUI, and system tray apps"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -51,7 +51,12 @@ susops-tray = "susops.tray:main"
51
51
  where = ["src"]
52
52
 
53
53
  [tool.setuptools.package-data]
54
- susops = ["assets/*.png", "assets/icons/**/*.svg", "assets/icons/**/*.png"]
54
+ susops = [
55
+ "assets/*.png",
56
+ "assets/icons/**/*.svg",
57
+ "assets/icons/**/*.png",
58
+ "tui/**/*.tcss",
59
+ ]
55
60
 
56
61
  [tool.pytest.ini_options]
57
62
  testpaths = ["tests"]
@@ -45,6 +45,7 @@ _ALLOWED_METHODS: set[str] = {
45
45
  "get_pac_url", "get_status_url",
46
46
  # Bandwidth
47
47
  "get_bandwidth", "get_bandwidth_totals", "get_bandwidth_global",
48
+ "get_bandwidth_history",
48
49
  # Reconnect introspection
49
50
  "reconnect_monitor_info",
50
51
  # Process introspection
@@ -376,6 +376,11 @@ class _BandwidthSampler:
376
376
  with self._lock:
377
377
  return self._totals.get(tag, (0.0, 0.0))
378
378
 
379
+ def get_history(self, tag: str) -> list[list[float]]:
380
+ """Return the persisted (rx_bps, tx_bps) samples for *tag* (newest last)."""
381
+ with self._lock:
382
+ return list(self._history.get(tag, []))
383
+
379
384
  def reset_totals(self, tag: str | None = None) -> None:
380
385
  """Reset cumulative counters. Pass tag=None to reset all."""
381
386
  with self._lock:
@@ -2143,6 +2148,10 @@ class SusOpsManager:
2143
2148
  """Return cumulative (rx_bytes, tx_bytes) since last start. Resets on stop."""
2144
2149
  return self._bw_sampler.get_totals(tag)
2145
2150
 
2151
+ def get_bandwidth_history(self, tag: str) -> list[list[float]]:
2152
+ """Return persisted [rx_bps, tx_bps] samples for *tag* (oldest → newest)."""
2153
+ return self._bw_sampler.get_history(tag)
2154
+
2146
2155
  def get_bandwidth_global(self) -> tuple[float, float]:
2147
2156
  """Return (rx_bps, tx_bps) summed across every connection."""
2148
2157
  rx_total = 0.0
@@ -0,0 +1,63 @@
1
+ /* SusOps TUI — global theme */
2
+
3
+ /* ── Base ── */
4
+ Screen { background: $background; }
5
+ Header { background: $primary-darken-3; }
6
+ Footer { background: $surface-darken-2; }
7
+ .footer-row { dock: bottom; height: 1; }
8
+ .footer-row Footer { dock: none; width: 1fr; height: 1; }
9
+ .footer-version { width: auto; padding: 0 1; background: $surface-darken-2; color: $text-muted; content-align: center middle; }
10
+ .footer-edit-config { width: auto; padding: 0 1; background: $surface-darken-2; color: $accent; content-align: center middle; }
11
+ .footer-logo { width: auto; padding: 0 1; background: $surface-darken-2; content-align: center middle; }
12
+
13
+ /* ── Dashboard ── */
14
+ #conn-list > ListItem { height: 1; padding: 0 1; }
15
+
16
+ /* ── Connection editor ── */
17
+ .tab-actions { height: 3; layout: horizontal; padding: 0 1; background: $surface-darken-1; }
18
+ .tab-actions Button { margin-right: 1; }
19
+ #detail-preview { height: 6; border: round $primary-darken-1; border-title-align: left; padding: 0 1; margin: 0 1 1 1; color: $text-muted; }
20
+
21
+ /* ── Share ── */
22
+ #share-toolbar { height: 3; layout: horizontal; padding: 0 1; background: $surface-darken-1; }
23
+ #share-toolbar Button { margin-right: 1; }
24
+ #share-list-panel { width: 35; background: $surface-darken-1; border-right: solid $primary-darken-2; }
25
+ #share-list { height: 1fr; margin: 1; border: round $primary-darken-1; border-title-align: left; }
26
+ #share-list > ListItem { height: 1; padding: 0 1; }
27
+ #share-detail { width: 1fr; padding: 1 2; height: 1fr; }
28
+ #share-status { height: 1; padding: 0 1; background: $surface-darken-2; color: $text-muted; }
29
+
30
+ /* ── Logs ── */
31
+ #log-toolbar { height: 3; layout: horizontal; padding: 0 1; background: $surface-darken-1; align: left middle; }
32
+ #log-toolbar Label { margin-right: 1; height: 1; }
33
+ #log-toolbar Switch { margin-right: 2; }
34
+ #log-toolbar Button { margin-left: 2; }
35
+ #log-view { height: 1fr; border: round $primary-darken-1; border-title-align: left; margin: 0 1 1 1; }
36
+
37
+ /* ── Config ── */
38
+ #config-toolbar { height: 3; layout: horizontal; padding: 0 1; background: $surface-darken-1; }
39
+ #config-toolbar Button { margin-right: 1; }
40
+ #config-area { height: 1fr; border: round $primary-darken-1; border-title-align: left; margin: 0 1 1 1; }
41
+
42
+ /* ── PAC viewer ── */
43
+ #pac-area { height: 1fr; border: round $primary-darken-1; border-title-align: left; margin: 0 1 1 1; }
44
+
45
+ /* ── Modals ── */
46
+ ModalScreen { align: center middle; background: rgba(0, 0, 0, 0.75); }
47
+ .modal-dialog { width: 76; height: auto; border: round $primary; padding: 1 2; background: $surface; }
48
+ .modal-dialog Label { margin-top: 1; }
49
+ .modal-dialog > Label:first-child { margin-top: 0; }
50
+ .modal-dialog Input { margin-bottom: 1; }
51
+ .modal-dialog Select { margin-bottom: 1; }
52
+ .modal-dialog .modal-error { color: $error; margin-top: 0; }
53
+ .modal-dialog .modal-hint { margin-top: 0; }
54
+ .modal-btn-row { layout: horizontal; height: auto; margin-top: 1; }
55
+ .modal-btn-row Button { margin-right: 1; }
56
+ /* 2-column form rows */
57
+ .modal-form-row { height: auto; margin-top: 1; }
58
+ .modal-proto-row { height: auto; }
59
+ .modal-field { width: 1fr; height: auto; layout: vertical; padding-right: 1; }
60
+ .modal-field:last-child { padding-right: 0; }
61
+ .modal-field Label { margin-top: 0; }
62
+ .modal-field Input { margin-bottom: 0; }
63
+ .modal-field Select { margin-bottom: 0; }
@@ -290,11 +290,19 @@ class DashboardScreen(Screen):
290
290
  bw: dict[str, tuple[float, float]] = {}
291
291
  bw_totals: dict[str, tuple[float, float]] = {}
292
292
  uptimes: dict[str, float | None] = {}
293
+ bw_history_seed: dict[str, list] = {}
293
294
  for cs in result.connection_statuses:
294
295
  extras[cs.tag] = mgr.get_process_info(cs.tag)
295
296
  bw[cs.tag] = mgr.get_bandwidth(cs.tag)
296
297
  bw_totals[cs.tag] = mgr.get_bandwidth_totals(cs.tag)
297
298
  uptimes[cs.tag] = mgr.get_uptime(cs.tag)
299
+ # Only fetch persisted history for tags we haven't seeded
300
+ # yet — saves an RPC per tick once the chart is rolling.
301
+ if cs.tag not in self._rx_history:
302
+ try:
303
+ bw_history_seed[cs.tag] = mgr.get_bandwidth_history(cs.tag) or []
304
+ except Exception:
305
+ bw_history_seed[cs.tag] = []
298
306
 
299
307
  shares = mgr.list_shares()
300
308
  config = mgr.list_config()
@@ -312,7 +320,8 @@ class DashboardScreen(Screen):
312
320
  )
313
321
  return
314
322
  self.app.call_from_thread(
315
- self._apply_status, result, extras, bw, bw_totals, uptimes, shares, config, reconnect
323
+ self._apply_status, result, extras, bw, bw_totals, uptimes, shares,
324
+ config, reconnect, bw_history_seed,
316
325
  )
317
326
 
318
327
  def _apply_status(
@@ -325,7 +334,9 @@ class DashboardScreen(Screen):
325
334
  shares: list,
326
335
  config,
327
336
  reconnect: dict,
337
+ bw_history_seed: dict | None = None,
328
338
  ) -> None:
339
+ bw_history_seed = bw_history_seed or {}
329
340
  self._last_config = config
330
341
  self._last_shares = shares
331
342
 
@@ -352,13 +363,22 @@ class DashboardScreen(Screen):
352
363
  }
353
364
  self._conn_data = new_conn_data
354
365
 
355
- # Update rolling bandwidth history (60 samples)
366
+ # Update rolling bandwidth history (60 samples). Seed from the
367
+ # daemon's persisted history (pre-fetched by refresh_status) on
368
+ # first encounter so reopening the TUI doesn't reset the chart to
369
+ # flatlined zeros.
356
370
  for cs in result.connection_statuses:
357
371
  tag = cs.tag
358
372
  rx, tx = bw.get(tag, (0.0, 0.0))
359
373
  if tag not in self._rx_history:
360
- self._rx_history[tag] = deque([0.0] * 60, maxlen=60)
361
- self._tx_history[tag] = deque([0.0] * 60, maxlen=60)
374
+ rx_seed = [0.0] * 60
375
+ tx_seed = [0.0] * 60
376
+ persisted = (bw_history_seed.get(tag) or [])[-60:]
377
+ if persisted:
378
+ rx_seed[-len(persisted):] = [s[0] for s in persisted]
379
+ tx_seed[-len(persisted):] = [s[1] for s in persisted]
380
+ self._rx_history[tag] = deque(rx_seed, maxlen=60)
381
+ self._tx_history[tag] = deque(tx_seed, maxlen=60)
362
382
  self._rx_history[tag].append(rx)
363
383
  self._tx_history[tag].append(tx)
364
384
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: susops
3
- Version: 3.0.0rc3.dev6
3
+ Version: 3.0.0rc3.dev8
4
4
  Summary: SusOps — SSH SOCKS5 proxy manager with PAC server, Textual TUI, and system tray apps
5
5
  License: MIT
6
6
  Requires-Python: >=3.11
@@ -63,6 +63,7 @@ src/susops/tray/mac.py
63
63
  src/susops/tui/__init__.py
64
64
  src/susops/tui/__main__.py
65
65
  src/susops/tui/app.py
66
+ src/susops/tui/app.tcss
66
67
  src/susops/tui/cli.py
67
68
  src/susops/tui/screens/__init__.py
68
69
  src/susops/tui/screens/connections.py
File without changes
File without changes
File without changes