data-sourcerer 0.3.0__py3-none-any.whl → 0.5.0__py3-none-any.whl

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 (55) hide show
  1. {data_sourcerer-0.3.0.dist-info → data_sourcerer-0.5.0.dist-info}/METADATA +11 -8
  2. {data_sourcerer-0.3.0.dist-info → data_sourcerer-0.5.0.dist-info}/RECORD +55 -34
  3. sourcerer/__init__.py +3 -1
  4. sourcerer/domain/package_meta/__init__.py +0 -0
  5. sourcerer/domain/package_meta/entities.py +9 -0
  6. sourcerer/domain/package_meta/services.py +9 -0
  7. sourcerer/domain/settings/__init__.py +0 -0
  8. sourcerer/domain/settings/entities.py +11 -0
  9. sourcerer/domain/settings/repositories.py +20 -0
  10. sourcerer/domain/settings/services.py +19 -0
  11. sourcerer/domain/storage_provider/entities.py +1 -1
  12. sourcerer/infrastructure/db/models.py +23 -0
  13. sourcerer/infrastructure/package_meta/__init__.py +0 -0
  14. sourcerer/infrastructure/package_meta/services.py +26 -0
  15. sourcerer/infrastructure/settings/__init__.py +0 -0
  16. sourcerer/infrastructure/settings/repositories.py +59 -0
  17. sourcerer/infrastructure/settings/services.py +16 -0
  18. sourcerer/infrastructure/storage_provider/services/azure.py +1 -3
  19. sourcerer/infrastructure/storage_provider/services/gcp.py +1 -3
  20. sourcerer/infrastructure/storage_provider/services/s3.py +1 -2
  21. sourcerer/infrastructure/utils.py +1 -0
  22. sourcerer/presentation/di_container.py +13 -0
  23. sourcerer/presentation/screens/about/__init__.py +0 -0
  24. sourcerer/presentation/screens/about/main.py +60 -0
  25. sourcerer/presentation/screens/about/styles.tcss +32 -0
  26. sourcerer/presentation/screens/file_system_finder/__init__.py +0 -0
  27. sourcerer/presentation/screens/file_system_finder/main.py +3 -9
  28. sourcerer/presentation/screens/main/main.py +116 -8
  29. sourcerer/presentation/screens/main/messages/preview_request.py +1 -0
  30. sourcerer/presentation/screens/main/styles.tcss +13 -4
  31. sourcerer/presentation/screens/main/widgets/storage_content.py +10 -3
  32. sourcerer/presentation/screens/main/widgets/storage_list_sidebar.py +102 -18
  33. sourcerer/presentation/screens/preview_content/main.py +202 -15
  34. sourcerer/presentation/screens/preview_content/styles.tcss +39 -4
  35. sourcerer/presentation/screens/preview_content/text_area_style.py +60 -0
  36. sourcerer/presentation/screens/provider_creds_list/main.py +23 -9
  37. sourcerer/presentation/screens/provider_creds_list/styles.tcss +9 -0
  38. sourcerer/presentation/screens/provider_creds_registration/main.py +3 -3
  39. sourcerer/presentation/screens/settings/__init__.py +0 -0
  40. sourcerer/presentation/screens/settings/main.py +70 -0
  41. sourcerer/presentation/screens/settings/styles.tcss +44 -0
  42. sourcerer/presentation/screens/shared/modal_screens.py +37 -0
  43. sourcerer/presentation/screens/shared/widgets/button.py +11 -0
  44. sourcerer/presentation/screens/shared/widgets/labeled_input.py +1 -3
  45. sourcerer/presentation/screens/storage_action_progress/main.py +1 -2
  46. sourcerer/presentation/screens/storages_list/main.py +24 -9
  47. sourcerer/presentation/screens/storages_list/styles.tcss +7 -0
  48. sourcerer/presentation/screens/storages_registration/main.py +3 -3
  49. sourcerer/presentation/settings.py +1 -0
  50. sourcerer/presentation/utils.py +1 -0
  51. sourcerer/settings.py +2 -0
  52. sourcerer/utils.py +19 -1
  53. {data_sourcerer-0.3.0.dist-info → data_sourcerer-0.5.0.dist-info}/WHEEL +0 -0
  54. {data_sourcerer-0.3.0.dist-info → data_sourcerer-0.5.0.dist-info}/entry_points.txt +0 -0
  55. {data_sourcerer-0.3.0.dist-info → data_sourcerer-0.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: data-sourcerer
3
- Version: 0.3.0
3
+ Version: 0.5.0
4
4
  Summary: Sourcerer is a terminal cloud storage navigator.
5
5
  Author-email: Bohdana Kuzmenko <bohdana.kuzmenko.dev@gmail.com>
6
6
  License: MIT
@@ -16,6 +16,7 @@ Requires-Dist: dependency-injector<5.0.0,>=4.43.0
16
16
  Requires-Dist: google-cloud-storage<4.0.0,>=3.1.0
17
17
  Requires-Dist: humanize<5.0.0,>=4.12.1
18
18
  Requires-Dist: msgspec>=0.19.0
19
+ Requires-Dist: packaging>=25.0
19
20
  Requires-Dist: pyopenssl>=22.1.0; sys_platform == 'linux'
20
21
  Requires-Dist: sqlalchemy-utils<1.0.0,>=0.41.2
21
22
  Requires-Dist: sqlalchemy<3.0.0,>=2.0.38
@@ -36,21 +37,23 @@ Description-Content-Type: text/markdown
36
37
 
37
38
  # 🧙‍♂️ Sourcerer
38
39
 
39
- **Sourcerer** is a CLI-based cloud storage explorer that provides a unified interface for developers and DevOps
40
- engineers to view and manage files across multiple cloud providers like
40
+ **Sourcerer** is a CLI-based cloud storage explorer that provides a unified interface for developers and DevOps
41
+ engineers to view and manage files across multiple cloud providers like
41
42
  **GCP Storage**, **Azure Storage**, **AWS S3**, and **S3-compatible services**.
42
43
 
43
44
  > Your terminal. Your storages. Your control.
44
45
 
46
+ [Demo page](https://the-impact-craft.github.io/sourcerer/)
47
+
45
48
  ---
46
49
 
47
50
  ## ✨ Features
48
51
 
49
- - 🔍 Unified file browser for GCP Storage, Azure Storage, AWS S3, and S3-compatible services
50
- - 🧭 Terminal UI (TUI) built with [Textual](https://github.com/Textualize/textual)
51
- - 🗂️ Explore buckets and objects seamlessly
52
- - 🔄 Upload, download, and delete files
53
- - 🔐 Secure credential management via local **SQLite database**
52
+ - 🔍 Unified file browser for GCP Storage, Azure Storage, AWS S3, and S3-compatible services
53
+ - 🧭 Terminal UI (TUI) built with [Textual](https://github.com/Textualize/textual)
54
+ - 🗂️ Explore buckets and objects seamlessly
55
+ - 🔄 Upload, download, and delete files
56
+ - 🔐 Secure credential management via local **SQLite database**
54
57
 
55
58
  ---
56
59
 
@@ -1,6 +1,6 @@
1
- sourcerer/__init__.py,sha256=3SFTxE-wK6g4MigwpFa7z88woGrTeoJTPs8F1qg8bII,585
2
- sourcerer/settings.py,sha256=dwY2GoaWD1CsAoVtWzaAhP7_e3KUugmBr2E624JSedc,1388
3
- sourcerer/utils.py,sha256=4jAlcofepAQMcD1cYDsC1ryGwBLxE9m7ckPS6CzDsCI,879
1
+ sourcerer/__init__.py,sha256=8QZGs8YbjerouVXeMDz5qYQPZeFHcyXhaO8IKkxzePM,636
2
+ sourcerer/settings.py,sha256=jIUcq9-_yYPxLS_8m1iKlkGCOg_pbhIjHXkR1LQmHQI,1463
3
+ sourcerer/utils.py,sha256=l5AitLCh58TdYwhC-Z2wQVXvq1q37ObuXeqDYba4hRU,1361
4
4
  sourcerer/domain/__init__.py,sha256=rV21d-dD-e0q4EQ2LfWDSDLlrUOjnHnWBtWPujoue0o,556
5
5
  sourcerer/domain/access_credentials/__init__.py,sha256=pFAwnr74uy09e7kubYsuaqzkVPkTA66dwjKzpIGQkAY,217
6
6
  sourcerer/domain/access_credentials/entities.py,sha256=m5hktJPmcwgG7vG-HKasA-gAd8US6jrZxGso4sCLZc8,2123
@@ -11,17 +11,24 @@ sourcerer/domain/file_system/__init__.py,sha256=5diScp5Q8Hw8e_4vqacVocQBw0KA7fPs
11
11
  sourcerer/domain/file_system/entities.py,sha256=IeTu5VYxmBQMH7AcKfW1Muk4wUmgZ769eWwNyqeWRpw,2060
12
12
  sourcerer/domain/file_system/exceptions.py,sha256=TairaMaLkSHgbiq3pImkmVZAACoVHy-1vQMl7Ox24cQ,509
13
13
  sourcerer/domain/file_system/services.py,sha256=kQeEM1Lt28UBVT9uG4M2iFDSxSvQdaJw4QzM9giUdjY,2208
14
+ sourcerer/domain/package_meta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ sourcerer/domain/package_meta/entities.py,sha256=IWAYpoYp4vrf2kxaLtlZrQSP5QmI8neufIY0QehWJjY,183
16
+ sourcerer/domain/package_meta/services.py,sha256=5hJDrOWV0wuTCoL4IrZ1uI1-Y-xH-NfOGSZlGlnmwP0,256
17
+ sourcerer/domain/settings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ sourcerer/domain/settings/entities.py,sha256=JJLFgJMO9NnpdOjGkAW6z-qxosIQigtbW3B-qcrdDD4,244
19
+ sourcerer/domain/settings/repositories.py,sha256=0EQxUjWnlL8raPj52pm7774U4rSgHHZfYy5lBfli2LE,611
20
+ sourcerer/domain/settings/services.py,sha256=j1nwddLhaWfmvtFMCYmQjsQBE34FGF06YLtF-lnsrRQ,817
14
21
  sourcerer/domain/shared/__init__.py,sha256=pkCn6PfBLIlYT5q4xWq3cNtOfbUrrePiH06TduLq6_o,148
15
22
  sourcerer/domain/shared/entities.py,sha256=jJR1PhJBWrSeJyEFvauEYJ1UXG7BcT6pi2oFpmglu4I,497
16
23
  sourcerer/domain/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
24
  sourcerer/domain/storage/entities.py,sha256=bSWVFx4Hh7_RLCHVLN_U2Wg6XxeYUyzDGLUpbFuWTLo,687
18
25
  sourcerer/domain/storage/repositories.py,sha256=v7RU8_X_HgFT3wn7Yc5m07aN7DEbsBMh8sRZSRSCTxU,1003
19
26
  sourcerer/domain/storage_provider/__init__.py,sha256=P3RUH9LFkWez3ehCczgVbnbp0tZZepPeOQf9spsC8FQ,192
20
- sourcerer/domain/storage_provider/entities.py,sha256=EgHkVFizMzzpzN161DlmWKPV9QhoRo8X7iiLtcN3_Wk,1931
27
+ sourcerer/domain/storage_provider/entities.py,sha256=koaY4M4nWeie9kXJrHLJ85fEvaLxOQJEq8Zl3vmGwMM,1931
21
28
  sourcerer/domain/storage_provider/exceptions.py,sha256=6xK5r62Bhedx3vV0_i7Eu5ZG5IExxeiuaGHG5sX17i4,508
22
29
  sourcerer/domain/storage_provider/services.py,sha256=Zm6nrKqQJW-9ZaqTp9wQaQuwMeH6hkLJZCkhQ_sTRF0,3997
23
30
  sourcerer/infrastructure/__init__.py,sha256=HQoqA8S9Vx2dr1Eua86wu_YxwXyY6jqa4IfEoZJcXcQ,616
24
- sourcerer/infrastructure/utils.py,sha256=_8dtobWxgrfHK7CUCFHq2AXS54KBwQQgDWWFxR21pFA,5198
31
+ sourcerer/infrastructure/utils.py,sha256=o6yxQ4f-kKYn-oRIEKpx3YWXwEEXfEEBvntW_JRMYnk,5199
25
32
  sourcerer/infrastructure/access_credentials/__init__.py,sha256=7BSXnI9n59_PuGxHjOra6PG82R_6JlrU4S1tsJx4WGM,249
26
33
  sourcerer/infrastructure/access_credentials/exceptions.py,sha256=usad48RTA7ub8AfnUs2EzbD-fiUwpmnRGjJluBjd3m4,1101
27
34
  sourcerer/infrastructure/access_credentials/registry.py,sha256=YEjkbh5639j8LkAocozC8O_7t1pPcrJwbL1LCJhiGkY,3416
@@ -29,10 +36,15 @@ sourcerer/infrastructure/access_credentials/repositories.py,sha256=_GLT-d3yarQD9
29
36
  sourcerer/infrastructure/access_credentials/services.py,sha256=QFU-eRKsl7P4Eg44i-A8BT6llVlT4vc6juvlJE39dq4,15720
30
37
  sourcerer/infrastructure/db/__init__.py,sha256=sjx2F0aDnxej7-FAjhFJYIQQ5d2apmItkquKbvGYIBc,175
31
38
  sourcerer/infrastructure/db/config.py,sha256=c3ubAxBWt-zDUwTe3pAgJ86-rw-LZZAZppuBRo93Xn8,1792
32
- sourcerer/infrastructure/db/models.py,sha256=XLkV7wpKZTPKCur32hpapT690O5UiILhYhTTr3dMw_U,2780
39
+ sourcerer/infrastructure/db/models.py,sha256=SnGWpz4PWBeXm6UvXU_rKhQcxscs_ckqjtP7WV2Cu9o,3571
33
40
  sourcerer/infrastructure/file_system/__init__.py,sha256=Swx8arwXeZ4E40ViDSpQglmUrIlpuEvZY6crcWpGC2g,219
34
41
  sourcerer/infrastructure/file_system/exceptions.py,sha256=obx0NxVuhVzLgL5K2MA5d4rNImlRHJ4Gr-2RrIuKJqk,2944
35
42
  sourcerer/infrastructure/file_system/services.py,sha256=J0XHv-zF8WblE6bzzEpKkqU_k3gefMj_OArWBRFtGHA,5649
43
+ sourcerer/infrastructure/package_meta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
+ sourcerer/infrastructure/package_meta/services.py,sha256=GKX9W5BGaeOkY4eeemp_mLRzj_DYaUyKIHZDTKeLCtU,873
45
+ sourcerer/infrastructure/settings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
+ sourcerer/infrastructure/settings/repositories.py,sha256=BZwDtjwR6pM79NB2q1Vr6VB-jjGG9GqkFP9kG4Oatgk,2104
47
+ sourcerer/infrastructure/settings/services.py,sha256=-wGRpkxSPvrCZnkUhiXztrfJCchxZEhNDwSVgZ8DYsQ,613
36
48
  sourcerer/infrastructure/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
49
  sourcerer/infrastructure/storage/repositories.py,sha256=GTMxxdeRmnxqpLUq_FCX9i28tkUL6xYRSsv9rVYFTzM,2334
38
50
  sourcerer/infrastructure/storage/services.py,sha256=wP37li_ts-By0Z_q5pizl__v-nRjFlkM7LlAJghujnc,1024
@@ -40,29 +52,33 @@ sourcerer/infrastructure/storage_provider/__init__.py,sha256=GONjDCsTmd6f_fF3lzx
40
52
  sourcerer/infrastructure/storage_provider/exceptions.py,sha256=acx3IIXD2yWlzLvD2asJBEpKEa6eJW31uKkzzr8MrR4,3336
41
53
  sourcerer/infrastructure/storage_provider/registry.py,sha256=8dbRLOx1jLK_i18uuh_JnKvId9NJBECKg4nG9F_dFH4,2249
42
54
  sourcerer/infrastructure/storage_provider/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
- sourcerer/infrastructure/storage_provider/services/azure.py,sha256=A7k8Zs6rK5nBsUr5ZNBzkDQWRxpU0qnfWAcrz3eZzAk,9796
44
- sourcerer/infrastructure/storage_provider/services/gcp.py,sha256=nohx-WE5STYRkeZd4nk8blBZxmWSWSEz5bdFOXN1QYc,9097
45
- sourcerer/infrastructure/storage_provider/services/s3.py,sha256=lSpf5SPyTZGl19ChgYBtf3IR6uUWULuAvLBEdD-oarM,9277
55
+ sourcerer/infrastructure/storage_provider/services/azure.py,sha256=ZCglIQ0DQX1mYimsT4rS1tUvZtLbvpR_NZZzjGw7sB4,9757
56
+ sourcerer/infrastructure/storage_provider/services/gcp.py,sha256=HyiGF-xfUYnykZOaAqqAFpuASsf0Plcp3kPjfvRZuXM,9058
57
+ sourcerer/infrastructure/storage_provider/services/s3.py,sha256=avRAtgZTobKnovfCG_lnW9xZOG77cnpDnZGazn85XHc,9239
46
58
  sourcerer/presentation/__init__.py,sha256=kzOeaTpy9hm61MLl_nybdooRrawFUd1uEX4f3Y-84ZU,472
47
59
  sourcerer/presentation/app.py,sha256=ROu3vSWzo6d8W30A9Zqi5zdLcVeHJsGLDJMLTKrthHE,1018
48
- sourcerer/presentation/di_container.py,sha256=KTDSWEtR_YTqJTOsyhwBBHqB9gqIkX-nu5a8Ks9UlDc,2215
49
- sourcerer/presentation/settings.py,sha256=iR8oK62c3kzgN5w3h0sEQlXhE5AEwufk_pv3hjMODMo,1255
50
- sourcerer/presentation/utils.py,sha256=9sDvwnXsycxHXvH0V_we5F6BzGyncGvWQ-vogaUqMBM,2979
60
+ sourcerer/presentation/di_container.py,sha256=m05fzDTGI11BXDQy1xmNl8rPHZfS1Zo_QlPhAhIyOHQ,2737
61
+ sourcerer/presentation/settings.py,sha256=TcoDQWzekC4pYBKTVrUoXZK5s05or_F0KXmW04P6uEA,1256
62
+ sourcerer/presentation/utils.py,sha256=ea02DEM1QOKtquc1x4ijLpD47p5whteesQP33N6Xizs,2980
51
63
  sourcerer/presentation/screens/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
64
+ sourcerer/presentation/screens/about/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
65
+ sourcerer/presentation/screens/about/main.py,sha256=7CnXMOYHCmEHjvnEbyHbrV-RUsw3g0OZiKdyiDp1h-w,2091
66
+ sourcerer/presentation/screens/about/styles.tcss,sha256=ImwlRBJjlyjIKiFb5WF5BydFAErNDuonyGKIdxtNpzM,484
52
67
  sourcerer/presentation/screens/critical_error/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
68
  sourcerer/presentation/screens/critical_error/main.py,sha256=10Ip1InBzktwwM2ijKXBOkhvBXGorw1X8EGxdh75WZE,2208
54
69
  sourcerer/presentation/screens/critical_error/styles.tcss,sha256=mURvbf0_npkRtzVBs2bVBybbyNK9cO_6Ar2Muk1Mpv8,604
55
- sourcerer/presentation/screens/file_system_finder/main.py,sha256=AC_G6VYMTO0NOYmGmOZ3be5isTGFjebR0IHFHU-yw3o,10343
70
+ sourcerer/presentation/screens/file_system_finder/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
71
+ sourcerer/presentation/screens/file_system_finder/main.py,sha256=8V603I74iKoyMMChbl8XjpQAu614qpW0sbRlkCAxSjg,10221
56
72
  sourcerer/presentation/screens/file_system_finder/styles.tcss,sha256=fZkdwXFsDkjXkaIskLxoQ_YHsLWKjgrn6hYseugg_68,718
57
73
  sourcerer/presentation/screens/file_system_finder/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
74
  sourcerer/presentation/screens/file_system_finder/widgets/file_system_navigator.py,sha256=giQXiNEsSAmBV-YWc1eXcJDFUWxpCppv0oNxgvrLZxg,31070
59
75
  sourcerer/presentation/screens/main/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
60
- sourcerer/presentation/screens/main/main.py,sha256=vIsrhj0rb1OR0ITASDvIl-yf1_7n8oNi494s7QRRBz8,23127
61
- sourcerer/presentation/screens/main/styles.tcss,sha256=Ruv2vBKzM8njH7OS2TCpZqCmRVEp7XQLeBN4XhVB5AU,381
76
+ sourcerer/presentation/screens/main/main.py,sha256=joYB72Z1H-kEYkJomMfGPN4m4R_QLUuxUbQeCBUtORs,27945
77
+ sourcerer/presentation/screens/main/styles.tcss,sha256=MgL2UwcLKWXY9R3qqk3cgBqjpW26r68MorAX42KRc28,502
62
78
  sourcerer/presentation/screens/main/messages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
63
79
  sourcerer/presentation/screens/main/messages/delete_request.py,sha256=puaU1UtbDErfYi8ViPEawhSbycnUpwdE81zZfzlslNE,203
64
80
  sourcerer/presentation/screens/main/messages/download_request.py,sha256=3urSTrvNbod1FrXfu-C1UDZqOu5D0OC-NJsKJhhprXE,205
65
- sourcerer/presentation/screens/main/messages/preview_request.py,sha256=px8N3E2VEexKmpZESb9nYDQvhXUThPxM0dU41B533tM,184
81
+ sourcerer/presentation/screens/main/messages/preview_request.py,sha256=-UVWFVr4vsB0LGj97MTT8pLbJ3eNJClMWvT6vQ4lqw8,198
66
82
  sourcerer/presentation/screens/main/messages/refresh_storages_list_request.py,sha256=NxOBcUf5oKF1cMCcHZny8qG2Jv6zTdPmYeFV9qORHh8,136
67
83
  sourcerer/presentation/screens/main/messages/resizing_rule.py,sha256=ws7lzS08h6qqeihF66XV5FsX26YkjQOje_4vgCw2mqI,332
68
84
  sourcerer/presentation/screens/main/messages/select_storage_item.py,sha256=iZOwzOwFLhsr58WV01-NtTFr4LXKykz1i76nayLkCLw,269
@@ -73,43 +89,48 @@ sourcerer/presentation/screens/main/mixins/resize_containers_watcher_mixin.py,sh
73
89
  sourcerer/presentation/screens/main/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
90
  sourcerer/presentation/screens/main/widgets/gradient.py,sha256=Ow6oaX0tsFNbUKRh_e9ITph20_tKoQgqzM7rujwKmxs,1263
75
91
  sourcerer/presentation/screens/main/widgets/resizing_rule.py,sha256=W4tAbSZlmAIytP1BNlSvBeiMGA7cD6l13IpxG2JYzxk,2088
76
- sourcerer/presentation/screens/main/widgets/storage_content.py,sha256=TnPG4uN9-7oZj4JbZC_Xj60BGj6jbm3IxygK-TsVpQg,26097
77
- sourcerer/presentation/screens/main/widgets/storage_list_sidebar.py,sha256=83R7HximPBPIj-3J9oWnISv10RcCabTo5EVWmhO5lSk,7748
92
+ sourcerer/presentation/screens/main/widgets/storage_content.py,sha256=7ngNzPSagFi2GzzYNzFQqq6DCppLil2taghJdkSube0,26278
93
+ sourcerer/presentation/screens/main/widgets/storage_list_sidebar.py,sha256=gGRO3r0z76J2snLzpzJDMUfb6__7il4FGVmXqoji_sg,11029
78
94
  sourcerer/presentation/screens/preview_content/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
- sourcerer/presentation/screens/preview_content/main.py,sha256=QX3eyoLkgMhRLneUz3rIHkzFGmxHqD1vvok9LcxU8cE,2785
80
- sourcerer/presentation/screens/preview_content/styles.tcss,sha256=Qk0nZtUfp-mCFaUXtkjwimAnshXnhEgh719aT4_lDPQ,374
95
+ sourcerer/presentation/screens/preview_content/main.py,sha256=UejOzmE6cl6HKTH_N0-spoByyTJRxZwI8-0nm3S8hi0,9111
96
+ sourcerer/presentation/screens/preview_content/styles.tcss,sha256=ESFpZgwZlehkrbCVhA45ODiXTpFuwqQPvQypsSjGxpQ,887
97
+ sourcerer/presentation/screens/preview_content/text_area_style.py,sha256=AOX8CG9A77gQIhF8GHTW45clPWrQcRKqu0KJcUzXrFU,2699
81
98
  sourcerer/presentation/screens/provider_creds_list/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
82
- sourcerer/presentation/screens/provider_creds_list/main.py,sha256=ybnaT6GROnNfIcwhfa5zrtEL4JaP80qRiL4YjqMocyk,8595
83
- sourcerer/presentation/screens/provider_creds_list/styles.tcss,sha256=_BXTWQw_LjPjbAG-V4YxxOeHn4GFShVjc4K1by-uVR0,956
99
+ sourcerer/presentation/screens/provider_creds_list/main.py,sha256=hOljhgw6f-RFckfPmzQoFJpvHXhjzaP2kVMQJdsjQp0,9135
100
+ sourcerer/presentation/screens/provider_creds_list/styles.tcss,sha256=CuSeHllFGWHW25av4UYm4YkhXZ8x0fDi6bEjv7hWOYA,1107
84
101
  sourcerer/presentation/screens/provider_creds_list/messages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
102
  sourcerer/presentation/screens/provider_creds_list/messages/reload_credentials_request.py,sha256=zkyLFkXZHQMj5OsDU_IiInUnezHNWyvYn8yZLoJmJn0,134
86
103
  sourcerer/presentation/screens/provider_creds_registration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
87
- sourcerer/presentation/screens/provider_creds_registration/main.py,sha256=sIkdS1cvjzT4-0Y-z1R2bLiNx8chinGQvr5v9VWcoBg,11354
104
+ sourcerer/presentation/screens/provider_creds_registration/main.py,sha256=CkGFef_JdMvcH0ve-XPGA_I8ZRTdzJNurvjUjZ0ZjLo,11422
88
105
  sourcerer/presentation/screens/provider_creds_registration/styles.tcss,sha256=gd1SNeRoHTYwNzdGxK-2aDqNPeY5b2wFWajtoNn5--Y,612
89
106
  sourcerer/presentation/screens/question/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
90
107
  sourcerer/presentation/screens/question/main.py,sha256=7ogFbKrqP9deBYfgq7Bs6NkiSdy3whEu51mW_--TCIY,980
91
108
  sourcerer/presentation/screens/question/styles.tcss,sha256=NT8Ty4opqYLG1-sal3m1us4M4LNhtTcQywlU-Al-v7o,396
109
+ sourcerer/presentation/screens/settings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
110
+ sourcerer/presentation/screens/settings/main.py,sha256=sjFEqGKKJ1bfb901Vz21HPx-BRfndtXLFQ_jeRat_Gg,2464
111
+ sourcerer/presentation/screens/settings/styles.tcss,sha256=116APkhBck6ktct-P_wqokrm80edrkeA2NnwLYVqFak,705
92
112
  sourcerer/presentation/screens/shared/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
93
113
  sourcerer/presentation/screens/shared/containers.py,sha256=9Tkl5SbPNgycZlfp5Pq50pHnJqbP0EckmxayXPPuhFs,378
114
+ sourcerer/presentation/screens/shared/modal_screens.py,sha256=ycpqEy9M3Dh_q-EO44_YbIyDAb9lkF8ZCUUje5fh42c,1128
94
115
  sourcerer/presentation/screens/shared/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
95
- sourcerer/presentation/screens/shared/widgets/button.py,sha256=aGsJTwK04nTXarafkl8TgdeB9EGfRwn4gffoPvFFwiU,1580
96
- sourcerer/presentation/screens/shared/widgets/labeled_input.py,sha256=OmaZjRJSg8LABvBvbN3LQ0s9M3-jp9X2i3yCVNbRhEk,2798
116
+ sourcerer/presentation/screens/shared/widgets/button.py,sha256=4L_reDy_nJg7l-xJwXbl6wWx0X5zYWyKwKd5OT_Qkms,1941
117
+ sourcerer/presentation/screens/shared/widgets/labeled_input.py,sha256=q6SBBnQykxDaMoVA3J8yJe3Ce9SPtElbO4OkShwBMQU,2774
97
118
  sourcerer/presentation/screens/shared/widgets/spinner.py,sha256=Do_Gc32GKA9ibFhORjfiK0RM3a7bvhnJ1SvnCXQJsHY,1713
98
119
  sourcerer/presentation/screens/storage_action_progress/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
99
- sourcerer/presentation/screens/storage_action_progress/main.py,sha256=0nNSxKwZv-ly5uA1cpdZf6x-UO8xoF0poHwc9VcGMj4,17899
120
+ sourcerer/presentation/screens/storage_action_progress/main.py,sha256=j5APRiZVpp08AN2jWTSrxj8jCGqTJ1Vczqf3_ezAHwE,17895
100
121
  sourcerer/presentation/screens/storage_action_progress/styles.tcss,sha256=ffvDxRWhckE5tjEG4jwlhQNqeQsYpdF71104StWCGWU,818
101
122
  sourcerer/presentation/screens/storages_list/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
102
- sourcerer/presentation/screens/storages_list/main.py,sha256=trlNfWIe3VE318ksy803TC1AH9duYN3ZEfzOAv1a9eM,6254
103
- sourcerer/presentation/screens/storages_list/styles.tcss,sha256=BlvlL5M-WGtMZuyqxT37gGlAIi1AFfILMaLiL-ZwvIc,766
123
+ sourcerer/presentation/screens/storages_list/main.py,sha256=i7Kxxz4W-OAg3fynUAKH7JO-PTwxUgegztbmUeFQ5eI,6823
124
+ sourcerer/presentation/screens/storages_list/styles.tcss,sha256=iKECoDkKiQB7UwxmHguYMd0nSzEWFScczXFWDpFPdN0,881
104
125
  sourcerer/presentation/screens/storages_list/messages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
105
126
  sourcerer/presentation/screens/storages_list/messages/reload_storages_request.py,sha256=PtwlWgPUP_ZcAid535fd4Py6AvKUVhaXbc0WMka4QD0,131
106
127
  sourcerer/presentation/screens/storages_registration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
107
- sourcerer/presentation/screens/storages_registration/main.py,sha256=_MlyeTbI-zoNWegGY6jnZPR9aoeTeeXDpjXQL8hEFIM,3734
128
+ sourcerer/presentation/screens/storages_registration/main.py,sha256=xam1qrUgmh7kA1y7EEbUAO1znZRpd1FQ7igN7Q_4j50,3802
108
129
  sourcerer/presentation/screens/storages_registration/styles.tcss,sha256=Yd78pkiaaShb30r5s6qlYlLxyB62DJNeAQqs_gMcP6k,601
109
130
  sourcerer/presentation/themes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
110
131
  sourcerer/presentation/themes/github_dark.py,sha256=9E1mEOr701nU-ZDSKBccMl3GYchroCEsxEVelm5oI-E,497
111
- data_sourcerer-0.3.0.dist-info/METADATA,sha256=JWKRdTOJMClCyOZR6alR2Ew2aFZtvN0fxAYxAnM52Ew,2535
112
- data_sourcerer-0.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
113
- data_sourcerer-0.3.0.dist-info/entry_points.txt,sha256=CyD02GehPW6QuhR5oDY5tLLRHQ9qbPXe0v3aT1pK3N8,62
114
- data_sourcerer-0.3.0.dist-info/licenses/LICENSE,sha256=HjZ7RAG3i6izxvitGfY4feHfvW5F8DPj5eF0YBSf2rI,1073
115
- data_sourcerer-0.3.0.dist-info/RECORD,,
132
+ data_sourcerer-0.5.0.dist-info/METADATA,sha256=IJq5I6ZR1wDk_-IM9M3zk4j63kOh6JsgGVbL1fQulNk,2614
133
+ data_sourcerer-0.5.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
134
+ data_sourcerer-0.5.0.dist-info/entry_points.txt,sha256=CyD02GehPW6QuhR5oDY5tLLRHQ9qbPXe0v3aT1pK3N8,62
135
+ data_sourcerer-0.5.0.dist-info/licenses/LICENSE,sha256=HjZ7RAG3i6izxvitGfY4feHfvW5F8DPj5eF0YBSf2rI,1073
136
+ data_sourcerer-0.5.0.dist-info/RECORD,,
sourcerer/__init__.py CHANGED
@@ -12,4 +12,6 @@ The application is structured using a clean architecture approach with:
12
12
  - Presentation layer: User interface components
13
13
  """
14
14
 
15
- __version__ = "0.3.0"
15
+ name = "sourcerer"
16
+ package_name = "data_sourcerer"
17
+ __version__ = "0.5.0"
File without changes
@@ -0,0 +1,9 @@
1
+ from msgspec._core import Struct
2
+
3
+
4
+ class PackageMeta(Struct):
5
+ version: str
6
+ latest_version: str | None
7
+ has_available_update: bool
8
+ system_version: str
9
+ platform: str
@@ -0,0 +1,9 @@
1
+ from abc import ABCMeta, abstractmethod
2
+
3
+ from sourcerer.domain.package_meta.entities import PackageMeta
4
+
5
+
6
+ class BasePackageMetaService(metaclass=ABCMeta):
7
+ @abstractmethod
8
+ def get_package_meta(self) -> PackageMeta:
9
+ raise NotImplementedError
File without changes
@@ -0,0 +1,11 @@
1
+ from msgspec._core import Struct
2
+
3
+
4
+ class Settings(Struct):
5
+ theme: str = "github-dark"
6
+ group_by_access_credentials: bool = False
7
+
8
+
9
+ class SettingsFields:
10
+ theme = "theme"
11
+ group_by_access_credentials = "group_by_access_credentials"
@@ -0,0 +1,20 @@
1
+ from abc import ABCMeta, abstractmethod
2
+
3
+ from sourcerer.domain.settings.entities import Settings
4
+
5
+
6
+ class BaseSettingsRepository(metaclass=ABCMeta):
7
+ @abstractmethod
8
+ def get_settings(self) -> Settings:
9
+ """Retrieve all settings as a Settings."""
10
+ raise NotImplementedError()
11
+
12
+ @abstractmethod
13
+ def get_setting(self, key: str) -> str:
14
+ """Retrieve a setting by its key."""
15
+ raise NotImplementedError()
16
+
17
+ @abstractmethod
18
+ def set_setting(self, key: str, value: str) -> None:
19
+ """Set a setting with a given key and value."""
20
+ raise NotImplementedError()
@@ -0,0 +1,19 @@
1
+ from sourcerer.domain.settings.entities import Settings
2
+ from sourcerer.domain.settings.repositories import BaseSettingsRepository
3
+
4
+
5
+ class BaseSettingsService:
6
+ def __init__(self, repository: BaseSettingsRepository):
7
+ self.repository = repository
8
+
9
+ def load_settings(self) -> Settings:
10
+ """Load settings from the settings file."""
11
+ raise NotImplementedError("This method should be implemented by subclasses.")
12
+
13
+ def get_setting(self, key: str) -> str:
14
+ """Get the value of a setting by its key."""
15
+ raise NotImplementedError("This method should be implemented by subclasses.")
16
+
17
+ def set_setting(self, key: str, value: str) -> None:
18
+ """Set the value of a setting by its key."""
19
+ raise NotImplementedError("This method should be implemented by subclasses.")
@@ -63,7 +63,7 @@ class File(Struct):
63
63
 
64
64
  uuid: str
65
65
  key: str
66
- size: str
66
+ size: int
67
67
  is_text: bool
68
68
  date_modified: datetime | None = None
69
69
 
@@ -76,3 +76,26 @@ class Storage(Base):
76
76
  cascade="save-update",
77
77
  backref=backref("storages", passive_deletes=True),
78
78
  )
79
+
80
+
81
+ class Settings(Base):
82
+ """
83
+ SQLAlchemy model for storing application settings.
84
+
85
+ This model represents the settings table in the database,
86
+ storing key-value pairs for application configuration.
87
+
88
+ Attributes:
89
+ id (int): Primary key
90
+ key (str): Setting key
91
+ value (str): Setting value
92
+ created_at (datetime): Timestamp when the setting was created
93
+ updated_at (datetime): Timestamp when the setting was last updated
94
+ """
95
+
96
+ __tablename__ = "settings"
97
+ id = Column(Integer, primary_key=True)
98
+ key = Column(String, unique=True, nullable=False)
99
+ value = Column(String, nullable=False)
100
+ created_at = Column(DateTime, default=datetime.utcnow)
101
+ updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
File without changes
@@ -0,0 +1,26 @@
1
+ import platform
2
+
3
+ from packaging import version
4
+
5
+ from sourcerer import __version__, package_name
6
+ from sourcerer.domain.package_meta.entities import PackageMeta
7
+ from sourcerer.domain.package_meta.services import BasePackageMetaService
8
+ from sourcerer.utils import get_last_package_version
9
+
10
+
11
+ class PackageMetaService(BasePackageMetaService):
12
+ def get_package_meta(self) -> PackageMeta:
13
+ latest_version = get_last_package_version(package_name)
14
+ has_available_update = (
15
+ version.parse(latest_version) > version.parse(__version__)
16
+ if latest_version
17
+ else False
18
+ )
19
+
20
+ return PackageMeta(
21
+ version=__version__,
22
+ latest_version=latest_version,
23
+ has_available_update=has_available_update,
24
+ system_version=platform.release(),
25
+ platform=platform.system(),
26
+ )
File without changes
@@ -0,0 +1,59 @@
1
+ from typing import get_type_hints
2
+
3
+ from sourcerer.domain.settings.entities import Settings
4
+ from sourcerer.domain.settings.repositories import BaseSettingsRepository
5
+ from sourcerer.infrastructure.db.models import Settings as DBSettings
6
+
7
+
8
+ class SQLAlchemySettingsRepository(BaseSettingsRepository):
9
+ def __init__(self, db):
10
+ """
11
+ Initialize the repository with a database session factory.
12
+
13
+ Args:
14
+ db: Database session factory
15
+ """
16
+ self.db = db
17
+
18
+ def get_settings(self) -> Settings:
19
+ """Retrieve all settings as a Settings."""
20
+
21
+ with self.db() as session:
22
+ settings = session.query(DBSettings).all()
23
+ type_hints = get_type_hints(Settings)
24
+ return Settings(
25
+ **{
26
+ setting.key: self.parse_value(
27
+ setting.key, setting.value, type_hints.get(setting.key, str)
28
+ )
29
+ for setting in settings
30
+ }
31
+ )
32
+
33
+ def get_setting(self, key: str) -> str:
34
+ """Retrieve a setting by its key."""
35
+ with self.db() as session:
36
+ setting = session.query(DBSettings).filter(DBSettings.key == key).first()
37
+ if setting is None:
38
+ raise KeyError(f"Setting '{key}' not found.")
39
+ return setting.value
40
+
41
+ def set_setting(self, key: str, value: str) -> None:
42
+ """Set a setting with a given key and value."""
43
+ with self.db() as session:
44
+ setting = session.query(DBSettings).filter(DBSettings.key == key).first()
45
+ if setting is None:
46
+ setting = DBSettings(key=key, value=value)
47
+ session.add(setting)
48
+ else:
49
+ setting.value = value
50
+ session.commit()
51
+
52
+ def parse_value(self, key: str, value: str, expected_type):
53
+ if expected_type is bool:
54
+ return value.lower() in ("true", "1", "yes", "on")
55
+ if expected_type is int:
56
+ return int(value)
57
+ if expected_type is float:
58
+ return float(value)
59
+ return value # Assume string or leave as-is
@@ -0,0 +1,16 @@
1
+ from sourcerer.domain.settings.entities import Settings
2
+ from sourcerer.domain.settings.services import BaseSettingsService
3
+
4
+
5
+ class SettingsService(BaseSettingsService):
6
+ def load_settings(self) -> Settings:
7
+ """Load settings from the settings file."""
8
+ return self.repository.get_settings()
9
+
10
+ def get_setting(self, key: str) -> str:
11
+ """Get the value of a setting by its key."""
12
+ return self.repository.get_setting(key)
13
+
14
+ def set_setting(self, key: str, value: str) -> None:
15
+ """Set the value of a setting by its key."""
16
+ self.repository.set_setting(key, value)
@@ -10,7 +10,6 @@ from collections.abc import Callable
10
10
  from pathlib import Path
11
11
  from typing import Any
12
12
 
13
- import humanize
14
13
  from azure.mgmt.storage import StorageManagementClient
15
14
  from azure.storage.blob import BlobServiceClient
16
15
  from platformdirs import user_downloads_dir
@@ -38,7 +37,6 @@ from sourcerer.infrastructure.utils import generate_uuid, is_text_file
38
37
 
39
38
  @storage_provider(StorageProvider.AzureStorage)
40
39
  class AzureStorageProviderService(BaseStorageProviderService):
41
-
42
40
  def __init__(self, credentials: Any):
43
41
  """
44
42
  Initialize the service with Azure credentials.
@@ -137,7 +135,7 @@ class AzureStorageProviderService(BaseStorageProviderService):
137
135
  File(
138
136
  generate_uuid(),
139
137
  remaining_path,
140
- size=humanize.naturalsize(blob.size),
138
+ size=blob.size,
141
139
  date_modified=blob.last_modified,
142
140
  is_text=is_text_file(blob.name),
143
141
  )
@@ -9,7 +9,6 @@ from collections.abc import Callable
9
9
  from pathlib import Path
10
10
  from typing import Any
11
11
 
12
- import humanize
13
12
  from platformdirs import user_downloads_dir
14
13
 
15
14
  from sourcerer.domain.shared.entities import StorageProvider
@@ -119,7 +118,6 @@ class GCPStorageProviderService(BaseStorageProviderService):
119
118
  ListStorageItemsError: If an error occurs while listing items
120
119
  """
121
120
  try:
122
-
123
121
  files = []
124
122
  folders = []
125
123
  if path and not path.endswith("/"):
@@ -136,7 +134,7 @@ class GCPStorageProviderService(BaseStorageProviderService):
136
134
  File(
137
135
  generate_uuid(),
138
136
  blob.name[len(path) :],
139
- size=humanize.naturalsize(blob.size),
137
+ size=blob.size,
140
138
  date_modified=blob.updated.date(),
141
139
  is_text=is_text_file(blob.name),
142
140
  )
@@ -10,7 +10,6 @@ from itertools import groupby
10
10
  from pathlib import Path
11
11
  from typing import Any
12
12
 
13
- import humanize
14
13
  from platformdirs import user_downloads_dir
15
14
 
16
15
  from sourcerer.domain.shared.entities import StorageProvider
@@ -173,7 +172,7 @@ class S3ProviderService(BaseStorageProviderService):
173
172
  File(
174
173
  generate_uuid(),
175
174
  i.get("Key").replace(path, ""),
176
- humanize.naturalsize(i.get("Size")),
175
+ i.get("Size"),
177
176
  is_text_file(i.get("Key")),
178
177
  i.get("LastModified"),
179
178
  )
@@ -4,6 +4,7 @@ Utility functions for the Sourcerer application.
4
4
  This module provides various utility functions used throughout the application,
5
5
  including UUID generation, MIME type detection, and file type checking.
6
6
  """
7
+
7
8
  import mimetypes
8
9
  import secrets
9
10
  import uuid
@@ -16,6 +16,9 @@ from sourcerer.infrastructure.access_credentials.repositories import (
16
16
  from sourcerer.infrastructure.access_credentials.services import CredentialsService
17
17
  from sourcerer.infrastructure.db.config import Database
18
18
  from sourcerer.infrastructure.file_system.services import FileSystemService
19
+ from sourcerer.infrastructure.package_meta.services import PackageMetaService
20
+ from sourcerer.infrastructure.settings.repositories import SQLAlchemySettingsRepository
21
+ from sourcerer.infrastructure.settings.services import SettingsService
19
22
  from sourcerer.infrastructure.storage.repositories import SQLAlchemyStoragesRepository
20
23
  from sourcerer.infrastructure.storage.services import StoragesService
21
24
  from sourcerer.settings import APP_DIR, DB_NAME
@@ -50,6 +53,10 @@ class DiContainer(containers.DeclarativeContainer):
50
53
  SQLAlchemyStoragesRepository, session_factory
51
54
  )
52
55
 
56
+ settings_repository = providers.Factory(
57
+ SQLAlchemySettingsRepository, session_factory
58
+ )
59
+
53
60
  credentials_service = providers.Factory(
54
61
  CredentialsService, repository=credentials_repository
55
62
  )
@@ -59,3 +66,9 @@ class DiContainer(containers.DeclarativeContainer):
59
66
  )
60
67
 
61
68
  file_system_service = providers.Factory(FileSystemService, Path.home())
69
+
70
+ package_meta_service = providers.Factory(PackageMetaService)
71
+ settings_service = providers.Factory(
72
+ SettingsService,
73
+ repository=settings_repository,
74
+ )
File without changes
@@ -0,0 +1,60 @@
1
+ from dependency_injector.wiring import Provide
2
+ from rich.text import Text
3
+ from textual import on
4
+ from textual.app import ComposeResult
5
+ from textual.containers import Container, Horizontal
6
+ from textual.widgets import Static
7
+
8
+ from sourcerer.domain.package_meta.services import BasePackageMetaService
9
+ from sourcerer.presentation.di_container import DiContainer
10
+ from sourcerer.presentation.screens.shared.modal_screens import ExitBoundModalScreen
11
+ from sourcerer.presentation.screens.shared.widgets.button import Button
12
+
13
+
14
+ class AboutScreen(ExitBoundModalScreen):
15
+ """Screen with a parameter."""
16
+
17
+ CSS_PATH = "styles.tcss"
18
+
19
+ def __init__(
20
+ self,
21
+ package_meta_service: BasePackageMetaService = Provide[
22
+ DiContainer.package_meta_service
23
+ ],
24
+ ) -> None:
25
+ super().__init__()
26
+ self.package_meta_service = package_meta_service
27
+
28
+ def compose(self) -> ComposeResult:
29
+ package_meta = self.package_meta_service.get_package_meta()
30
+
31
+ with Container():
32
+ yield Static(Text("Sourcerer", style="bold cyan", justify="center"))
33
+ yield Static(
34
+ Text(
35
+ f"Version: {package_meta.version}"
36
+ f"{' (newer version is available)' if package_meta.has_available_update else ''}",
37
+ style="dim",
38
+ justify="center",
39
+ )
40
+ )
41
+ yield Static(
42
+ Text(
43
+ f"Platform: {package_meta.platform}", style="dim", justify="center"
44
+ )
45
+ )
46
+ yield Static(
47
+ Text(
48
+ f"System Version: {package_meta.system_version}",
49
+ style="dim",
50
+ justify="center",
51
+ )
52
+ )
53
+ with Horizontal(id="controls"):
54
+ yield Button("Close", name="close")
55
+
56
+ @on(Button.Click)
57
+ def on_button_clicked(self, event: Button.Click) -> None:
58
+ """Handle button clicked events."""
59
+ if event.action == "close":
60
+ self.action_cancel_screen()