create-awesome-python-app 0.2.3__py3-none-any.whl → 0.2.4__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.
- create_awesome_python_app/__init__.py +1 -1
- create_awesome_python_app/catalog.py +26 -18
- create_awesome_python_app/prompt_style.py +76 -24
- {create_awesome_python_app-0.2.3.dist-info → create_awesome_python_app-0.2.4.dist-info}/METADATA +2 -2
- create_awesome_python_app-0.2.4.dist-info/RECORD +9 -0
- create_awesome_python_app-0.2.3.dist-info/RECORD +0 -9
- {create_awesome_python_app-0.2.3.dist-info → create_awesome_python_app-0.2.4.dist-info}/WHEEL +0 -0
- {create_awesome_python_app-0.2.3.dist-info → create_awesome_python_app-0.2.4.dist-info}/entry_points.txt +0 -0
|
@@ -16,7 +16,10 @@ from rich.console import Console
|
|
|
16
16
|
from rich.table import Table
|
|
17
17
|
|
|
18
18
|
from create_awesome_python_app import __version__
|
|
19
|
-
from create_awesome_python_app.prompt_style import
|
|
19
|
+
from create_awesome_python_app.prompt_style import (
|
|
20
|
+
custom_template_title,
|
|
21
|
+
template_title_tokens,
|
|
22
|
+
)
|
|
20
23
|
|
|
21
24
|
console = Console(stderr=True)
|
|
22
25
|
|
|
@@ -27,7 +30,7 @@ CUSTOM_TEMPLATE_SENTINEL = "__custom_template__"
|
|
|
27
30
|
class TemplateChoice:
|
|
28
31
|
"""Searchable interactive template choice."""
|
|
29
32
|
|
|
30
|
-
title:
|
|
33
|
+
title: Any
|
|
31
34
|
value: str
|
|
32
35
|
search: str
|
|
33
36
|
|
|
@@ -236,35 +239,40 @@ def build_template_choices(data: dict[str, Any]) -> list[TemplateChoice]:
|
|
|
236
239
|
category_name = categories.get(category_slug, category_slug)
|
|
237
240
|
badge = short_category_label(category_name).ljust(10)[:10]
|
|
238
241
|
slug = str(template.get("slug", ""))
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
242
|
+
labels_raw = template.get("labels", [])
|
|
243
|
+
labels = (
|
|
244
|
+
[str(label) for label in labels_raw[:3]]
|
|
245
|
+
if isinstance(labels_raw, list)
|
|
246
|
+
else []
|
|
247
|
+
)
|
|
243
248
|
description = str(template.get("description", "")).strip()
|
|
244
|
-
# Keep slug + short description in the title so select(use_search_filter)
|
|
245
|
-
# can match them (filter scans Choice.title only).
|
|
246
|
-
description_suffix = dim(f" — {description}") if description else ""
|
|
247
249
|
name = str(template.get("name", slug))
|
|
248
|
-
|
|
249
|
-
#
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
250
|
+
search = _search_text(template, category_name)
|
|
251
|
+
# FormattedText tokens (not raw ANSI): select() prints str titles
|
|
252
|
+
# literally, which showed ^[[1;94m… in terminals.
|
|
253
|
+
title = template_title_tokens(
|
|
254
|
+
category_slug=category_slug,
|
|
255
|
+
badge=badge,
|
|
256
|
+
name=name,
|
|
257
|
+
slug=slug,
|
|
258
|
+
labels=labels,
|
|
259
|
+
description=description,
|
|
260
|
+
search=search,
|
|
254
261
|
)
|
|
255
262
|
choices.append(
|
|
256
263
|
TemplateChoice(
|
|
257
264
|
title=title,
|
|
258
265
|
value=template_url,
|
|
259
|
-
search=
|
|
266
|
+
search=search,
|
|
260
267
|
)
|
|
261
268
|
)
|
|
262
269
|
|
|
270
|
+
custom_search = "custom own template url github file"
|
|
263
271
|
choices.append(
|
|
264
272
|
TemplateChoice(
|
|
265
|
-
title=
|
|
273
|
+
title=custom_template_title(custom_search),
|
|
266
274
|
value=CUSTOM_TEMPLATE_SENTINEL,
|
|
267
|
-
search=
|
|
275
|
+
search=custom_search,
|
|
268
276
|
)
|
|
269
277
|
)
|
|
270
278
|
return choices
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
|
+
from typing import Any
|
|
6
7
|
|
|
7
8
|
from questionary import Style
|
|
8
9
|
|
|
@@ -25,37 +26,88 @@ CPA_PROMPT_STYLE = Style.from_dict(
|
|
|
25
26
|
}
|
|
26
27
|
)
|
|
27
28
|
|
|
29
|
+
# Bright prompt_toolkit style strings (not raw ANSI — select() prints str titles
|
|
30
|
+
# literally, so escapes show as ^[[...m unless titles are FormattedText tokens).
|
|
31
|
+
_CATEGORY_STYLES = (
|
|
32
|
+
"fg:#facc15 bold", # yellow
|
|
33
|
+
"fg:#4ade80 bold", # green
|
|
34
|
+
"fg:#22d3ee bold", # cyan
|
|
35
|
+
"fg:#e879f9 bold", # magenta
|
|
36
|
+
"fg:#60a5fa bold", # blue
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class SearchableFormattedText(list):
|
|
41
|
+
"""FormattedText tokens with ``.lower()`` for questionary search filter.
|
|
42
|
+
|
|
43
|
+
``select(use_search_filter=True)`` does ``needle in choice.title.lower()``.
|
|
44
|
+
A plain token list has no ``.lower()``; this keeps filter working while
|
|
45
|
+
titles render as styled FormattedText.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(self, tokens: list[tuple[str, str]], search: str) -> None:
|
|
49
|
+
super().__init__(tokens)
|
|
50
|
+
self._search = search
|
|
51
|
+
|
|
52
|
+
def lower(self) -> str:
|
|
53
|
+
return self._search.lower()
|
|
54
|
+
|
|
28
55
|
|
|
29
56
|
def colors_enabled() -> bool:
|
|
30
57
|
return not os.environ.get("NO_COLOR")
|
|
31
58
|
|
|
32
59
|
|
|
33
|
-
def
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return text
|
|
37
|
-
return f"\033[{code}m{text}\033[0m"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
# Bold bright ANSI — readable on dark terminals; select() renders these safely
|
|
41
|
-
# (unlike autocomplete, which HTML-parses choice text).
|
|
42
|
-
_CATEGORY_PALETTE = (
|
|
43
|
-
"1;93", # bright yellow
|
|
44
|
-
"1;92", # bright green
|
|
45
|
-
"1;96", # bright cyan
|
|
46
|
-
"1;95", # bright magenta
|
|
47
|
-
"1;94", # bright blue
|
|
48
|
-
)
|
|
49
|
-
|
|
60
|
+
def category_style(slug: str) -> str:
|
|
61
|
+
idx = sum(ord(char) for char in slug) % len(_CATEGORY_STYLES)
|
|
62
|
+
return _CATEGORY_STYLES[idx]
|
|
50
63
|
|
|
51
|
-
def color_category(slug: str, label: str) -> str:
|
|
52
|
-
idx = sum(ord(char) for char in slug) % len(_CATEGORY_PALETTE)
|
|
53
|
-
return ansi(_CATEGORY_PALETTE[idx], label)
|
|
54
64
|
|
|
65
|
+
def plain_title_text(title: Any) -> str:
|
|
66
|
+
"""Join FormattedText token text (or return a plain string title)."""
|
|
67
|
+
if isinstance(title, list):
|
|
68
|
+
return "".join(str(token[1]) for token in title)
|
|
69
|
+
return str(title)
|
|
55
70
|
|
|
56
|
-
def bold(text: str) -> str:
|
|
57
|
-
return ansi("1", text)
|
|
58
71
|
|
|
72
|
+
def template_title_tokens(
|
|
73
|
+
*,
|
|
74
|
+
category_slug: str,
|
|
75
|
+
badge: str,
|
|
76
|
+
name: str,
|
|
77
|
+
slug: str,
|
|
78
|
+
labels: list[str],
|
|
79
|
+
description: str,
|
|
80
|
+
search: str,
|
|
81
|
+
) -> SearchableFormattedText | str:
|
|
82
|
+
"""Build a select-safe title: FormattedText when colors on, else plain str."""
|
|
83
|
+
label_suffix = ""
|
|
84
|
+
if labels:
|
|
85
|
+
label_suffix = " · " + ", ".join(labels[:3])
|
|
86
|
+
description_suffix = f" — {description}" if description else ""
|
|
87
|
+
plain = f"{badge} {name} ({slug}){label_suffix}{description_suffix}"
|
|
59
88
|
|
|
60
|
-
|
|
61
|
-
|
|
89
|
+
if not colors_enabled():
|
|
90
|
+
return plain
|
|
91
|
+
|
|
92
|
+
tokens: list[tuple[str, str]] = [
|
|
93
|
+
(category_style(category_slug), badge),
|
|
94
|
+
("", " "),
|
|
95
|
+
("bold", name),
|
|
96
|
+
("class:instruction", f" ({slug})"),
|
|
97
|
+
]
|
|
98
|
+
if label_suffix:
|
|
99
|
+
tokens.append(("class:instruction", label_suffix))
|
|
100
|
+
if description_suffix:
|
|
101
|
+
tokens.append(("fg:#94a3b8", description_suffix))
|
|
102
|
+
return SearchableFormattedText(tokens, search=search or plain)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def custom_template_title(search: str) -> SearchableFormattedText | str:
|
|
106
|
+
label = "Use my own template URL"
|
|
107
|
+
plain = " " * 12 + label
|
|
108
|
+
if not colors_enabled():
|
|
109
|
+
return plain
|
|
110
|
+
return SearchableFormattedText(
|
|
111
|
+
[("", " " * 12), ("italic fg:#94a3b8", label)],
|
|
112
|
+
search=search or plain,
|
|
113
|
+
)
|
{create_awesome_python_app-0.2.3.dist-info → create_awesome_python_app-0.2.4.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: create-awesome-python-app
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: Composable scaffolding CLI for production-ready Python apps
|
|
5
5
|
Project-URL: Homepage, https://github.com/Create-Python-App/create-python-app
|
|
6
6
|
Project-URL: Repository, https://github.com/Create-Python-App/create-python-app
|
|
@@ -8,7 +8,7 @@ Project-URL: Issues, https://github.com/Create-Python-App/create-python-app/issu
|
|
|
8
8
|
Project-URL: Changelog, https://github.com/Create-Python-App/create-python-app/blob/main/CHANGELOG.md
|
|
9
9
|
License-Expression: MIT
|
|
10
10
|
Requires-Python: >=3.12
|
|
11
|
-
Requires-Dist: create-python-app-core>=0.2.
|
|
11
|
+
Requires-Dist: create-python-app-core>=0.2.4
|
|
12
12
|
Requires-Dist: questionary>=2.1.1
|
|
13
13
|
Requires-Dist: rich>=15.0.0
|
|
14
14
|
Requires-Dist: typer>=0.27.0
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
create_awesome_python_app/__init__.py,sha256=D04aKhr6EFJUIRigne5PNis9DoItI8X-4SC6lK_VfJE,60
|
|
2
|
+
create_awesome_python_app/cache.py,sha256=eHKUlunGexZKYJo0HDNoBoOVq0DjLyRYgEe_wUh4Fk0,11812
|
|
3
|
+
create_awesome_python_app/catalog.py,sha256=w5GS_kQ6MM37AilutXbYZ9Hk5JGa_12crzI0LYzKfRg,16821
|
|
4
|
+
create_awesome_python_app/cli.py,sha256=6og82WI-RZxTqHETqrieA8relgppwCqFntOEmOFrIPk,21195
|
|
5
|
+
create_awesome_python_app/prompt_style.py,sha256=pERX1qPQSLZXeJvPVFC7z85dvte73ZV4iuENPQduheE,3463
|
|
6
|
+
create_awesome_python_app-0.2.4.dist-info/METADATA,sha256=KD7yBBBomrwpw4hfqJm9aIBGWnjBukcHl48bOqjt8_M,1428
|
|
7
|
+
create_awesome_python_app-0.2.4.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
8
|
+
create_awesome_python_app-0.2.4.dist-info/entry_points.txt,sha256=rszFbUjY-lvMDZ96zX1lALJwgD1mI3CkUuNyF2qqjYo,81
|
|
9
|
+
create_awesome_python_app-0.2.4.dist-info/RECORD,,
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
create_awesome_python_app/__init__.py,sha256=DPsQbeW1110h7DEbWLxV7pLEEDAUnc5WkLiBamNsjSU,60
|
|
2
|
-
create_awesome_python_app/cache.py,sha256=eHKUlunGexZKYJo0HDNoBoOVq0DjLyRYgEe_wUh4Fk0,11812
|
|
3
|
-
create_awesome_python_app/catalog.py,sha256=_x6TqYAVlURM1dbNsflB9I4g8ku_anZzuK_ggN_OvKs,16909
|
|
4
|
-
create_awesome_python_app/cli.py,sha256=6og82WI-RZxTqHETqrieA8relgppwCqFntOEmOFrIPk,21195
|
|
5
|
-
create_awesome_python_app/prompt_style.py,sha256=A1RQn2hX7khu21bNL5KfD1ZNAHUJfCzmxXIRMgin658,1670
|
|
6
|
-
create_awesome_python_app-0.2.3.dist-info/METADATA,sha256=Kvw2BT6Ff3YF3ZkH8DVJd5uSFyepcGGlMBqX6fgJAp4,1428
|
|
7
|
-
create_awesome_python_app-0.2.3.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
8
|
-
create_awesome_python_app-0.2.3.dist-info/entry_points.txt,sha256=rszFbUjY-lvMDZ96zX1lALJwgD1mI3CkUuNyF2qqjYo,81
|
|
9
|
-
create_awesome_python_app-0.2.3.dist-info/RECORD,,
|
{create_awesome_python_app-0.2.3.dist-info → create_awesome_python_app-0.2.4.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|