pyvegh 0.7.0__tar.gz → 0.8.0__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.
- {pyvegh-0.7.0 → pyvegh-0.8.0}/Cargo.lock +1 -1
- {pyvegh-0.7.0 → pyvegh-0.8.0}/Cargo.toml +1 -1
- {pyvegh-0.7.0 → pyvegh-0.8.0}/PKG-INFO +18 -25
- {pyvegh-0.7.0 → pyvegh-0.8.0}/README.md +17 -24
- {pyvegh-0.7.0 → pyvegh-0.8.0}/pyproject.toml +1 -1
- {pyvegh-0.7.0 → pyvegh-0.8.0}/python/vegh/__init__.py +3 -1
- pyvegh-0.8.0/python/vegh/analytics.py +281 -0
- pyvegh-0.8.0/python/vegh/cli.py +6 -0
- pyvegh-0.7.0/python/vegh/cli.py → pyvegh-0.8.0/python/vegh/cli_commands.py +115 -581
- pyvegh-0.8.0/python/vegh/cli_config.py +81 -0
- pyvegh-0.8.0/python/vegh/cli_helpers.py +124 -0
- pyvegh-0.8.0/python/vegh/cli_hooks.py +48 -0
- pyvegh-0.8.0/python/vegh/cli_main.py +98 -0
- pyvegh-0.8.0/python/vegh/cli_repo.py +90 -0
- pyvegh-0.8.0/python/vegh/config.jsonc +1116 -0
- pyvegh-0.8.0/python/vegh/jsonc.py +53 -0
- {pyvegh-0.7.0 → pyvegh-0.8.0}/src/core.rs +11 -9
- {pyvegh-0.7.0 → pyvegh-0.8.0}/src/lib.rs +49 -20
- {pyvegh-0.7.0 → pyvegh-0.8.0}/src/storage.rs +39 -37
- pyvegh-0.7.0/python/vegh/analytics.py +0 -621
- {pyvegh-0.7.0 → pyvegh-0.8.0}/.github/workflows/ci.yml +0 -0
- {pyvegh-0.7.0 → pyvegh-0.8.0}/.github/workflows/release.yml +0 -0
- {pyvegh-0.7.0 → pyvegh-0.8.0}/.github/workflows/rust-clippy.yml +0 -0
- {pyvegh-0.7.0 → pyvegh-0.8.0}/.gitignore +0 -0
- {pyvegh-0.7.0 → pyvegh-0.8.0}/LICENSE +0 -0
- {pyvegh-0.7.0 → pyvegh-0.8.0}/src/hash.rs +0 -0
- {pyvegh-0.7.0 → pyvegh-0.8.0}/tests/integration_test.sh +0 -0
- {pyvegh-0.7.0 → pyvegh-0.8.0}/tests/test_smoke.py +0 -0
- {pyvegh-0.7.0 → pyvegh-0.8.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyvegh
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Classifier: Programming Language :: Python :: 3.10
|
|
5
5
|
Classifier: Programming Language :: Python :: 3.11
|
|
6
6
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -49,13 +49,16 @@ It delivers the raw performance of Rust (Zstd multithreaded compression, Tar arc
|
|
|
49
49
|
## Installation
|
|
50
50
|
|
|
51
51
|
Install directly from PyPI:
|
|
52
|
-
```
|
|
52
|
+
```shell
|
|
53
53
|
pip install pyvegh
|
|
54
|
+
|
|
55
|
+
# Or via uv
|
|
56
|
+
uv pip install pyvegh
|
|
54
57
|
```
|
|
55
58
|
|
|
56
59
|
Or build from source (requires Rust):
|
|
57
60
|
|
|
58
|
-
```
|
|
61
|
+
```shell
|
|
59
62
|
maturin develop --release
|
|
60
63
|
```
|
|
61
64
|
|
|
@@ -67,7 +70,7 @@ PyVegh provides a powerful command-line interface via the `vegh` (or `pyvegh`) c
|
|
|
67
70
|
|
|
68
71
|
Set up your default server URL and Auth Token so you don't have to type them every time.
|
|
69
72
|
|
|
70
|
-
```
|
|
73
|
+
```shell
|
|
71
74
|
vegh config
|
|
72
75
|
# Or one-liner:
|
|
73
76
|
vegh config send --url https://api.teaserverse.online/test --auth YOUR_TOKEN
|
|
@@ -83,7 +86,7 @@ vegh config reset
|
|
|
83
86
|
|
|
84
87
|
Pack a directory into a highly compressed snapshot.
|
|
85
88
|
|
|
86
|
-
```
|
|
89
|
+
```shell
|
|
87
90
|
# Basic snapshot
|
|
88
91
|
vegh snap ./my-project --output backup.vegh
|
|
89
92
|
|
|
@@ -95,7 +98,7 @@ vegh snap ./my-project --dry-run
|
|
|
95
98
|
|
|
96
99
|
View the Analytics Dashboard to break down your project by language and lines of code.
|
|
97
100
|
|
|
98
|
-
```
|
|
101
|
+
```shell
|
|
99
102
|
vegh loc backup.vegh
|
|
100
103
|
|
|
101
104
|
# Show Source Lines of Code (SLOC) instead of total LOC
|
|
@@ -106,7 +109,7 @@ vegh loc backup.vegh --sloc
|
|
|
106
109
|
### 4\. Prompt
|
|
107
110
|
|
|
108
111
|
Generate a structured XML context of your codebase to feed directly into ChatGPT, Claude, or Gemini.
|
|
109
|
-
```
|
|
112
|
+
```shell
|
|
110
113
|
# Generate XML context to stdout
|
|
111
114
|
vegh prompt .
|
|
112
115
|
|
|
@@ -125,7 +128,7 @@ vegh prompt . --clean --output context.xml
|
|
|
125
128
|
|
|
126
129
|
Clean up old snapshots to free disk space.
|
|
127
130
|
|
|
128
|
-
```
|
|
131
|
+
```shell
|
|
129
132
|
# Keep only the 5 most recent snapshots in the current directory
|
|
130
133
|
vegh prune --keep 5
|
|
131
134
|
|
|
@@ -137,7 +140,7 @@ vegh prune --keep 1 --force
|
|
|
137
140
|
|
|
138
141
|
Check file integrity (Blake3) and view embedded metadata.
|
|
139
142
|
|
|
140
|
-
```
|
|
143
|
+
```shell
|
|
141
144
|
vegh check backup.vegh
|
|
142
145
|
```
|
|
143
146
|
|
|
@@ -145,7 +148,7 @@ vegh check backup.vegh
|
|
|
145
148
|
|
|
146
149
|
Restore the snapshot to a target directory. Supports **Partial Restore**.
|
|
147
150
|
|
|
148
|
-
```
|
|
151
|
+
```shell
|
|
149
152
|
# Full restore
|
|
150
153
|
vegh restore backup.vegh ./restored-folder
|
|
151
154
|
|
|
@@ -160,7 +163,7 @@ vegh restore backup.vegh ./restored-folder --flatten
|
|
|
160
163
|
|
|
161
164
|
Inspect content without extracting.
|
|
162
165
|
|
|
163
|
-
```
|
|
166
|
+
```shell
|
|
164
167
|
# View a file's content inside the snapshot
|
|
165
168
|
vegh cat backup.vegh src/main.rs
|
|
166
169
|
|
|
@@ -175,7 +178,7 @@ vegh diff backup.vegh ./current-project
|
|
|
175
178
|
|
|
176
179
|
Send the snapshot to a remote server. Supports **Chunked Uploads** for reliability.
|
|
177
180
|
|
|
178
|
-
```
|
|
181
|
+
```shell
|
|
179
182
|
# Auto-detects if chunking is needed, or force it:
|
|
180
183
|
vegh send backup.vegh --force-chunk
|
|
181
184
|
```
|
|
@@ -184,7 +187,7 @@ vegh send backup.vegh --force-chunk
|
|
|
184
187
|
|
|
185
188
|
Check your environment and installation health.
|
|
186
189
|
|
|
187
|
-
```
|
|
190
|
+
```shell
|
|
188
191
|
vegh doctor
|
|
189
192
|
```
|
|
190
193
|
|
|
@@ -194,18 +197,8 @@ Create a `.veghhooks.json` in your workspace.
|
|
|
194
197
|
|
|
195
198
|
```json
|
|
196
199
|
{
|
|
197
|
-
"
|
|
198
|
-
|
|
199
|
-
"echo '🚀 Pre-snap hook started...'",
|
|
200
|
-
"echo 'Preparing to backup...'",
|
|
201
|
-
"echo 'Backing up database...'",
|
|
202
|
-
"echo 'Backup completed.'"
|
|
203
|
-
],
|
|
204
|
-
"post": [
|
|
205
|
-
"echo '🎉 Snapshot completed!'",
|
|
206
|
-
"echo 'Cleaning up...'"
|
|
207
|
-
]
|
|
208
|
-
}
|
|
200
|
+
"pre": ["echo 'Checking...'", "ruff check -e"],
|
|
201
|
+
"post": ["echo 'Clean up...'"]
|
|
209
202
|
}
|
|
210
203
|
```
|
|
211
204
|
|
|
@@ -21,13 +21,16 @@ It delivers the raw performance of Rust (Zstd multithreaded compression, Tar arc
|
|
|
21
21
|
## Installation
|
|
22
22
|
|
|
23
23
|
Install directly from PyPI:
|
|
24
|
-
```
|
|
24
|
+
```shell
|
|
25
25
|
pip install pyvegh
|
|
26
|
+
|
|
27
|
+
# Or via uv
|
|
28
|
+
uv pip install pyvegh
|
|
26
29
|
```
|
|
27
30
|
|
|
28
31
|
Or build from source (requires Rust):
|
|
29
32
|
|
|
30
|
-
```
|
|
33
|
+
```shell
|
|
31
34
|
maturin develop --release
|
|
32
35
|
```
|
|
33
36
|
|
|
@@ -39,7 +42,7 @@ PyVegh provides a powerful command-line interface via the `vegh` (or `pyvegh`) c
|
|
|
39
42
|
|
|
40
43
|
Set up your default server URL and Auth Token so you don't have to type them every time.
|
|
41
44
|
|
|
42
|
-
```
|
|
45
|
+
```shell
|
|
43
46
|
vegh config
|
|
44
47
|
# Or one-liner:
|
|
45
48
|
vegh config send --url https://api.teaserverse.online/test --auth YOUR_TOKEN
|
|
@@ -55,7 +58,7 @@ vegh config reset
|
|
|
55
58
|
|
|
56
59
|
Pack a directory into a highly compressed snapshot.
|
|
57
60
|
|
|
58
|
-
```
|
|
61
|
+
```shell
|
|
59
62
|
# Basic snapshot
|
|
60
63
|
vegh snap ./my-project --output backup.vegh
|
|
61
64
|
|
|
@@ -67,7 +70,7 @@ vegh snap ./my-project --dry-run
|
|
|
67
70
|
|
|
68
71
|
View the Analytics Dashboard to break down your project by language and lines of code.
|
|
69
72
|
|
|
70
|
-
```
|
|
73
|
+
```shell
|
|
71
74
|
vegh loc backup.vegh
|
|
72
75
|
|
|
73
76
|
# Show Source Lines of Code (SLOC) instead of total LOC
|
|
@@ -78,7 +81,7 @@ vegh loc backup.vegh --sloc
|
|
|
78
81
|
### 4\. Prompt
|
|
79
82
|
|
|
80
83
|
Generate a structured XML context of your codebase to feed directly into ChatGPT, Claude, or Gemini.
|
|
81
|
-
```
|
|
84
|
+
```shell
|
|
82
85
|
# Generate XML context to stdout
|
|
83
86
|
vegh prompt .
|
|
84
87
|
|
|
@@ -97,7 +100,7 @@ vegh prompt . --clean --output context.xml
|
|
|
97
100
|
|
|
98
101
|
Clean up old snapshots to free disk space.
|
|
99
102
|
|
|
100
|
-
```
|
|
103
|
+
```shell
|
|
101
104
|
# Keep only the 5 most recent snapshots in the current directory
|
|
102
105
|
vegh prune --keep 5
|
|
103
106
|
|
|
@@ -109,7 +112,7 @@ vegh prune --keep 1 --force
|
|
|
109
112
|
|
|
110
113
|
Check file integrity (Blake3) and view embedded metadata.
|
|
111
114
|
|
|
112
|
-
```
|
|
115
|
+
```shell
|
|
113
116
|
vegh check backup.vegh
|
|
114
117
|
```
|
|
115
118
|
|
|
@@ -117,7 +120,7 @@ vegh check backup.vegh
|
|
|
117
120
|
|
|
118
121
|
Restore the snapshot to a target directory. Supports **Partial Restore**.
|
|
119
122
|
|
|
120
|
-
```
|
|
123
|
+
```shell
|
|
121
124
|
# Full restore
|
|
122
125
|
vegh restore backup.vegh ./restored-folder
|
|
123
126
|
|
|
@@ -132,7 +135,7 @@ vegh restore backup.vegh ./restored-folder --flatten
|
|
|
132
135
|
|
|
133
136
|
Inspect content without extracting.
|
|
134
137
|
|
|
135
|
-
```
|
|
138
|
+
```shell
|
|
136
139
|
# View a file's content inside the snapshot
|
|
137
140
|
vegh cat backup.vegh src/main.rs
|
|
138
141
|
|
|
@@ -147,7 +150,7 @@ vegh diff backup.vegh ./current-project
|
|
|
147
150
|
|
|
148
151
|
Send the snapshot to a remote server. Supports **Chunked Uploads** for reliability.
|
|
149
152
|
|
|
150
|
-
```
|
|
153
|
+
```shell
|
|
151
154
|
# Auto-detects if chunking is needed, or force it:
|
|
152
155
|
vegh send backup.vegh --force-chunk
|
|
153
156
|
```
|
|
@@ -156,7 +159,7 @@ vegh send backup.vegh --force-chunk
|
|
|
156
159
|
|
|
157
160
|
Check your environment and installation health.
|
|
158
161
|
|
|
159
|
-
```
|
|
162
|
+
```shell
|
|
160
163
|
vegh doctor
|
|
161
164
|
```
|
|
162
165
|
|
|
@@ -166,18 +169,8 @@ Create a `.veghhooks.json` in your workspace.
|
|
|
166
169
|
|
|
167
170
|
```json
|
|
168
171
|
{
|
|
169
|
-
"
|
|
170
|
-
|
|
171
|
-
"echo '🚀 Pre-snap hook started...'",
|
|
172
|
-
"echo 'Preparing to backup...'",
|
|
173
|
-
"echo 'Backing up database...'",
|
|
174
|
-
"echo 'Backup completed.'"
|
|
175
|
-
],
|
|
176
|
-
"post": [
|
|
177
|
-
"echo '🎉 Snapshot completed!'",
|
|
178
|
-
"echo 'Cleaning up...'"
|
|
179
|
-
]
|
|
180
|
-
}
|
|
172
|
+
"pre": ["echo 'Checking...'", "ruff check -e"],
|
|
173
|
+
"post": ["echo 'Clean up...'"]
|
|
181
174
|
}
|
|
182
175
|
```
|
|
183
176
|
|
|
@@ -9,9 +9,10 @@ from ._core import (
|
|
|
9
9
|
get_metadata,
|
|
10
10
|
count_locs,
|
|
11
11
|
scan_locs_dir,
|
|
12
|
+
read_snapshot_text,
|
|
12
13
|
)
|
|
13
14
|
|
|
14
|
-
__version__ = "0.
|
|
15
|
+
__version__ = "0.8.0"
|
|
15
16
|
__all__ = [
|
|
16
17
|
"create_snap",
|
|
17
18
|
"dry_run_snap",
|
|
@@ -20,5 +21,6 @@ __all__ = [
|
|
|
20
21
|
"get_metadata",
|
|
21
22
|
"count_locs",
|
|
22
23
|
"scan_locs_dir",
|
|
24
|
+
"read_snapshot_text",
|
|
23
25
|
"__version__",
|
|
24
26
|
]
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import List, Tuple, Dict
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
from rich.table import Table
|
|
5
|
+
from rich.panel import Panel
|
|
6
|
+
from rich.text import Text
|
|
7
|
+
from rich import box
|
|
8
|
+
from rich.layout import Layout
|
|
9
|
+
from rich.align import Align
|
|
10
|
+
|
|
11
|
+
from .jsonc import parse
|
|
12
|
+
|
|
13
|
+
# Load configuration from JSON
|
|
14
|
+
config_path = Path(__file__).parent / "config.jsonc"
|
|
15
|
+
with open(config_path, "r", encoding="utf-8") as f:
|
|
16
|
+
config = parse(f.read())
|
|
17
|
+
|
|
18
|
+
LANG_MAP = {k: tuple(v) for k, v in config["LANG_MAP"].items()}
|
|
19
|
+
SLOC_IGNORE_EXTS = set(config["SLOC_IGNORE_EXTS"])
|
|
20
|
+
COMMENT_MAP = config["COMMENT_MAP"]
|
|
21
|
+
FILENAME_MAP = {k: tuple(v) for k, v in config["FILENAME_MAP"].items()}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ProjectStats:
|
|
25
|
+
def __init__(self):
|
|
26
|
+
self.total_files = 0
|
|
27
|
+
self.total_loc = 0
|
|
28
|
+
self.lang_stats: Dict[str, Dict] = {}
|
|
29
|
+
|
|
30
|
+
def add_file(self, path_str: str, loc: int):
|
|
31
|
+
self.total_files += 1
|
|
32
|
+
self.total_loc += loc
|
|
33
|
+
|
|
34
|
+
path = Path(path_str)
|
|
35
|
+
# .lower() handles both .s and .S
|
|
36
|
+
ext = path.suffix.lower()
|
|
37
|
+
name = path.name.lower()
|
|
38
|
+
|
|
39
|
+
# Identify Language
|
|
40
|
+
lang, color = "Other", "white"
|
|
41
|
+
|
|
42
|
+
if name in FILENAME_MAP:
|
|
43
|
+
lang, color = FILENAME_MAP[name]
|
|
44
|
+
elif ext in LANG_MAP:
|
|
45
|
+
lang, color = LANG_MAP[ext]
|
|
46
|
+
|
|
47
|
+
# Update Stats
|
|
48
|
+
if lang not in self.lang_stats:
|
|
49
|
+
self.lang_stats[lang] = {"files": 0, "loc": 0, "color": color}
|
|
50
|
+
|
|
51
|
+
self.lang_stats[lang]["files"] += 1
|
|
52
|
+
self.lang_stats[lang]["loc"] += loc
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _make_bar(label: str, percent: float, color: str, width: int = 30) -> Text:
|
|
56
|
+
"""Manually renders a progress bar using unicode blocks."""
|
|
57
|
+
filled_len = int((percent / 100.0) * width)
|
|
58
|
+
unfilled_len = width - filled_len
|
|
59
|
+
|
|
60
|
+
bar_str = ("█" * filled_len) + ("░" * unfilled_len)
|
|
61
|
+
|
|
62
|
+
text = Text()
|
|
63
|
+
text.append(f"{label:<20}", style=f"bold {color}")
|
|
64
|
+
text.append(f"{bar_str} ", style=color)
|
|
65
|
+
text.append(f"{percent:>5.1f}%", style="bold white")
|
|
66
|
+
return text
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def scan_sloc(path: str) -> List[Tuple[str, int]]:
|
|
70
|
+
"""Scans a directory for SLOC, using core dry_run_snap for traversal."""
|
|
71
|
+
# We need to import dry_run_snap here or pass it in.
|
|
72
|
+
# To avoid circular imports, we'll try to import it inside the function
|
|
73
|
+
from ._core import dry_run_snap
|
|
74
|
+
|
|
75
|
+
results = []
|
|
76
|
+
|
|
77
|
+
# Use dry_run_snap to get the file list (respecting .gitignore/veghignore)
|
|
78
|
+
# dry_run_snap returns (path, size). We just want the path.
|
|
79
|
+
files = dry_run_snap(path)
|
|
80
|
+
|
|
81
|
+
base_path = Path(path)
|
|
82
|
+
|
|
83
|
+
for relative_path, _ in files:
|
|
84
|
+
full_path = base_path / relative_path
|
|
85
|
+
sloc = calculate_sloc(str(full_path))
|
|
86
|
+
results.append((relative_path, sloc))
|
|
87
|
+
|
|
88
|
+
return results
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def count_sloc_from_text(content: str, ext: str) -> int:
|
|
92
|
+
"""Core logic to count SLOC from a string content."""
|
|
93
|
+
if ext in SLOC_IGNORE_EXTS:
|
|
94
|
+
return 0
|
|
95
|
+
|
|
96
|
+
comment_prefix = COMMENT_MAP.get(ext)
|
|
97
|
+
count = 0
|
|
98
|
+
|
|
99
|
+
# Process line by line
|
|
100
|
+
for line in content.splitlines():
|
|
101
|
+
line = line.strip()
|
|
102
|
+
if not line:
|
|
103
|
+
continue
|
|
104
|
+
if comment_prefix and line.startswith(comment_prefix):
|
|
105
|
+
continue
|
|
106
|
+
count += 1
|
|
107
|
+
|
|
108
|
+
return count
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def calculate_sloc(file_path: str) -> int:
|
|
112
|
+
"""Calculates Source Lines of Code (SLOC) from a file on disk."""
|
|
113
|
+
path = Path(file_path)
|
|
114
|
+
ext = path.suffix.lower()
|
|
115
|
+
|
|
116
|
+
if ext in SLOC_IGNORE_EXTS:
|
|
117
|
+
return 0
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
# Check if file is binary
|
|
121
|
+
with open(file_path, "rb") as f:
|
|
122
|
+
chunk = f.read(512)
|
|
123
|
+
if b'\x00' in chunk:
|
|
124
|
+
return 0
|
|
125
|
+
|
|
126
|
+
# Read file with error handling
|
|
127
|
+
with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
|
|
128
|
+
content = f.read()
|
|
129
|
+
return count_sloc_from_text(content, ext)
|
|
130
|
+
except Exception:
|
|
131
|
+
return 0
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def render_dashboard(
|
|
135
|
+
console: Console,
|
|
136
|
+
file_name: str,
|
|
137
|
+
raw_results: List[Tuple[str, int]],
|
|
138
|
+
metric_name: str = "LOC",
|
|
139
|
+
):
|
|
140
|
+
"""Draws the beautiful CodeTease Analytics Dashboard."""
|
|
141
|
+
|
|
142
|
+
# 1. Process Data
|
|
143
|
+
stats = ProjectStats()
|
|
144
|
+
for path, loc in raw_results:
|
|
145
|
+
if loc > 0:
|
|
146
|
+
stats.add_file(path, loc)
|
|
147
|
+
|
|
148
|
+
if stats.total_loc == 0:
|
|
149
|
+
console.print(
|
|
150
|
+
"[yellow]No code detected (or binary only). Is this a ghost project?[/yellow]"
|
|
151
|
+
)
|
|
152
|
+
return
|
|
153
|
+
|
|
154
|
+
sorted_langs = sorted(
|
|
155
|
+
stats.lang_stats.items(), key=lambda item: item[1]["loc"], reverse=True
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# 2. Build Layout
|
|
159
|
+
layout = Layout()
|
|
160
|
+
layout.split(
|
|
161
|
+
Layout(name="header", size=3),
|
|
162
|
+
Layout(name="body", ratio=1),
|
|
163
|
+
Layout(name="footer", size=3),
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
layout["body"].split_row(
|
|
167
|
+
Layout(name="left", ratio=1), Layout(name="right", ratio=1)
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# --- Header ---
|
|
171
|
+
title_text = Text(
|
|
172
|
+
f"📊 Vegh Analytics ({metric_name}): {file_name}",
|
|
173
|
+
style="bold white on blue",
|
|
174
|
+
justify="center",
|
|
175
|
+
)
|
|
176
|
+
layout["header"].update(Panel(title_text, box=box.HEAVY))
|
|
177
|
+
|
|
178
|
+
# --- Left: Detailed Table ---
|
|
179
|
+
table = Table(box=box.SIMPLE_HEAD, expand=True)
|
|
180
|
+
table.add_column("Lang", style="bold")
|
|
181
|
+
table.add_column("Files", justify="right")
|
|
182
|
+
table.add_column(metric_name, justify="right", style="green")
|
|
183
|
+
table.add_column("%", justify="right")
|
|
184
|
+
|
|
185
|
+
for lang, data in sorted_langs:
|
|
186
|
+
percent = (data["loc"] / stats.total_loc) * 100
|
|
187
|
+
table.add_row(
|
|
188
|
+
f"[{data['color']}]{lang}[/{data['color']}]",
|
|
189
|
+
str(data["files"]),
|
|
190
|
+
f"{data['loc']:,}",
|
|
191
|
+
f"{percent:.1f}%",
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
layout["left"].update(
|
|
195
|
+
Panel(table, title="[bold]Breakdown[/bold]", border_style="cyan")
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
# --- Right: Custom Manual Bar Chart ---
|
|
199
|
+
chart_content = Text()
|
|
200
|
+
|
|
201
|
+
# Take Top 12 languages
|
|
202
|
+
for i, (lang, data) in enumerate(sorted_langs[:12]):
|
|
203
|
+
percent = (data["loc"] / stats.total_loc) * 100
|
|
204
|
+
bar = _make_bar(lang, percent, data["color"])
|
|
205
|
+
chart_content.append(bar)
|
|
206
|
+
chart_content.append("\n")
|
|
207
|
+
|
|
208
|
+
if len(sorted_langs) > 12:
|
|
209
|
+
chart_content.append(
|
|
210
|
+
f"\n... and {len(sorted_langs) - 12} others", style="dim italic"
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
layout["right"].update(
|
|
214
|
+
Panel(
|
|
215
|
+
Align.center(chart_content, vertical="middle"),
|
|
216
|
+
title="[bold]Distribution[/bold]",
|
|
217
|
+
border_style="green",
|
|
218
|
+
)
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
# --- Footer: Summary & Fun Comment ---
|
|
222
|
+
if sorted_langs:
|
|
223
|
+
top_lang = sorted_langs[0][0]
|
|
224
|
+
else:
|
|
225
|
+
top_lang = "Other"
|
|
226
|
+
|
|
227
|
+
comment = "Code Hard, Play Hard! 🚀"
|
|
228
|
+
|
|
229
|
+
# Logic Fun Comment
|
|
230
|
+
if top_lang == "Rust":
|
|
231
|
+
comment = "Blazingly Fast! 🦀"
|
|
232
|
+
elif top_lang == "Python":
|
|
233
|
+
comment = "Snake Charmer! 🐍"
|
|
234
|
+
elif top_lang == "Haskell":
|
|
235
|
+
comment = "Purely Functional... and confusing! 😵💫"
|
|
236
|
+
elif top_lang == "Mojo":
|
|
237
|
+
comment = "AI Speedster! 🔥"
|
|
238
|
+
elif top_lang == "Solidity":
|
|
239
|
+
comment = "Wen Lambo? 🏎️"
|
|
240
|
+
elif top_lang == "Elixir":
|
|
241
|
+
comment = "Scalability God! 💜"
|
|
242
|
+
elif top_lang == "Astro":
|
|
243
|
+
comment = "To the stars! 🚀"
|
|
244
|
+
elif top_lang == "CSS":
|
|
245
|
+
comment = "Center a div? Good luck! 🎨"
|
|
246
|
+
elif "React" in top_lang:
|
|
247
|
+
comment = "Component Heaven! ⚛️"
|
|
248
|
+
elif top_lang in ["JavaScript", "TypeScript", "Vue", "Svelte"]:
|
|
249
|
+
comment = "Web Scale! 🌐"
|
|
250
|
+
elif top_lang in ["Assembly", "C", "C++"]:
|
|
251
|
+
comment = "Low Level Wizardry! 🧙♂️"
|
|
252
|
+
elif top_lang in ["FDON", "FWON", "BXSON"]:
|
|
253
|
+
comment = "Teasers! ⚡"
|
|
254
|
+
elif top_lang == "HTML":
|
|
255
|
+
comment = "How To Meet Ladies? 😉"
|
|
256
|
+
elif top_lang == "Go":
|
|
257
|
+
comment = "Gopher it! 🐹"
|
|
258
|
+
elif top_lang == "Java":
|
|
259
|
+
comment = "Enterprise Grade! ☕"
|
|
260
|
+
elif top_lang == "C#":
|
|
261
|
+
comment = "Microsoft Magic! 🪟"
|
|
262
|
+
elif top_lang == "PHP":
|
|
263
|
+
comment = "Elephant in the room! 🐘"
|
|
264
|
+
elif top_lang == "Swift":
|
|
265
|
+
comment = "Feeling Swift? 🍏"
|
|
266
|
+
elif top_lang == "Dart":
|
|
267
|
+
comment = "Fluttering away! 🐦"
|
|
268
|
+
elif top_lang == "SQL":
|
|
269
|
+
comment = "DROP TABLE production; 💀"
|
|
270
|
+
elif top_lang == "Terraform":
|
|
271
|
+
comment = "Infrastructure as Code! 🏗️"
|
|
272
|
+
elif top_lang == "Dockerfile":
|
|
273
|
+
comment = "Containerized! 🐳"
|
|
274
|
+
|
|
275
|
+
summary = f"[bold]Total {metric_name}:[/bold] [green]{stats.total_loc:,}[/green] | [bold]Analyzed Files:[/bold] {stats.total_files} | [italic]{comment}[/italic]"
|
|
276
|
+
|
|
277
|
+
layout["footer"].update(
|
|
278
|
+
Panel(Text.from_markup(summary, justify="center"), border_style="blue")
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
console.print(layout)
|