config-cli-gui 0.3.3__tar.gz → 0.3.5__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 (86) hide show
  1. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/.github/workflows/main.yml +1 -1
  2. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/HISTORY.md +27 -0
  3. {config_cli_gui-0.3.3/src/config_cli_gui.egg-info → config_cli_gui-0.3.5}/PKG-INFO +2 -1
  4. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/config.yaml +18 -24
  5. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/docs/usage/config.md +20 -23
  6. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/pyproject.toml +1 -0
  7. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/src/config_cli_gui/_version.py +3 -3
  8. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/src/config_cli_gui/config.py +120 -10
  9. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/src/config_cli_gui/docs.py +8 -4
  10. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/src/config_cli_gui/gui.py +155 -72
  11. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/src/config_cli_gui/logging.py +80 -6
  12. config_cli_gui-0.3.5/src/config_cli_gui/persistence.py +75 -0
  13. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5/src/config_cli_gui.egg-info}/PKG-INFO +2 -1
  14. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/src/config_cli_gui.egg-info/SOURCES.txt +1 -1
  15. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/src/config_cli_gui.egg-info/requires.txt +1 -0
  16. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/tests/example_project/cli/cli_example.py +6 -1
  17. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/tests/example_project/config/config_example.py +3 -64
  18. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/tests/example_project/gui/gui_example.py +52 -6
  19. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/tests/test_config_manager.py +1 -1
  20. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/uv.lock +14 -0
  21. config_cli_gui-0.3.3/tests/example_project/gui/config.yaml +0 -48
  22. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/.github/FUNDING.yml +0 -0
  23. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  24. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  25. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  26. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/.github/actions/setup-environment/action.yml +0 -0
  27. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/.github/dependabot.yml +0 -0
  28. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/.github/init.sh +0 -0
  29. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/.github/release_message.sh +0 -0
  30. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/.github/update_funding.py +0 -0
  31. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/.github/workflows/release.yml +0 -0
  32. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/.github/workflows/update_readme.yml +0 -0
  33. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/.gitignore +0 -0
  34. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/.idea/runConfigurations/config_generate.xml +0 -0
  35. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/.idea/runConfigurations/example_project_cli.xml +0 -0
  36. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/.idea/runConfigurations/example_project_gui.xml +0 -0
  37. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/.pre-commit-config.yaml +0 -0
  38. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/.readthedocs.yaml +0 -0
  39. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/LICENSE +0 -0
  40. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/Makefile +0 -0
  41. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/README.md +0 -0
  42. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/docs/.nav.yml +0 -0
  43. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/docs/_static/img/favicon.png +0 -0
  44. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/docs/_static/img/logo.png +0 -0
  45. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/docs/_static/img/settings_dlg.png +0 -0
  46. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/docs/_static/img/settings_doc.png +0 -0
  47. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/docs/css/custom.css +0 -0
  48. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/docs/develop/contributing.md +0 -0
  49. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/docs/develop/make_windows.md +0 -0
  50. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/docs/develop/naming_convention.md +0 -0
  51. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/docs/funding/funding.md +0 -0
  52. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/docs/getting-started/install.md +0 -0
  53. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/docs/getting-started/virtual-environment.md +0 -0
  54. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/docs/index.md +0 -0
  55. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/docs/usage/cli.md +0 -0
  56. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/mkdocs.yml +0 -0
  57. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/scripts/show_filelist.ps1 +0 -0
  58. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/scripts/show_tree.ps1 +0 -0
  59. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/scripts/show_tree.py +0 -0
  60. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/scripts/update_readme.py +0 -0
  61. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/setup.cfg +0 -0
  62. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/src/__init__.py +0 -0
  63. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/src/config_cli_gui/__init__.py +0 -0
  64. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/src/config_cli_gui/cli.py +0 -0
  65. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/src/config_cli_gui/configtypes/__init__.py +0 -0
  66. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/src/config_cli_gui/configtypes/color.py +0 -0
  67. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/src/config_cli_gui/configtypes/font.py +0 -0
  68. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/src/config_cli_gui/configtypes/vector.py +0 -0
  69. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/src/config_cli_gui.egg-info/dependency_links.txt +0 -0
  70. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/src/config_cli_gui.egg-info/entry_points.txt +0 -0
  71. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/src/config_cli_gui.egg-info/top_level.txt +0 -0
  72. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/template.yml.url +0 -0
  73. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/tests/__init__.py +0 -0
  74. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/tests/example_project/__init__.py +0 -0
  75. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/tests/example_project/__main__.py +0 -0
  76. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/tests/example_project/cli/__init__.py +0 -0
  77. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/tests/example_project/cli/__main__.py +0 -0
  78. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/tests/example_project/config/__init__.py +0 -0
  79. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/tests/example_project/core/__init__.py +0 -0
  80. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/tests/example_project/core/base.py +0 -0
  81. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/tests/example_project/example.gpx +0 -0
  82. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/tests/example_project/gui/__init__.py +0 -0
  83. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/tests/example_project/gui/__main__.py +0 -0
  84. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/tests/test_cli.py +0 -0
  85. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/tests/test_docs.py +0 -0
  86. {config_cli_gui-0.3.3 → config_cli_gui-0.3.5}/tests/test_generic_cli.py +0 -0
@@ -63,7 +63,7 @@ jobs:
63
63
  fail-fast: false
64
64
  matrix:
65
65
  python-version: [ 3.11 ]
66
- os: [ ubuntu-latest, macos-latest ]
66
+ os: [ ubuntu-latest ]
67
67
  runs-on: ${{ matrix.os }}
68
68
  steps:
69
69
  - uses: actions/checkout@v4
@@ -4,6 +4,33 @@ Changelog
4
4
 
5
5
  (unreleased)
6
6
  ------------
7
+ - Improve gui. [Paul Magister]
8
+ - Fix gui log issue: even better fix. [Paul Magister]
9
+ - Fix gui log issue: [Paul Magister]
10
+
11
+ Previously the logger handler attempted to call the passed object like a function; GuiLogWriter provides write(), not a call. Because emit() swallowed exceptions, you saw nothing in the GUI log.
12
+ Now connect_gui_logging detects objects with .write() and registers the writer.write callable with the UI handler. GuiLogHandler.emit calls that callable, which writes to the text widget and the text appears.
13
+ - Improve config consistency: correctly consider logging parameters.
14
+ [Paul Magister]
15
+ - Improve config usage consistency: allways load last config. Move
16
+ AppConfig into config.py since it is used in the core lib component.
17
+ [Paul Magister]
18
+ - Save last opened config to start there on the next session, move theme
19
+ to app category. [Paul Magister]
20
+ - Do not run tests on macos. [Paul Magister]
21
+
22
+
23
+ 0.3.4 (2026-06-18)
24
+ ------------------
25
+ - Docs: Update HISTORY.md for release 0.3.4. [Paul Magister]
26
+ - Bugfix: init color display properly. [Paul Magister]
27
+ - Use ttkbootstrap gui styles. [Paul Magister]
28
+ - Improve GUI scaling and font sizes. [Paul Magister]
29
+
30
+
31
+ 0.3.3 (2026-06-13)
32
+ ------------------
33
+ - Docs: Update HISTORY.md for release 0.3.3. [Paul Magister]
7
34
  - Update to minimum python 3.11. [Paul Magister]
8
35
 
9
36
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: config-cli-gui
3
- Version: 0.3.3
3
+ Version: 0.3.5
4
4
  Summary: Feature-rich Python project template for config-cli-gui.
5
5
  Author: pamagister
6
6
  Requires-Python: <3.12,>=3.11
@@ -9,6 +9,7 @@ License-File: LICENSE
9
9
  Requires-Dist: pydantic>=2.11.7
10
10
  Requires-Dist: pyyaml>=6.0.2
11
11
  Requires-Dist: pillow>=12.0.0
12
+ Requires-Dist: ttkbootstrap>=1.20.3
12
13
  Provides-Extra: dev
13
14
  Requires-Dist: pytest>=8.4.0; extra == "dev"
14
15
  Requires-Dist: pytest-mock>=3.14.1; extra == "dev"
@@ -1,3 +1,16 @@
1
+ app:
2
+ # Date format to use | type=str
3
+ date_format: '%Y-%m-%d'
4
+ # Logging level for the application | type=str | choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']
5
+ log_level: INFO
6
+ # Maximum log file size in MB before rotation | type=int
7
+ log_file_max_size: 2
8
+ # Enable logging to file | type=bool | choices=[True, False]
9
+ enable_file_logging: true
10
+ # Enable logging to console | type=bool | choices=[True, False]
11
+ enable_console_logging: true
12
+ # GUI theme setting supported by ttkbootstrap | type=str | choices=['cosmo', 'flatly', 'litera', 'minty', 'lumen', 'sandstone', 'yeti', 'pulse', 'united', 'darkly', 'superhero', 'solar', 'cyborg', 'vapor', 'simplex']
13
+ theme: superhero
1
14
  cli:
2
15
  # Path to input (file or folder) | type=str | [CLI]
3
16
  input: ''
@@ -9,26 +22,7 @@ cli:
9
22
  extract_waypoints: true
10
23
  # Include elevation data in waypoints | type=bool | [CLI] | choices=[True, False]
11
24
  elevation: false
12
- app:
13
- # Date format to use | type=str
14
- date_format: '%Y-%m-%d'
15
- # Logging level for the application | type=str | choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']
16
- log_level: INFO
17
- # Maximum log file size in MB before rotation | type=int
18
- log_file_max_size: 10
19
- # Number of backup log files to keep | type=int
20
- log_backup_count: 5
21
- # Log message format style | type=str | choices=['simple', 'detailed', 'json']
22
- log_format: detailed
23
- # Maximum number of worker threads | type=int
24
- max_workers: 4
25
- # Enable logging to file | type=bool | choices=[True, False]
26
- enable_file_logging: true
27
- # Enable logging to console | type=bool | choices=[True, False]
28
- enable_console_logging: true
29
25
  gui:
30
- # GUI theme setting | type=str | choices=['light', 'dark', 'auto']
31
- theme: light
32
26
  # Default window width | type=int
33
27
  window_width: 800
34
28
  # Default window height | type=int
@@ -40,21 +34,21 @@ gui:
40
34
  # Maximum number of log lines to keep in GUI | type=int
41
35
  max_log_lines: 1000
42
36
  # Point in 2D space | type=Vector
43
- point2D: (7, 11)
37
+ point2D: (7.0, 11.0)
44
38
  # Point in 3D space | type=Vector
45
39
  point3D: (1.2, 3.4, 5.6)
46
40
  misc:
47
41
  # Example integer | type=int
48
42
  some_numeric: 42
49
43
  # Example vector 2D | type=Vector
50
- some_vector2d: (1, 2)
44
+ some_vector2d: (1.0, 2.0)
51
45
  # Example vector 3D | type=Vector
52
46
  some_vector3d: (1.1, 2.2, 3.3)
53
- # Path to the file to use | type=PosixPath
47
+ # Path to the file to use | type=str
54
48
  some_file: some_file.txt
55
49
  # Color setting for the application | type=Color
56
50
  some_color: '#ff0000'
57
51
  # Date setting for the application | type=datetime
58
- some_date: '2025-12-31T10:30:45'
52
+ some_date: '2025-12-31T10:30:00'
59
53
  # Font setting for the application | type=Font
60
- some_font: 'DejaVuSans.ttf, 12, #0000ff'
54
+ some_font: 'DejaVuSans.ttf, 12.0, #0000ff'
@@ -3,6 +3,17 @@
3
3
  These parameters are available to configure the behavior of your application.
4
4
  The parameters in the cli category can be accessed via the command line interface.
5
5
 
6
+ ## Category "app"
7
+
8
+ | Name | Type | Description | Default | Choices |
9
+ |------------------------|------|---------------------------------------------|------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
10
+ | date_format | str | Date format to use | '%Y-%m-%d' | - |
11
+ | log_level | str | Logging level for the application | 'INFO' | ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'] |
12
+ | log_file_max_size | int | Maximum log file size in MB before rotation | 2 | - |
13
+ | enable_file_logging | bool | Enable logging to file | True | [True, False] |
14
+ | enable_console_logging | bool | Enable logging to console | True | [True, False] |
15
+ | theme | str | GUI theme setting supported by ttkbootstrap | 'darkly' | ['cosmo', 'flatly', 'litera', 'minty', 'lumen', 'sandstone', 'yeti', 'pulse', 'united', 'darkly', 'superhero', 'solar', 'cyborg', 'vapor', 'simplex'] |
16
+
6
17
  ## Category "cli"
7
18
 
8
19
  | Name | Type | Description | Default | Choices |
@@ -13,31 +24,17 @@ The parameters in the cli category can be accessed via the command line interfac
13
24
  | extract_waypoints | bool | Extract starting points of each track as waypoint | True | [True, False] |
14
25
  | elevation | bool | Include elevation data in waypoints | False | [True, False] |
15
26
 
16
- ## Category "app"
17
-
18
- | Name | Type | Description | Default | Choices |
19
- |------------------------|------|---------------------------------------------|------------|---------------------------------------------------|
20
- | date_format | str | Date format to use | '%Y-%m-%d' | - |
21
- | log_level | str | Logging level for the application | 'INFO' | ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'] |
22
- | log_file_max_size | int | Maximum log file size in MB before rotation | 10 | - |
23
- | log_backup_count | int | Number of backup log files to keep | 5 | - |
24
- | log_format | str | Log message format style | 'detailed' | ['simple', 'detailed', 'json'] |
25
- | max_workers | int | Maximum number of worker threads | 4 | - |
26
- | enable_file_logging | bool | Enable logging to file | True | [True, False] |
27
- | enable_console_logging | bool | Enable logging to console | True | [True, False] |
28
-
29
27
  ## Category "gui"
30
28
 
31
- | Name | Type | Description | Default | Choices |
32
- |-------------------|--------|------------------------------------------------|-----------------------|---------------------------|
33
- | theme | str | GUI theme setting | 'light' | ['light', 'dark', 'auto'] |
34
- | window_width | int | Default window width | 800 | - |
35
- | window_height | int | Default window height | 600 | - |
36
- | log_window_height | int | Height of the log window in pixels | 200 | - |
37
- | auto_scroll_log | bool | Automatically scroll to the newest log entries | True | [True, False] |
38
- | max_log_lines | int | Maximum number of log lines to keep in GUI | 1000 | - |
39
- | point2D | Vector | Point in 2D space | Vector(7, 11) | - |
40
- | point3D | Vector | Point in 3D space | Vector(1.2, 3.4, 5.6) | - |
29
+ | Name | Type | Description | Default | Choices |
30
+ |-------------------|--------|------------------------------------------------|-----------------------|---------------|
31
+ | window_width | int | Default window width | 800 | - |
32
+ | window_height | int | Default window height | 600 | - |
33
+ | log_window_height | int | Height of the log window in pixels | 200 | - |
34
+ | auto_scroll_log | bool | Automatically scroll to the newest log entries | True | [True, False] |
35
+ | max_log_lines | int | Maximum number of log lines to keep in GUI | 1000 | - |
36
+ | point2D | Vector | Point in 2D space | Vector(7, 11) | - |
37
+ | point3D | Vector | Point in 3D space | Vector(1.2, 3.4, 5.6) | - |
41
38
 
42
39
  ## Category "misc"
43
40
 
@@ -11,6 +11,7 @@ dependencies = [
11
11
  "pydantic>=2.11.7",
12
12
  "pyyaml>=6.0.2",
13
13
  "pillow>=12.0.0",
14
+ "ttkbootstrap>=1.20.3",
14
15
  ]
15
16
 
16
17
  # Dev dependencies as optional dependencies
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.3.3'
22
- __version_tuple__ = version_tuple = (0, 3, 3)
21
+ __version__ = version = '0.3.5'
22
+ __version_tuple__ = version_tuple = (0, 3, 5)
23
23
 
24
- __commit_id__ = commit_id = 'gbc73a5894'
24
+ __commit_id__ = commit_id = 'g089c3018c'
@@ -1,4 +1,5 @@
1
1
  import json
2
+ import logging
2
3
  from abc import ABC, abstractmethod
3
4
  from collections.abc import Iterable
4
5
  from dataclasses import dataclass
@@ -9,6 +10,7 @@ from typing import Any
9
10
  import yaml
10
11
  from pydantic import BaseModel
11
12
 
13
+ from config_cli_gui import persistence
12
14
  from config_cli_gui.configtypes.color import Color
13
15
  from config_cli_gui.configtypes.font import Font
14
16
  from config_cli_gui.configtypes.vector import Vector
@@ -63,21 +65,25 @@ class ConfigSerializer:
63
65
  TYPE_MAPPING = {
64
66
  Font: {
65
67
  "to_serializable": lambda v: v.to_str(),
66
- "from_serializable": lambda v: Font.from_list(v)
67
- if isinstance(v, list)
68
- else Font.from_str(v),
68
+ "from_serializable": lambda v: (
69
+ Font.from_list(v) if isinstance(v, list) else Font.from_str(v)
70
+ ),
69
71
  },
70
72
  Color: {
71
73
  "to_serializable": lambda v: v.to_hex(),
72
- "from_serializable": lambda v: Color.from_list(v)
73
- if isinstance(v, list)
74
- else (Color.from_hex(v) if isinstance(v, str) else v),
74
+ "from_serializable": lambda v: (
75
+ Color.from_list(v)
76
+ if isinstance(v, list)
77
+ else (Color.from_hex(v) if isinstance(v, str) else v)
78
+ ),
75
79
  },
76
80
  Vector: {
77
81
  "to_serializable": lambda v: v.to_str(),
78
- "from_serializable": lambda v: Vector.from_list(v)
79
- if isinstance(v, list)
80
- else (Vector.from_str(v) if isinstance(v, str) else v),
82
+ "from_serializable": lambda v: (
83
+ Vector.from_list(v)
84
+ if isinstance(v, list)
85
+ else (Vector.from_str(v) if isinstance(v, str) else v)
86
+ ),
81
87
  },
82
88
  Path: {
83
89
  "to_serializable": lambda v: str(v.as_posix()),
@@ -85,7 +91,7 @@ class ConfigSerializer:
85
91
  },
86
92
  datetime: {
87
93
  "to_serializable": lambda v: v.isoformat(),
88
- "from_serializable": lambda v: datetime.fromisoformat(v) if isinstance(v, str) else v,
94
+ "from_serializable": lambda v: (datetime.fromisoformat(v) if isinstance(v, str) else v),
89
95
  },
90
96
  }
91
97
 
@@ -104,6 +110,74 @@ class ConfigSerializer:
104
110
  return value
105
111
 
106
112
 
113
+ class AppConfig(ConfigCategory):
114
+ """Application-specific configuration parameters (centralized).
115
+
116
+ This category was moved from example project to the core library so that
117
+ every project uses a consistent, non-editable application configuration
118
+ section. It contains general application settings such as logging and
119
+ theme preferences.
120
+ """
121
+
122
+ def get_category_name(self) -> str:
123
+ return "app"
124
+
125
+ date_format: ConfigParameter = ConfigParameter(
126
+ name="date_format",
127
+ value="%Y-%m-%d",
128
+ help="Date format to use",
129
+ )
130
+
131
+ log_level: ConfigParameter = ConfigParameter(
132
+ name="log_level",
133
+ value="INFO",
134
+ choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
135
+ help="Logging level for the application",
136
+ )
137
+
138
+ log_file_max_size: ConfigParameter = ConfigParameter(
139
+ name="log_file_max_size",
140
+ value=2,
141
+ help="Maximum log file size in MB before rotation",
142
+ )
143
+
144
+ enable_file_logging: ConfigParameter = ConfigParameter(
145
+ name="enable_file_logging",
146
+ value=True,
147
+ help="Enable logging to file",
148
+ )
149
+
150
+ enable_console_logging: ConfigParameter = ConfigParameter(
151
+ name="enable_console_logging",
152
+ value=True,
153
+ help="Enable logging to console",
154
+ )
155
+
156
+ # ttkbootstrap.Style().theme_names()
157
+ theme: ConfigParameter = ConfigParameter(
158
+ name="theme",
159
+ value="darkly",
160
+ choices=[
161
+ "cosmo",
162
+ "flatly",
163
+ "litera",
164
+ "minty",
165
+ "lumen",
166
+ "sandstone",
167
+ "yeti",
168
+ "pulse",
169
+ "united",
170
+ "darkly",
171
+ "superhero",
172
+ "solar",
173
+ "cyborg",
174
+ "vapor",
175
+ "simplex",
176
+ ],
177
+ help="GUI theme setting supported by ttkbootstrap",
178
+ )
179
+
180
+
107
181
  class ConfigManager:
108
182
  """Manages loading, saving, and accessing configuration categories."""
109
183
 
@@ -115,7 +189,9 @@ class ConfigManager:
115
189
  ):
116
190
  self._categories: dict[str, ConfigCategory] = {}
117
191
  self._serializer = ConfigSerializer()
192
+ self.app: AppConfig = AppConfig()
118
193
 
194
+ categories = (self.app, *categories)
119
195
  for category in categories:
120
196
  if not isinstance(category, ConfigCategory):
121
197
  raise TypeError(f"Expected ConfigCategory instance, got {type(category)}")
@@ -163,6 +239,18 @@ class ConfigManager:
163
239
  data = yaml.safe_load(f) if path.suffix.lower() in [".yml", ".yaml"] else json.load(f)
164
240
 
165
241
  self._apply_config_data(data)
242
+ # Persist the information about the last used configuration file so the
243
+ # application can restore the same config on next start.
244
+ try:
245
+ persistence.write_last_used_config("config-cli-gui", str(path))
246
+ except Exception:
247
+ # Persistence should not break normal config loading.
248
+ pass
249
+ try:
250
+ logging.getLogger("config_cli_gui").info(f"Loaded configuration from: {path}")
251
+ except Exception:
252
+ # Non-fatal: logging may not be configured yet.
253
+ pass
166
254
 
167
255
  def _apply_config_data(self, data: dict[str, Any]) -> None:
168
256
  """Apply loaded data to the configuration parameters."""
@@ -201,6 +289,28 @@ class ConfigManager:
201
289
  if file_format == "yaml":
202
290
  self._append_comments_to_yaml(path)
203
291
 
292
+ # Record the saved config as the last-used config as well.
293
+ try:
294
+ persistence.write_last_used_config("config-cli-gui", str(path))
295
+ except Exception:
296
+ # Ignore persistence failures when saving
297
+ pass
298
+ try:
299
+ logging.getLogger("config_cli_gui").info(f"Saved configuration to: {path}")
300
+ except Exception:
301
+ # Non-fatal: logging may not be configured yet.
302
+ pass
303
+
304
+ def get_last_used_config(self) -> str | None:
305
+ """Return the last used config file path recorded by the persistence layer.
306
+
307
+ This is a convenience wrapper around persistence.read_last_used_config.
308
+ """
309
+ try:
310
+ return persistence.read_last_used_config("config-cli-gui")
311
+ except Exception:
312
+ return None
313
+
204
314
  def to_dict(self) -> dict[str, Any]:
205
315
  """Convert all configuration categories to a dictionary."""
206
316
  result: dict[str, dict[str, Any]] = {}
@@ -16,13 +16,15 @@ class DocumentationGenerator:
16
16
  def pad(s, width):
17
17
  return s + " " * (width - len(s))
18
18
 
19
- markdown_content = dedent("""
19
+ markdown_content = dedent(
20
+ """
20
21
  # Configuration Parameters
21
22
 
22
23
  These parameters are available to configure the behavior of your application.
23
24
  The parameters in the cli category can be accessed via the command line interface.
24
25
 
25
- """).lstrip()
26
+ """
27
+ ).lstrip()
26
28
 
27
29
  for category_name, category in self.config_manager._categories.items():
28
30
  markdown_content += f'## Category "{category_name}"\n\n'
@@ -165,13 +167,15 @@ class DocumentationGenerator:
165
167
  continue
166
168
  example_value = param.choices[0] if param.choices else param.value
167
169
  examples.append(
168
- dedent(f"""
170
+ dedent(
171
+ f"""
169
172
  ### {i}. With {param.name} parameter
170
173
 
171
174
  ```bash
172
175
  python -m {app_name} --{param.name} {example_value} {required_arg}
173
176
  ```
174
- """)
177
+ """
178
+ )
175
179
  )
176
180
 
177
181
  markdown = dedent(