vibetuner 2.7.0__tar.gz → 2.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.
Potentially problematic release.
This version of vibetuner might be problematic. Click here for more details.
- {vibetuner-2.7.0 → vibetuner-2.8.0}/PKG-INFO +2 -2
- {vibetuner-2.7.0 → vibetuner-2.8.0}/pyproject.toml +2 -2
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/config.py +7 -2
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/frontend/__init__.py +2 -2
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/frontend/middleware.py +1 -1
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/frontend/routes/health.py +4 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/mongo.py +2 -2
- vibetuner-2.8.0/src/vibetuner/paths.py +229 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates.py +9 -15
- vibetuner-2.7.0/src/vibetuner/paths.py +0 -112
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/__init__.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/__main__.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/cli/__init__.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/cli/run.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/context.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/frontend/AGENTS.md +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/frontend/CLAUDE.md +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/frontend/context.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/frontend/deps.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/frontend/email.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/frontend/hotreload.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/frontend/lifespan.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/frontend/oauth.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/frontend/routes/__init__.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/frontend/routes/auth.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/frontend/routes/debug.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/frontend/routes/language.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/frontend/routes/meta.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/frontend/routes/user.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/frontend/templates.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/logging.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/models/AGENTS.md +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/models/CLAUDE.md +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/models/__init__.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/models/blob.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/models/email_verification.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/models/mixins.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/models/oauth.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/models/registry.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/models/types.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/models/user.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/services/AGENTS.md +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/services/CLAUDE.md +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/services/__init__.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/services/blob.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/services/email.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/tasks/AGENTS.md +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/tasks/CLAUDE.md +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/tasks/__init__.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/tasks/context.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/tasks/worker.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/email/AGENTS.md +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/email/CLAUDE.md +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/email/default/magic_link.html.jinja +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/email/default/magic_link.txt.jinja +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/AGENTS.md +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/CLAUDE.md +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/base/favicons.html.jinja +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/base/footer.html.jinja +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/base/header.html.jinja +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/base/opengraph.html.jinja +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/base/skeleton.html.jinja +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/debug/collections.html.jinja +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/debug/components/debug_nav.html.jinja +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/debug/index.html.jinja +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/debug/info.html.jinja +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/debug/users.html.jinja +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/debug/version.html.jinja +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/email/magic_link.txt.jinja +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/email_sent.html.jinja +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/index.html.jinja +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/lang/select.html.jinja +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/login.html.jinja +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/meta/browserconfig.xml.jinja +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/meta/robots.txt.jinja +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/meta/site.webmanifest.jinja +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/meta/sitemap.xml.jinja +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/user/edit.html.jinja +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/user/profile.html.jinja +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/markdown/.placeholder +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/markdown/AGENTS.md +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/markdown/CLAUDE.md +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/time.py +0 -0
- {vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/versioning.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: vibetuner
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.8.0
|
|
4
4
|
Summary: Blessed Python dependencies for Vibetuner projects
|
|
5
5
|
Requires-Dist: aioboto3>=15.5.0
|
|
6
6
|
Requires-Dist: arel>=0.4.0
|
|
@@ -44,5 +44,5 @@ Requires-Dist: types-aioboto3[s3,ses]>=15.5.0 ; extra == 'dev'
|
|
|
44
44
|
Requires-Dist: types-authlib>=1.6.5.20251005 ; extra == 'dev'
|
|
45
45
|
Requires-Dist: types-pyyaml>=6.0.12.20250915 ; extra == 'dev'
|
|
46
46
|
Requires-Dist: uv-bump>=0.3.1 ; extra == 'dev'
|
|
47
|
-
Requires-Python: >=3.
|
|
47
|
+
Requires-Python: >=3.11
|
|
48
48
|
Provides-Extra: dev
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "vibetuner"
|
|
3
3
|
description = "Blessed Python dependencies for Vibetuner projects"
|
|
4
|
-
version = "2.
|
|
5
|
-
requires-python = ">=3.
|
|
4
|
+
version = "2.8.0"
|
|
5
|
+
requires-python = ">=3.11"
|
|
6
6
|
dependencies = [
|
|
7
7
|
# These are the base dependencies of AllTuner's blessed stack
|
|
8
8
|
"aioboto3>=15.5.0",
|
|
@@ -17,14 +17,19 @@ from pydantic import (
|
|
|
17
17
|
from pydantic_extra_types.language_code import LanguageAlpha2
|
|
18
18
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
19
19
|
|
|
20
|
-
from
|
|
21
|
-
from
|
|
20
|
+
from .paths import config_vars as config_vars_path
|
|
21
|
+
from .versioning import version
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
current_year: int = datetime.now().year
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
def _load_project_config() -> "ProjectConfiguration":
|
|
28
|
+
if config_vars_path is None:
|
|
29
|
+
raise RuntimeError(
|
|
30
|
+
"Project root not detected. Cannot load project configuration. "
|
|
31
|
+
"Ensure you're running from within a project directory with .copier-answers.yml"
|
|
32
|
+
)
|
|
28
33
|
if not config_vars_path.exists():
|
|
29
34
|
return ProjectConfiguration()
|
|
30
35
|
return ProjectConfiguration(
|
|
@@ -21,8 +21,8 @@ def register_router(router: APIRouter) -> None:
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
try:
|
|
24
|
-
import app.frontend.oauth as _app_oauth # noqa: F401
|
|
25
|
-
import app.frontend.routes as _app_routes # noqa: F401
|
|
24
|
+
import app.frontend.oauth as _app_oauth # noqa: F401 # type: ignore[unresolved-import]
|
|
25
|
+
import app.frontend.routes as _app_routes # noqa: F401 # type: ignore[unresolved-import]
|
|
26
26
|
except (ImportError, ModuleNotFoundError):
|
|
27
27
|
pass
|
|
28
28
|
|
|
@@ -66,7 +66,7 @@ def user_preference_selector(conn: HTTPConnection) -> str | None:
|
|
|
66
66
|
|
|
67
67
|
|
|
68
68
|
shared_translator = get_translator()
|
|
69
|
-
if locales_path.exists() and locales_path.is_dir():
|
|
69
|
+
if locales_path is not None and locales_path.exists() and locales_path.is_dir():
|
|
70
70
|
# Load translations from the locales directory
|
|
71
71
|
shared_translator.load_from_directories([locales_path])
|
|
72
72
|
|
|
@@ -22,6 +22,10 @@ def health_ping():
|
|
|
22
22
|
@router.get("/id")
|
|
23
23
|
def health_instance_id():
|
|
24
24
|
"""Instance identification endpoint for distinguishing app instances"""
|
|
25
|
+
if root_path is None:
|
|
26
|
+
raise RuntimeError(
|
|
27
|
+
"Project root not detected. Cannot provide instance information."
|
|
28
|
+
)
|
|
25
29
|
return {
|
|
26
30
|
"app": settings.project.project_slug,
|
|
27
31
|
"port": int(os.environ.get("PORT", 8000)),
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from beanie import init_beanie
|
|
2
2
|
from pymongo import AsyncMongoClient
|
|
3
3
|
|
|
4
|
-
from
|
|
5
|
-
from
|
|
4
|
+
from .config import settings
|
|
5
|
+
from .models.registry import get_all_models
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
async def init_models() -> None:
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
from importlib.resources import files
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Self
|
|
4
|
+
|
|
5
|
+
from pydantic import computed_field, model_validator
|
|
6
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# Package-relative paths (for bundled templates in the vibetuner package)
|
|
10
|
+
_package_files = files("vibetuner")
|
|
11
|
+
_package_templates_traversable = _package_files / "templates"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _get_package_templates_path() -> Path:
|
|
15
|
+
"""Get package templates path, works for both installed and editable installs."""
|
|
16
|
+
try:
|
|
17
|
+
return Path(str(_package_templates_traversable))
|
|
18
|
+
except (TypeError, ValueError):
|
|
19
|
+
raise RuntimeError(
|
|
20
|
+
"Package templates are in a non-filesystem location. "
|
|
21
|
+
"This is not yet supported."
|
|
22
|
+
) from None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Package templates always available
|
|
26
|
+
package_templates = _get_package_templates_path()
|
|
27
|
+
core_templates = package_templates # Alias for backwards compatibility
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class PathSettings(BaseSettings):
|
|
31
|
+
"""Path settings with lazy auto-detection of project root."""
|
|
32
|
+
|
|
33
|
+
model_config = SettingsConfigDict(
|
|
34
|
+
case_sensitive=False,
|
|
35
|
+
extra="ignore",
|
|
36
|
+
validate_default=True,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
root: Path | None = None
|
|
40
|
+
fallback_path: str = "defaults"
|
|
41
|
+
|
|
42
|
+
@model_validator(mode="after")
|
|
43
|
+
def detect_project_root(self) -> Self:
|
|
44
|
+
"""Auto-detect project root if not explicitly set."""
|
|
45
|
+
if self.root is None:
|
|
46
|
+
detected = self._find_project_root()
|
|
47
|
+
if detected is not None:
|
|
48
|
+
self.root = detected
|
|
49
|
+
return self
|
|
50
|
+
|
|
51
|
+
@staticmethod
|
|
52
|
+
def _find_project_root() -> Path | None:
|
|
53
|
+
"""Find project root by searching for marker files."""
|
|
54
|
+
markers = [".copier-answers.yml", "pyproject.toml", ".git"]
|
|
55
|
+
current = Path.cwd()
|
|
56
|
+
|
|
57
|
+
for parent in [current, *current.parents]:
|
|
58
|
+
if any((parent / marker).exists() for marker in markers):
|
|
59
|
+
return parent
|
|
60
|
+
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
# Project-specific paths (only available if root is set)
|
|
64
|
+
@computed_field
|
|
65
|
+
@property
|
|
66
|
+
def templates(self) -> Path | None:
|
|
67
|
+
"""Project templates directory."""
|
|
68
|
+
return self.root / "templates" if self.root else None
|
|
69
|
+
|
|
70
|
+
@computed_field
|
|
71
|
+
@property
|
|
72
|
+
def app_templates(self) -> Path | None:
|
|
73
|
+
"""Deprecated: use templates instead."""
|
|
74
|
+
return self.templates
|
|
75
|
+
|
|
76
|
+
@computed_field
|
|
77
|
+
@property
|
|
78
|
+
def locales(self) -> Path | None:
|
|
79
|
+
"""Project locales directory."""
|
|
80
|
+
return self.root / "locales" if self.root else None
|
|
81
|
+
|
|
82
|
+
@computed_field
|
|
83
|
+
@property
|
|
84
|
+
def config_vars(self) -> Path | None:
|
|
85
|
+
"""Copier answers file."""
|
|
86
|
+
return self.root / ".copier-answers.yml" if self.root else None
|
|
87
|
+
|
|
88
|
+
@computed_field
|
|
89
|
+
@property
|
|
90
|
+
def assets(self) -> Path | None:
|
|
91
|
+
"""Project assets directory."""
|
|
92
|
+
return self.root / "assets" if self.root else None
|
|
93
|
+
|
|
94
|
+
@computed_field
|
|
95
|
+
@property
|
|
96
|
+
def statics(self) -> Path | None:
|
|
97
|
+
"""Project static assets directory."""
|
|
98
|
+
return self.root / "assets" / "statics" if self.root else None
|
|
99
|
+
|
|
100
|
+
@computed_field
|
|
101
|
+
@property
|
|
102
|
+
def css(self) -> Path | None:
|
|
103
|
+
"""Project CSS directory."""
|
|
104
|
+
return self.root / "assets" / "statics" / "css" if self.root else None
|
|
105
|
+
|
|
106
|
+
@computed_field
|
|
107
|
+
@property
|
|
108
|
+
def js(self) -> Path | None:
|
|
109
|
+
"""Project JavaScript directory."""
|
|
110
|
+
return self.root / "assets" / "statics" / "js" if self.root else None
|
|
111
|
+
|
|
112
|
+
@computed_field
|
|
113
|
+
@property
|
|
114
|
+
def favicons(self) -> Path | None:
|
|
115
|
+
"""Project favicons directory."""
|
|
116
|
+
return self.root / "assets" / "statics" / "favicons" if self.root else None
|
|
117
|
+
|
|
118
|
+
@computed_field
|
|
119
|
+
@property
|
|
120
|
+
def img(self) -> Path | None:
|
|
121
|
+
"""Project images directory."""
|
|
122
|
+
return self.root / "assets" / "statics" / "img" if self.root else None
|
|
123
|
+
|
|
124
|
+
# Template paths (always return a list, project + package)
|
|
125
|
+
@computed_field
|
|
126
|
+
@property
|
|
127
|
+
def frontend_templates(self) -> list[Path]:
|
|
128
|
+
"""Frontend template search paths (project overrides, then package)."""
|
|
129
|
+
paths = []
|
|
130
|
+
if self.root:
|
|
131
|
+
project_path = self.root / "templates" / "frontend"
|
|
132
|
+
if project_path.exists():
|
|
133
|
+
paths.append(project_path)
|
|
134
|
+
paths.append(package_templates / "frontend")
|
|
135
|
+
return paths
|
|
136
|
+
|
|
137
|
+
@computed_field
|
|
138
|
+
@property
|
|
139
|
+
def email_templates(self) -> list[Path]:
|
|
140
|
+
"""Email template search paths (project overrides, then package)."""
|
|
141
|
+
paths = []
|
|
142
|
+
if self.root:
|
|
143
|
+
project_path = self.root / "templates" / "email"
|
|
144
|
+
if project_path.exists():
|
|
145
|
+
paths.append(project_path)
|
|
146
|
+
paths.append(package_templates / "email")
|
|
147
|
+
return paths
|
|
148
|
+
|
|
149
|
+
@computed_field
|
|
150
|
+
@property
|
|
151
|
+
def markdown_templates(self) -> list[Path]:
|
|
152
|
+
"""Markdown template search paths (project overrides, then package)."""
|
|
153
|
+
paths = []
|
|
154
|
+
if self.root:
|
|
155
|
+
project_path = self.root / "templates" / "markdown"
|
|
156
|
+
if project_path.exists():
|
|
157
|
+
paths.append(project_path)
|
|
158
|
+
paths.append(package_templates / "markdown")
|
|
159
|
+
return paths
|
|
160
|
+
|
|
161
|
+
def set_root(self, project_root: Path) -> None:
|
|
162
|
+
"""Explicitly set project root (overrides auto-detection)."""
|
|
163
|
+
self.root = project_root
|
|
164
|
+
|
|
165
|
+
def to_template_path_list(self, path: Path) -> list[Path]:
|
|
166
|
+
"""Convert path to list with fallback."""
|
|
167
|
+
return [path, path / self.fallback_path]
|
|
168
|
+
|
|
169
|
+
def fallback_static_default(self, static_type: str, file_name: str) -> Path:
|
|
170
|
+
"""Return a fallback path for a static file."""
|
|
171
|
+
if self.statics is None:
|
|
172
|
+
raise RuntimeError(
|
|
173
|
+
"Project root not detected. Cannot access static assets."
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
paths_to_check = [
|
|
177
|
+
self.statics / static_type / file_name,
|
|
178
|
+
self.statics / self.fallback_path / static_type / file_name,
|
|
179
|
+
]
|
|
180
|
+
|
|
181
|
+
for path in paths_to_check:
|
|
182
|
+
if path.exists():
|
|
183
|
+
return path
|
|
184
|
+
|
|
185
|
+
raise FileNotFoundError(
|
|
186
|
+
f"Could not find {file_name} in any of the fallback paths: {paths_to_check}"
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
# Global settings instance with lazy auto-detection
|
|
191
|
+
_settings = PathSettings()
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
# Backwards-compatible module-level API
|
|
195
|
+
def set_project_root(project_root: Path) -> None:
|
|
196
|
+
"""Set the project root directory explicitly."""
|
|
197
|
+
_settings.set_root(project_root)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def to_template_path_list(path: Path) -> list[Path]:
|
|
201
|
+
"""Convert path to list with fallback."""
|
|
202
|
+
return _settings.to_template_path_list(path)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def fallback_static_default(static_type: str, file_name: str) -> Path:
|
|
206
|
+
"""Return a fallback path for a static file."""
|
|
207
|
+
return _settings.fallback_static_default(static_type, file_name)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
# Expose settings instance for direct access
|
|
211
|
+
paths = _settings
|
|
212
|
+
|
|
213
|
+
# Module-level variables that delegate to settings (backwards compatibility)
|
|
214
|
+
# Access like: from vibetuner.paths import frontend_templates
|
|
215
|
+
# Or better: from vibetuner.paths import paths; paths.frontend_templates
|
|
216
|
+
root = _settings.root
|
|
217
|
+
templates = _settings.templates
|
|
218
|
+
app_templates = _settings.app_templates
|
|
219
|
+
locales = _settings.locales
|
|
220
|
+
config_vars = _settings.config_vars
|
|
221
|
+
assets = _settings.assets
|
|
222
|
+
statics = _settings.statics
|
|
223
|
+
css = _settings.css
|
|
224
|
+
js = _settings.js
|
|
225
|
+
favicons = _settings.favicons
|
|
226
|
+
img = _settings.img
|
|
227
|
+
frontend_templates = _settings.frontend_templates
|
|
228
|
+
email_templates = _settings.email_templates
|
|
229
|
+
markdown_templates = _settings.markdown_templates
|
|
@@ -3,13 +3,7 @@ from typing import Any, Dict, Optional
|
|
|
3
3
|
|
|
4
4
|
from jinja2 import Environment, FileSystemLoader, TemplateNotFound
|
|
5
5
|
|
|
6
|
-
from .
|
|
7
|
-
app_templates,
|
|
8
|
-
core_templates,
|
|
9
|
-
email_templates,
|
|
10
|
-
frontend_templates,
|
|
11
|
-
markdown_templates,
|
|
12
|
-
)
|
|
6
|
+
from . import paths
|
|
13
7
|
|
|
14
8
|
|
|
15
9
|
def _get_base_paths_for_namespace(
|
|
@@ -22,19 +16,19 @@ def _get_base_paths_for_namespace(
|
|
|
22
16
|
|
|
23
17
|
# Map known namespaces to their predefined paths
|
|
24
18
|
if namespace == "email":
|
|
25
|
-
return email_templates
|
|
19
|
+
return paths.email_templates
|
|
26
20
|
if namespace == "markdown":
|
|
27
|
-
return markdown_templates
|
|
21
|
+
return paths.markdown_templates
|
|
28
22
|
if namespace == "frontend":
|
|
29
|
-
return frontend_templates
|
|
23
|
+
return paths.frontend_templates
|
|
30
24
|
|
|
31
25
|
# Default for unknown or None namespace
|
|
32
26
|
# Only include app_templates if project root has been set
|
|
33
|
-
|
|
34
|
-
if app_templates is not None:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return
|
|
27
|
+
path_list = []
|
|
28
|
+
if paths.app_templates is not None:
|
|
29
|
+
path_list.append(paths.app_templates)
|
|
30
|
+
path_list.append(paths.core_templates)
|
|
31
|
+
return path_list
|
|
38
32
|
|
|
39
33
|
|
|
40
34
|
def _build_search_paths(
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
from importlib.resources import files
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
|
|
4
|
-
# Package-relative paths (for bundled templates in the vibetuner package)
|
|
5
|
-
_package_files = files("vibetuner")
|
|
6
|
-
_package_templates_traversable = _package_files / "templates"
|
|
7
|
-
|
|
8
|
-
# Convert to Path when actually used (handles both filesystem and zip-based packages)
|
|
9
|
-
def _get_package_templates_path() -> Path:
|
|
10
|
-
"""Get package templates path, works for both installed and editable installs."""
|
|
11
|
-
# For most cases, we can convert directly to Path
|
|
12
|
-
# For zip files, importlib.resources handles extraction automatically
|
|
13
|
-
try:
|
|
14
|
-
return Path(str(_package_templates_traversable))
|
|
15
|
-
except (TypeError, ValueError):
|
|
16
|
-
# If we can't convert to Path, we're in a zip or similar
|
|
17
|
-
# In this case, we'll need to use as_file() context manager when accessing
|
|
18
|
-
# For now, raise an error - we can enhance this later if needed
|
|
19
|
-
raise RuntimeError(
|
|
20
|
-
"Package templates are in a non-filesystem location. "
|
|
21
|
-
"This is not yet supported."
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
package_templates = _get_package_templates_path()
|
|
26
|
-
|
|
27
|
-
# Project root (set at runtime by the application using vibetuner)
|
|
28
|
-
# When None, only package templates are available
|
|
29
|
-
root: Path | None = None
|
|
30
|
-
fallback_path = "defaults"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def set_project_root(project_root: Path) -> None:
|
|
34
|
-
"""Set the project root directory for the application using vibetuner.
|
|
35
|
-
|
|
36
|
-
This enables access to project-specific templates, assets, and locales.
|
|
37
|
-
Must be called before accessing project-specific paths.
|
|
38
|
-
"""
|
|
39
|
-
global root, templates, app_templates, locales, config_vars
|
|
40
|
-
global assets, statics, css, js, favicons, img
|
|
41
|
-
global frontend_templates, email_templates, markdown_templates
|
|
42
|
-
|
|
43
|
-
root = project_root
|
|
44
|
-
|
|
45
|
-
# Update project-specific paths
|
|
46
|
-
templates = root / "templates"
|
|
47
|
-
app_templates = templates # Deprecated: projects now use templates/ directly
|
|
48
|
-
locales = root / "locales"
|
|
49
|
-
config_vars = root / ".copier-answers.yml"
|
|
50
|
-
|
|
51
|
-
# Update asset paths
|
|
52
|
-
assets = root / "assets"
|
|
53
|
-
statics = assets / "statics"
|
|
54
|
-
css = statics / "css"
|
|
55
|
-
js = statics / "js"
|
|
56
|
-
favicons = statics / "favicons"
|
|
57
|
-
img = statics / "img"
|
|
58
|
-
|
|
59
|
-
# Update template lists to include project overrides
|
|
60
|
-
frontend_templates = [templates / "frontend", package_templates / "frontend"]
|
|
61
|
-
email_templates = [templates / "email", package_templates / "email"]
|
|
62
|
-
markdown_templates = [templates / "markdown", package_templates / "markdown"]
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def to_template_path_list(path: Path) -> list[Path]:
|
|
66
|
-
return [
|
|
67
|
-
path,
|
|
68
|
-
path / fallback_path,
|
|
69
|
-
]
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def fallback_static_default(static_type: str, file_name: str) -> Path:
|
|
73
|
-
"""Return a fallback path for a file."""
|
|
74
|
-
if root is None:
|
|
75
|
-
raise RuntimeError(
|
|
76
|
-
"Project root not set. Call set_project_root() before accessing assets."
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
paths_to_check = [
|
|
80
|
-
statics / static_type / file_name,
|
|
81
|
-
statics / fallback_path / static_type / file_name,
|
|
82
|
-
]
|
|
83
|
-
|
|
84
|
-
for path in paths_to_check:
|
|
85
|
-
if path.exists():
|
|
86
|
-
return path
|
|
87
|
-
|
|
88
|
-
raise FileNotFoundError(
|
|
89
|
-
f"Could not find {file_name} in any of the fallback paths: {paths_to_check}"
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
# Core templates point to package (always available)
|
|
94
|
-
core_templates = package_templates
|
|
95
|
-
|
|
96
|
-
# Template paths - initially only package templates, updated when set_project_root() is called
|
|
97
|
-
frontend_templates = [package_templates / "frontend"]
|
|
98
|
-
email_templates = [package_templates / "email"]
|
|
99
|
-
markdown_templates = [package_templates / "markdown"]
|
|
100
|
-
|
|
101
|
-
# Project-specific paths - will be None until set_project_root() is called
|
|
102
|
-
# These get updated by set_project_root()
|
|
103
|
-
templates: Path | None = None
|
|
104
|
-
app_templates: Path | None = None
|
|
105
|
-
locales: Path | None = None
|
|
106
|
-
config_vars: Path | None = None
|
|
107
|
-
assets: Path | None = None
|
|
108
|
-
statics: Path | None = None
|
|
109
|
-
css: Path | None = None
|
|
110
|
-
js: Path | None = None
|
|
111
|
-
favicons: Path | None = None
|
|
112
|
-
img: Path | None = None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/email/default/magic_link.html.jinja
RENAMED
|
File without changes
|
{vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/email/default/magic_link.txt.jinja
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/base/favicons.html.jinja
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/base/opengraph.html.jinja
RENAMED
|
File without changes
|
{vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/base/skeleton.html.jinja
RENAMED
|
File without changes
|
{vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/debug/collections.html.jinja
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/debug/version.html.jinja
RENAMED
|
File without changes
|
{vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/email/magic_link.txt.jinja
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/meta/browserconfig.xml.jinja
RENAMED
|
File without changes
|
|
File without changes
|
{vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/meta/site.webmanifest.jinja
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vibetuner-2.7.0 → vibetuner-2.8.0}/src/vibetuner/templates/frontend/user/profile.html.jinja
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|