igntui 0.0.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.
- igntui/__init__.py +57 -0
- igntui/__main__.py +10 -0
- igntui/app.py +37 -0
- igntui/cli/__init__.py +31 -0
- igntui/cli/base.py +104 -0
- igntui/cli/commands/__init__.py +17 -0
- igntui/cli/commands/cache_cmd.py +95 -0
- igntui/cli/commands/generate_cmd.py +85 -0
- igntui/cli/commands/list_cmd.py +66 -0
- igntui/cli/commands/test_cmd.py +64 -0
- igntui/cli/commands/tui_cmd.py +34 -0
- igntui/cli/parser.py +162 -0
- igntui/cli/setup.py +71 -0
- igntui/core/__init__.py +8 -0
- igntui/core/api/__init__.py +16 -0
- igntui/core/api/client.py +222 -0
- igntui/core/api/errors.py +29 -0
- igntui/core/api/rate_limiter.py +30 -0
- igntui/core/api/request_handler.py +124 -0
- igntui/core/api/response.py +16 -0
- igntui/core/cache.py +284 -0
- igntui/core/config.py +174 -0
- igntui/core/search.py +354 -0
- igntui/main.py +74 -0
- igntui/tui/__init__.py +22 -0
- igntui/tui/actions.py +254 -0
- igntui/tui/app.py +246 -0
- igntui/tui/curses_setup.py +88 -0
- igntui/tui/event_handler.py +310 -0
- igntui/tui/lifecycle.py +102 -0
- igntui/tui/renderer.py +108 -0
- igntui/tui/state.py +77 -0
- igntui/ui/__init__.py +33 -0
- igntui/ui/components/__init__.py +31 -0
- igntui/ui/components/base_dialog.py +75 -0
- igntui/ui/components/base_panel.py +85 -0
- igntui/ui/components/confirm_dialog.py +100 -0
- igntui/ui/components/content_panel.py +77 -0
- igntui/ui/components/export_dialog.py +139 -0
- igntui/ui/components/help_dialog.py +161 -0
- igntui/ui/components/save_dialog.py +109 -0
- igntui/ui/components/search_panel.py +52 -0
- igntui/ui/components/selected_panel.py +107 -0
- igntui/ui/components/splash.py +236 -0
- igntui/ui/components/status_bar.py +78 -0
- igntui/ui/components/templates_panel.py +153 -0
- igntui/utils/__init__.py +19 -0
- igntui/utils/logging.py +288 -0
- igntui-0.0.0.dist-info/METADATA +425 -0
- igntui-0.0.0.dist-info/RECORD +52 -0
- igntui-0.0.0.dist-info/WHEEL +4 -0
- igntui-0.0.0.dist-info/entry_points.txt +4 -0
igntui/core/cache.py
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import logging
|
|
7
|
+
import os
|
|
8
|
+
import time
|
|
9
|
+
from dataclasses import asdict, dataclass
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from threading import RLock
|
|
12
|
+
from typing import Any, Dict, List, Optional, Set
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class CacheEntry:
|
|
19
|
+
data: Any
|
|
20
|
+
timestamp: float
|
|
21
|
+
ttl: int
|
|
22
|
+
access_count: int = 0
|
|
23
|
+
last_access: Optional[float] = None
|
|
24
|
+
|
|
25
|
+
def is_expired(self) -> bool:
|
|
26
|
+
return time.time() > (self.timestamp + self.ttl)
|
|
27
|
+
|
|
28
|
+
def touch(self) -> None:
|
|
29
|
+
self.access_count += 1
|
|
30
|
+
self.last_access = time.time()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class CacheManager:
|
|
34
|
+
def __init__(self, cache_dir: str, default_ttl: int = 3600):
|
|
35
|
+
self.cache_dir = Path(cache_dir)
|
|
36
|
+
self.default_ttl = default_ttl
|
|
37
|
+
self._memory_cache: Dict[str, CacheEntry] = {}
|
|
38
|
+
self._lock = RLock()
|
|
39
|
+
|
|
40
|
+
self._stats = {
|
|
41
|
+
"hits": 0,
|
|
42
|
+
"misses": 0,
|
|
43
|
+
"sets": 0,
|
|
44
|
+
"deletes": 0,
|
|
45
|
+
"evictions": 0,
|
|
46
|
+
"disk_reads": 0,
|
|
47
|
+
"disk_writes": 0,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
|
51
|
+
self._load_persistent_cache()
|
|
52
|
+
|
|
53
|
+
logger.debug(f"Initialized cache manager with directory: {self.cache_dir}")
|
|
54
|
+
|
|
55
|
+
def get(self, key: str) -> Optional[Any]:
|
|
56
|
+
with self._lock:
|
|
57
|
+
if key in self._memory_cache:
|
|
58
|
+
entry = self._memory_cache[key]
|
|
59
|
+
|
|
60
|
+
if entry.is_expired():
|
|
61
|
+
del self._memory_cache[key]
|
|
62
|
+
self._delete_disk_cache(key)
|
|
63
|
+
self._stats["evictions"] += 1
|
|
64
|
+
self._stats["misses"] += 1
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
entry.touch()
|
|
68
|
+
self._stats["hits"] += 1
|
|
69
|
+
logger.debug(f"Cache hit for key: {key}")
|
|
70
|
+
return entry.data
|
|
71
|
+
|
|
72
|
+
disk_entry = self._load_disk_cache(key)
|
|
73
|
+
if disk_entry and not disk_entry.is_expired():
|
|
74
|
+
self._memory_cache[key] = disk_entry
|
|
75
|
+
disk_entry.touch()
|
|
76
|
+
self._stats["hits"] += 1
|
|
77
|
+
logger.debug(f"Disk cache hit for key: {key}")
|
|
78
|
+
return disk_entry.data
|
|
79
|
+
elif disk_entry:
|
|
80
|
+
self._delete_disk_cache(key)
|
|
81
|
+
self._stats["evictions"] += 1
|
|
82
|
+
|
|
83
|
+
self._stats["misses"] += 1
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
def set(self, key: str, value: Any, ttl: Optional[int] = None) -> None:
|
|
87
|
+
ttl = ttl or self.default_ttl
|
|
88
|
+
|
|
89
|
+
with self._lock:
|
|
90
|
+
entry = CacheEntry(data=value, timestamp=time.time(), ttl=ttl)
|
|
91
|
+
|
|
92
|
+
self._memory_cache[key] = entry
|
|
93
|
+
self._save_disk_cache(key, entry)
|
|
94
|
+
self._stats["sets"] += 1
|
|
95
|
+
|
|
96
|
+
logger.debug(f"Cached value for key: {key} (TTL: {ttl}s)")
|
|
97
|
+
|
|
98
|
+
def delete(self, key: str) -> bool:
|
|
99
|
+
with self._lock:
|
|
100
|
+
deleted = False
|
|
101
|
+
|
|
102
|
+
if key in self._memory_cache:
|
|
103
|
+
del self._memory_cache[key]
|
|
104
|
+
deleted = True
|
|
105
|
+
|
|
106
|
+
if self._delete_disk_cache(key):
|
|
107
|
+
deleted = True
|
|
108
|
+
|
|
109
|
+
if deleted:
|
|
110
|
+
self._stats["deletes"] += 1
|
|
111
|
+
logger.debug(f"Deleted cache entry: {key}")
|
|
112
|
+
|
|
113
|
+
return deleted
|
|
114
|
+
|
|
115
|
+
def clear(self) -> int:
|
|
116
|
+
with self._lock:
|
|
117
|
+
memory_count = len(self._memory_cache)
|
|
118
|
+
self._memory_cache.clear()
|
|
119
|
+
disk_count = 0
|
|
120
|
+
for cache_file in self.cache_dir.glob("*.cache"):
|
|
121
|
+
try:
|
|
122
|
+
cache_file.unlink()
|
|
123
|
+
disk_count += 1
|
|
124
|
+
except OSError:
|
|
125
|
+
pass
|
|
126
|
+
|
|
127
|
+
total_cleared = memory_count + disk_count
|
|
128
|
+
logger.info(f"Cleared {total_cleared} cache entries")
|
|
129
|
+
return total_cleared
|
|
130
|
+
|
|
131
|
+
def cleanup_expired(self) -> int:
|
|
132
|
+
with self._lock:
|
|
133
|
+
expired_keys = []
|
|
134
|
+
|
|
135
|
+
for key, entry in self._memory_cache.items():
|
|
136
|
+
if entry.is_expired():
|
|
137
|
+
expired_keys.append(key)
|
|
138
|
+
|
|
139
|
+
for key in expired_keys:
|
|
140
|
+
del self._memory_cache[key]
|
|
141
|
+
self._delete_disk_cache(key)
|
|
142
|
+
|
|
143
|
+
disk_cleaned = 0
|
|
144
|
+
for cache_file in self.cache_dir.glob("*.cache"):
|
|
145
|
+
key = cache_file.stem
|
|
146
|
+
if key not in self._memory_cache:
|
|
147
|
+
entry = self._load_disk_cache(key)
|
|
148
|
+
if entry and entry.is_expired():
|
|
149
|
+
cache_file.unlink()
|
|
150
|
+
disk_cleaned += 1
|
|
151
|
+
|
|
152
|
+
total_cleaned = len(expired_keys) + disk_cleaned
|
|
153
|
+
self._stats["evictions"] += total_cleaned
|
|
154
|
+
|
|
155
|
+
if total_cleaned > 0:
|
|
156
|
+
logger.info(f"Cleaned up {total_cleaned} expired cache entries")
|
|
157
|
+
|
|
158
|
+
return total_cleaned
|
|
159
|
+
|
|
160
|
+
def get_stats(self) -> Dict[str, Any]:
|
|
161
|
+
with self._lock:
|
|
162
|
+
total_requests = self._stats["hits"] + self._stats["misses"]
|
|
163
|
+
hit_rate = self._stats["hits"] / max(1, total_requests)
|
|
164
|
+
|
|
165
|
+
memory_entries = len(self._memory_cache)
|
|
166
|
+
disk_entries = len(list(self.cache_dir.glob("*.cache")))
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
"hit_rate": hit_rate,
|
|
170
|
+
"total_requests": total_requests,
|
|
171
|
+
"memory_entries": memory_entries,
|
|
172
|
+
"disk_entries": disk_entries,
|
|
173
|
+
"cache_dir": str(self.cache_dir),
|
|
174
|
+
"default_ttl": self.default_ttl,
|
|
175
|
+
**self._stats,
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
def _load_persistent_cache(self) -> None:
|
|
179
|
+
try:
|
|
180
|
+
loaded_count = 0
|
|
181
|
+
for cache_file in self.cache_dir.glob("*.cache"):
|
|
182
|
+
key = cache_file.stem
|
|
183
|
+
entry = self._load_disk_cache(key)
|
|
184
|
+
|
|
185
|
+
if entry and not entry.is_expired():
|
|
186
|
+
self._memory_cache[key] = entry
|
|
187
|
+
loaded_count += 1
|
|
188
|
+
elif entry:
|
|
189
|
+
cache_file.unlink()
|
|
190
|
+
|
|
191
|
+
if loaded_count > 0:
|
|
192
|
+
logger.info(f"Loaded {loaded_count} cache entries from disk")
|
|
193
|
+
|
|
194
|
+
except Exception as e:
|
|
195
|
+
logger.warning(f"Failed to load persistent cache: {e}")
|
|
196
|
+
|
|
197
|
+
def _load_disk_cache(self, key: str) -> Optional[CacheEntry]:
|
|
198
|
+
cache_file = self.cache_dir / f"{key}.cache"
|
|
199
|
+
|
|
200
|
+
try:
|
|
201
|
+
if cache_file.exists():
|
|
202
|
+
with open(cache_file, "r", encoding="utf-8") as f:
|
|
203
|
+
data = json.load(f)
|
|
204
|
+
|
|
205
|
+
self._stats["disk_reads"] += 1
|
|
206
|
+
return CacheEntry(**data)
|
|
207
|
+
|
|
208
|
+
except (json.JSONDecodeError, TypeError, KeyError, OSError) as e:
|
|
209
|
+
logger.warning(f"Failed to load cache file {cache_file}: {e}")
|
|
210
|
+
try:
|
|
211
|
+
cache_file.unlink()
|
|
212
|
+
except OSError:
|
|
213
|
+
pass
|
|
214
|
+
|
|
215
|
+
return None
|
|
216
|
+
|
|
217
|
+
def _save_disk_cache(self, key: str, entry: CacheEntry) -> None:
|
|
218
|
+
cache_file = self.cache_dir / f"{key}.cache"
|
|
219
|
+
|
|
220
|
+
try:
|
|
221
|
+
with open(cache_file, "w", encoding="utf-8") as f:
|
|
222
|
+
json.dump(asdict(entry), f, separators=(",", ":"))
|
|
223
|
+
|
|
224
|
+
self._stats["disk_writes"] += 1
|
|
225
|
+
|
|
226
|
+
except (OSError, TypeError) as e:
|
|
227
|
+
logger.warning(f"Failed to save cache file {cache_file}: {e}")
|
|
228
|
+
|
|
229
|
+
def _delete_disk_cache(self, key: str) -> bool:
|
|
230
|
+
cache_file = self.cache_dir / f"{key}.cache"
|
|
231
|
+
|
|
232
|
+
try:
|
|
233
|
+
if cache_file.exists():
|
|
234
|
+
cache_file.unlink()
|
|
235
|
+
return True
|
|
236
|
+
except OSError:
|
|
237
|
+
pass
|
|
238
|
+
|
|
239
|
+
return False
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class TemplateCache:
|
|
243
|
+
def __init__(self, cache_manager: CacheManager):
|
|
244
|
+
self.cache_manager = cache_manager
|
|
245
|
+
self._template_list_key = "gitignore_templates_list"
|
|
246
|
+
self._template_content_prefix = "gitignore_content_"
|
|
247
|
+
|
|
248
|
+
def get_template_list(self) -> Optional[List[str]]:
|
|
249
|
+
return self.cache_manager.get(self._template_list_key)
|
|
250
|
+
|
|
251
|
+
def set_template_list(self, templates: List[str]) -> None:
|
|
252
|
+
self.cache_manager.set(self._template_list_key, templates)
|
|
253
|
+
|
|
254
|
+
def get_template_content(self, technologies: List[str]) -> Optional[str]:
|
|
255
|
+
key = self._make_content_key(technologies)
|
|
256
|
+
return self.cache_manager.get(key)
|
|
257
|
+
|
|
258
|
+
def set_template_content(self, technologies: List[str], content: str) -> None:
|
|
259
|
+
key = self._make_content_key(technologies)
|
|
260
|
+
self.cache_manager.set(key, content)
|
|
261
|
+
|
|
262
|
+
def invalidate_template_content(self, template_name: str) -> int:
|
|
263
|
+
invalidated = 0
|
|
264
|
+
|
|
265
|
+
for cache_file in self.cache_manager.cache_dir.glob(
|
|
266
|
+
f"{self._template_content_prefix}*.cache"
|
|
267
|
+
):
|
|
268
|
+
key = cache_file.stem
|
|
269
|
+
|
|
270
|
+
if template_name.lower() in key.lower():
|
|
271
|
+
if self.cache_manager.delete(key):
|
|
272
|
+
invalidated += 1
|
|
273
|
+
|
|
274
|
+
if invalidated > 0:
|
|
275
|
+
logger.info(
|
|
276
|
+
f"Invalidated {invalidated} cache entries for template: {template_name}"
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
return invalidated
|
|
280
|
+
|
|
281
|
+
def _make_content_key(self, technologies: List[str]) -> str:
|
|
282
|
+
sorted_techs = sorted([tech.lower().strip() for tech in technologies])
|
|
283
|
+
tech_string = ",".join(sorted_techs)
|
|
284
|
+
return f"{self._template_content_prefix}{hash(tech_string) % 1000000:06d}"
|
igntui/core/config.py
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import logging
|
|
7
|
+
import os
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any, Dict, Optional
|
|
10
|
+
|
|
11
|
+
from .. import __version__
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Config:
|
|
17
|
+
DEFAULT_CONFIG = {
|
|
18
|
+
"api": {
|
|
19
|
+
"base_url": "https://www.toptal.com/developers/gitignore/api",
|
|
20
|
+
"timeout": 10,
|
|
21
|
+
"user_agent": f"igntui/{__version__}",
|
|
22
|
+
"cache_ttl": 3600,
|
|
23
|
+
"retry_attempts": 3,
|
|
24
|
+
},
|
|
25
|
+
"ui": {
|
|
26
|
+
"theme": "default",
|
|
27
|
+
"mouse_support": True,
|
|
28
|
+
"auto_save": False,
|
|
29
|
+
"panel_layout": "four_panel",
|
|
30
|
+
"show_help": True,
|
|
31
|
+
"animation_speed": 150,
|
|
32
|
+
},
|
|
33
|
+
"behavior": {
|
|
34
|
+
"max_recent_templates": 10,
|
|
35
|
+
"auto_refresh_interval": 3600,
|
|
36
|
+
"fuzzy_search_threshold": 0.6,
|
|
37
|
+
"save_usage_stats": True,
|
|
38
|
+
"auto_backup": True,
|
|
39
|
+
"max_cache_entries": 1000,
|
|
40
|
+
},
|
|
41
|
+
"logging": {
|
|
42
|
+
"level": "INFO",
|
|
43
|
+
"file_enabled": True,
|
|
44
|
+
"console_enabled": False,
|
|
45
|
+
"max_file_size": 10485760,
|
|
46
|
+
"backup_count": 5,
|
|
47
|
+
},
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
def __init__(self, config_path: Optional[Path] = None):
|
|
51
|
+
self.config_path = config_path or Path.home() / ".igntui.json"
|
|
52
|
+
self._config = self.DEFAULT_CONFIG.copy()
|
|
53
|
+
self._load_config()
|
|
54
|
+
|
|
55
|
+
def _load_config(self) -> None:
|
|
56
|
+
if self.config_path.exists():
|
|
57
|
+
try:
|
|
58
|
+
with open(self.config_path, "r", encoding="utf-8") as f:
|
|
59
|
+
file_config = json.load(f)
|
|
60
|
+
self._merge_config(file_config)
|
|
61
|
+
logger.info(f"Loaded configuration from {self.config_path}")
|
|
62
|
+
except (json.JSONDecodeError, OSError) as e:
|
|
63
|
+
logger.warning(f"Failed to load config file: {e}")
|
|
64
|
+
|
|
65
|
+
self._load_env_overrides()
|
|
66
|
+
|
|
67
|
+
def _merge_config(self, new_config: Dict[str, Any]) -> None:
|
|
68
|
+
def merge_dict(base: Dict, override: Dict) -> Dict:
|
|
69
|
+
result = base.copy()
|
|
70
|
+
for key, value in override.items():
|
|
71
|
+
if (
|
|
72
|
+
key in result
|
|
73
|
+
and isinstance(result[key], dict)
|
|
74
|
+
and isinstance(value, dict)
|
|
75
|
+
):
|
|
76
|
+
result[key] = merge_dict(result[key], value)
|
|
77
|
+
else:
|
|
78
|
+
result[key] = value
|
|
79
|
+
return result
|
|
80
|
+
|
|
81
|
+
self._config = merge_dict(self._config, new_config)
|
|
82
|
+
|
|
83
|
+
def _load_env_overrides(self) -> None:
|
|
84
|
+
env_mappings = {
|
|
85
|
+
"IGNTUI_API_URL": ["api", "base_url"],
|
|
86
|
+
"IGNTUI_API_TIMEOUT": ["api", "timeout"],
|
|
87
|
+
"IGNTUI_CACHE_TTL": ["api", "cache_ttl"],
|
|
88
|
+
"IGNTUI_THEME": ["ui", "theme"],
|
|
89
|
+
"IGNTUI_MOUSE": ["ui", "mouse_support"],
|
|
90
|
+
"IGNTUI_LOG_LEVEL": ["logging", "level"],
|
|
91
|
+
"IGNTUI_MAX_RECENT": ["behavior", "max_recent_templates"],
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
for env_var, config_path in env_mappings.items():
|
|
95
|
+
value = os.getenv(env_var)
|
|
96
|
+
if value is not None:
|
|
97
|
+
try:
|
|
98
|
+
if value.lower() in ("true", "false"):
|
|
99
|
+
value = value.lower() == "true"
|
|
100
|
+
elif value.isdigit():
|
|
101
|
+
value = int(value)
|
|
102
|
+
elif value.replace(".", "").isdigit():
|
|
103
|
+
value = float(value)
|
|
104
|
+
|
|
105
|
+
config_section = self._config
|
|
106
|
+
for key in config_path[:-1]:
|
|
107
|
+
config_section = config_section[key]
|
|
108
|
+
config_section[config_path[-1]] = value
|
|
109
|
+
|
|
110
|
+
logger.debug(
|
|
111
|
+
f"Set {'.'.join(config_path)} = {value} from {env_var}"
|
|
112
|
+
)
|
|
113
|
+
except (ValueError, KeyError) as e:
|
|
114
|
+
logger.warning(f"Failed to set config from {env_var}: {e}")
|
|
115
|
+
|
|
116
|
+
def get(self, *keys: str, default: Any = None) -> Any:
|
|
117
|
+
try:
|
|
118
|
+
value = self._config
|
|
119
|
+
for key in keys:
|
|
120
|
+
value = value[key]
|
|
121
|
+
return value
|
|
122
|
+
except (KeyError, TypeError):
|
|
123
|
+
return default
|
|
124
|
+
|
|
125
|
+
def set(self, *keys: str, value: Any) -> None:
|
|
126
|
+
config_section = self._config
|
|
127
|
+
for key in keys[:-1]:
|
|
128
|
+
if key not in config_section:
|
|
129
|
+
config_section[key] = {}
|
|
130
|
+
config_section = config_section[key]
|
|
131
|
+
config_section[keys[-1]] = value
|
|
132
|
+
|
|
133
|
+
def save(self) -> None:
|
|
134
|
+
try:
|
|
135
|
+
self.config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
136
|
+
with open(self.config_path, "w", encoding="utf-8") as f:
|
|
137
|
+
json.dump(self._config, f, indent=2, sort_keys=True)
|
|
138
|
+
logger.info(f"Saved configuration to {self.config_path}")
|
|
139
|
+
except OSError as e:
|
|
140
|
+
logger.error(f"Failed to save configuration: {e}")
|
|
141
|
+
|
|
142
|
+
def reset_to_defaults(self) -> None:
|
|
143
|
+
self._config = self.DEFAULT_CONFIG.copy()
|
|
144
|
+
logger.info("Reset configuration to defaults")
|
|
145
|
+
|
|
146
|
+
def get_cache_dir(self) -> Path:
|
|
147
|
+
cache_dir = Path.home() / ".cache" / "igntui"
|
|
148
|
+
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
149
|
+
return cache_dir
|
|
150
|
+
|
|
151
|
+
def get_log_file(self) -> Path:
|
|
152
|
+
return Path.home() / ".igntui.log"
|
|
153
|
+
|
|
154
|
+
def get_usage_file(self) -> Path:
|
|
155
|
+
return Path.home() / ".igntui_usage.json"
|
|
156
|
+
|
|
157
|
+
@property
|
|
158
|
+
def api_config(self) -> Dict[str, Any]:
|
|
159
|
+
return self._config["api"]
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def ui_config(self) -> Dict[str, Any]:
|
|
163
|
+
return self._config["ui"]
|
|
164
|
+
|
|
165
|
+
@property
|
|
166
|
+
def behavior_config(self) -> Dict[str, Any]:
|
|
167
|
+
return self._config["behavior"]
|
|
168
|
+
|
|
169
|
+
@property
|
|
170
|
+
def logging_config(self) -> Dict[str, Any]:
|
|
171
|
+
return self._config["logging"]
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
config = Config()
|