zrb 1.0.0b10__py3-none-any.whl → 1.2.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.
- zrb/builtin/git.py +8 -8
- zrb/builtin/llm/llm_chat.py +3 -3
- zrb/builtin/project/add/fastapp/fastapp_input.py +1 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/column/add_column_task.py +99 -55
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/column/add_column_util.py +301 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_task.py +24 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_util.py +61 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/gateway/view/content/my-module/my-entity.html +297 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/gateway_subroute.py +24 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/navigation_config_file.py +8 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/input.py +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_task.py +8 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_util.py +40 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/gateway/subroute/my_module.py +2 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/navigation_config_file.py +6 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/parser.py +2 -2
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/view.py +1 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/config.py +18 -8
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/config/navigation.py +39 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/route.py +52 -11
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/schema/navigation.py +95 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/subroute/auth.py +91 -8
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/util/auth.py +9 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/util/view.py +33 -8
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/auth/permission.html +311 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/auth/role.html +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/auth/user.html +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/error.html +4 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/login.html +67 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/logout.html +49 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/common/util.js +160 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/crud/style.css +14 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/crud/util.js +94 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/default/pico-style.css +23 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/default/script.js +44 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/default/style.css +102 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/template/default.html +73 -18
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/requirements.txt +1 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_homepage.py +2 -4
- zrb/builtin/project/create/project_task.py +2 -2
- zrb/builtin/random.py +3 -3
- zrb/builtin/setup/common_input.py +5 -5
- zrb/builtin/setup/tmux/tmux.py +1 -1
- zrb/builtin/setup/zsh/zsh.py +1 -1
- zrb/builtin/todo.py +4 -4
- zrb/input/base_input.py +17 -12
- zrb/input/bool_input.py +12 -5
- zrb/input/float_input.py +12 -5
- zrb/input/int_input.py +12 -5
- zrb/input/option_input.py +5 -5
- zrb/input/password_input.py +5 -5
- zrb/input/text_input.py +4 -4
- zrb/runner/web_route/refresh_token_api_route.py +1 -1
- zrb/runner/web_route/static/refresh-token.template.js +9 -0
- zrb/runner/web_route/static/static_route.py +1 -1
- zrb/util/load.py +13 -7
- {zrb-1.0.0b10.dist-info → zrb-1.2.0.dist-info}/METADATA +2 -2
- {zrb-1.0.0b10.dist-info → zrb-1.2.0.dist-info}/RECORD +60 -44
- zrb/util/llm/tool.py +0 -87
- {zrb-1.0.0b10.dist-info → zrb-1.2.0.dist-info}/WHEEL +0 -0
- {zrb-1.0.0b10.dist-info → zrb-1.2.0.dist-info}/entry_points.txt +0 -0
zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/crud/util.js
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
const CRUD_UTIL = {
|
2
|
+
|
3
|
+
renderPagination(paginationComponent, crudState, total, fetchFunction = "fetchRows") {
|
4
|
+
const totalPages = Math.ceil(total / crudState.pageSize);
|
5
|
+
paginationComponent.innerHTML = "";
|
6
|
+
// Ensure left alignment (if not already handled by PicoCSS or external CSS)
|
7
|
+
paginationComponent.style.textAlign = "left";
|
8
|
+
let paginationHTML = "";
|
9
|
+
// Only show "First" and "Previous" if we're not on page 1
|
10
|
+
if (crudState.currentPage > 1) {
|
11
|
+
paginationHTML += `<button class="secondary" onclick="${fetchFunction}(1)">«</button>`;
|
12
|
+
paginationHTML += `<button class="secondary" onclick="${fetchFunction}(${crudState.currentPage - 1})"><</button>`;
|
13
|
+
}
|
14
|
+
if (totalPages <= 5) {
|
15
|
+
// If total pages are few, simply list them all
|
16
|
+
for (let i = 1; i <= totalPages; i++) {
|
17
|
+
paginationHTML += `<button class="secondary" onclick="${fetchFunction}(${i})" ${i === crudState.currentPage ? "disabled" : ""}>${i}</button>`;
|
18
|
+
}
|
19
|
+
} else {
|
20
|
+
// Always show first page
|
21
|
+
paginationHTML += `<button class="secondary" onclick="${fetchFunction}(1)" ${crudState.currentPage === 1 ? "disabled" : ""}>1</button>`;
|
22
|
+
// Determine start and end for the page range around current page
|
23
|
+
const start = Math.max(2, crudState.currentPage - 1);
|
24
|
+
const end = Math.min(totalPages - 1, crudState.currentPage + 1);
|
25
|
+
// Add ellipsis if there's a gap between first page and the start of the range
|
26
|
+
if (start > 2) {
|
27
|
+
paginationHTML += `<span style="padding: 0 5px;">...</span>`;
|
28
|
+
}
|
29
|
+
// Render the range around the current page
|
30
|
+
for (let i = start; i <= end; i++) {
|
31
|
+
paginationHTML += `<button class="secondary" onclick="${fetchFunction}(${i})" ${i === crudState.currentPage ? "disabled" : ""}>${i}</button>`;
|
32
|
+
}
|
33
|
+
// Add ellipsis if there's a gap between the end of the range and the last page
|
34
|
+
if (end < totalPages - 1) {
|
35
|
+
paginationHTML += `<span style="padding: 0 5px;">...</span>`;
|
36
|
+
}
|
37
|
+
// Always show last page
|
38
|
+
paginationHTML += `<button class="secondary" onclick="${fetchFunction}(${totalPages})" ${crudState.currentPage === totalPages ? "disabled" : ""}>${totalPages}</button>`;
|
39
|
+
}
|
40
|
+
// Only show "Next" and "Last" if we're not on the last page
|
41
|
+
if (crudState.currentPage < totalPages) {
|
42
|
+
paginationHTML += `<button class="secondary" onclick="${fetchFunction}(${crudState.currentPage + 1})">></button>`;
|
43
|
+
paginationHTML += `<button class="secondary" onclick="${fetchFunction}(${totalPages})">»</button>`;
|
44
|
+
}
|
45
|
+
paginationComponent.innerHTML = paginationHTML;
|
46
|
+
},
|
47
|
+
|
48
|
+
splitUnescaped(query, delimiter=",") {
|
49
|
+
const parts = [];
|
50
|
+
let current = "";
|
51
|
+
let escaped = false;
|
52
|
+
for (let i = 0; i < query.length; i++) {
|
53
|
+
const char = query[i];
|
54
|
+
if (escaped) {
|
55
|
+
current += char;
|
56
|
+
escaped = false;
|
57
|
+
} else if (char === "\\") {
|
58
|
+
escaped = true;
|
59
|
+
} else if (char === delimiter) {
|
60
|
+
parts.push(current);
|
61
|
+
current = "";
|
62
|
+
} else {
|
63
|
+
current += char;
|
64
|
+
}
|
65
|
+
}
|
66
|
+
if (current != "") {
|
67
|
+
parts.push(current);
|
68
|
+
}
|
69
|
+
return parts;
|
70
|
+
},
|
71
|
+
|
72
|
+
isValidFilterQuery(query) {
|
73
|
+
const filterPattern = /^([\w]+):(eq|ne|gt|gte|lt|lte|like|in):(.+)$/;
|
74
|
+
const parts = this.splitUnescaped(query);
|
75
|
+
return parts.every(part => filterPattern.test(part));
|
76
|
+
},
|
77
|
+
|
78
|
+
getSearchParam(crudState, defaultSearchColumn, apiMode = false) {
|
79
|
+
return new URLSearchParams({
|
80
|
+
page: crudState.currentPage || 1,
|
81
|
+
page_size: crudState.pageSize || 10,
|
82
|
+
filter: this._getFilterSearchParamValue(crudState, defaultSearchColumn, apiMode),
|
83
|
+
}).toString();
|
84
|
+
},
|
85
|
+
|
86
|
+
_getFilterSearchParamValue(crudState, defaultSearchColumn, apiMode = false) {
|
87
|
+
const filter = crudState.filter || "";
|
88
|
+
if (!apiMode) {
|
89
|
+
return filter;
|
90
|
+
}
|
91
|
+
return this.isValidFilterQuery(filter) ? filter : `${defaultSearchColumn}:like:%${filter}%`;
|
92
|
+
}
|
93
|
+
|
94
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
h1 {
|
2
|
+
color: var(--pico-primary)
|
3
|
+
}
|
4
|
+
|
5
|
+
h2 {
|
6
|
+
color: var(--pico-primary)
|
7
|
+
}
|
8
|
+
|
9
|
+
h3 {
|
10
|
+
color: var(--pico-primary)
|
11
|
+
}
|
12
|
+
|
13
|
+
h4 {
|
14
|
+
color: var(--pico-primary)
|
15
|
+
}
|
16
|
+
|
17
|
+
h5 {
|
18
|
+
color: var(--pico-primary)
|
19
|
+
}
|
20
|
+
|
21
|
+
h6 {
|
22
|
+
color: var(--pico-primary)
|
23
|
+
}
|
@@ -0,0 +1,44 @@
|
|
1
|
+
// Hamburger menu functionality
|
2
|
+
const hamburgerMenu = document.querySelector('.hamburger-menu');
|
3
|
+
const layoutContainer = document.querySelector('.layout-container');
|
4
|
+
|
5
|
+
hamburgerMenu.addEventListener('click', function() {
|
6
|
+
layoutContainer.classList.toggle('menu-active');
|
7
|
+
});
|
8
|
+
|
9
|
+
// Close menu when clicking outside
|
10
|
+
document.addEventListener('click', function(event) {
|
11
|
+
if (!layoutContainer.contains(event.target) && !hamburgerMenu.contains(event.target)) {
|
12
|
+
layoutContainer.classList.remove('menu-active');
|
13
|
+
}
|
14
|
+
});
|
15
|
+
|
16
|
+
// Theme switcher functionality
|
17
|
+
const themeSelect = document.getElementById('theme-select');
|
18
|
+
|
19
|
+
function setTheme(theme) {
|
20
|
+
document.documentElement.setAttribute('data-theme', theme);
|
21
|
+
localStorage.setItem('theme', theme);
|
22
|
+
}
|
23
|
+
|
24
|
+
function getSavedTheme() {
|
25
|
+
return localStorage.getItem('theme') || 'auto';
|
26
|
+
}
|
27
|
+
|
28
|
+
const savedTheme = getSavedTheme();
|
29
|
+
setTheme(savedTheme);
|
30
|
+
themeSelect.value = savedTheme;
|
31
|
+
|
32
|
+
themeSelect.addEventListener('change', (e) => {
|
33
|
+
setTheme(e.target.value);
|
34
|
+
});
|
35
|
+
|
36
|
+
function updateAutoTheme() {
|
37
|
+
if (getSavedTheme() === 'auto') {
|
38
|
+
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
39
|
+
document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
updateAutoTheme();
|
44
|
+
window.matchMedia('(prefers-color-scheme: dark)').addListener(updateAutoTheme);
|
@@ -0,0 +1,102 @@
|
|
1
|
+
body {
|
2
|
+
overflow-x: hidden;
|
3
|
+
margin: 0;
|
4
|
+
padding: 0;
|
5
|
+
min-height: 100vh;
|
6
|
+
}
|
7
|
+
.layout-container {
|
8
|
+
display: flex;
|
9
|
+
min-height: 100vh;
|
10
|
+
}
|
11
|
+
aside {
|
12
|
+
position: fixed;
|
13
|
+
top: 0;
|
14
|
+
left: 0;
|
15
|
+
bottom: 0;
|
16
|
+
width: 300px;
|
17
|
+
padding: 1rem;
|
18
|
+
border-right: 1px solid var(--muted-border-color);
|
19
|
+
height: 100vh;
|
20
|
+
background: var(--background-color);
|
21
|
+
display: flex;
|
22
|
+
flex-direction: column;
|
23
|
+
}
|
24
|
+
aside nav {
|
25
|
+
flex-grow: 1; /* Pushes theme-switcher to the bottom */
|
26
|
+
overflow-y: auto;
|
27
|
+
overflow-x: hidden;
|
28
|
+
}
|
29
|
+
aside ul {
|
30
|
+
padding: 0;
|
31
|
+
list-style: none;
|
32
|
+
}
|
33
|
+
aside li {
|
34
|
+
margin-bottom: 0.5rem;
|
35
|
+
}
|
36
|
+
aside li > ul {
|
37
|
+
padding-left: 1rem;
|
38
|
+
}
|
39
|
+
.content-wrapper {
|
40
|
+
flex: 1;
|
41
|
+
overflow-x: hidden;
|
42
|
+
}
|
43
|
+
.content {
|
44
|
+
padding: 1rem;
|
45
|
+
}
|
46
|
+
.hamburger-menu {
|
47
|
+
display: none;
|
48
|
+
background: var(--pico-primary);
|
49
|
+
border: none;
|
50
|
+
font-size: 1.5rem;
|
51
|
+
cursor: pointer;
|
52
|
+
position: fixed;
|
53
|
+
top: 1rem;
|
54
|
+
left: 1rem;
|
55
|
+
z-index: 1001;
|
56
|
+
color: var(--contrast);
|
57
|
+
}
|
58
|
+
.theme-switcher {
|
59
|
+
display: flex;
|
60
|
+
align-items: center;
|
61
|
+
gap: 0.5rem;
|
62
|
+
margin-top: auto;
|
63
|
+
}
|
64
|
+
.active-link {
|
65
|
+
font-weight: bold;
|
66
|
+
color: var(--primary);
|
67
|
+
}
|
68
|
+
|
69
|
+
@media (max-width: 768px) {
|
70
|
+
.layout-container {
|
71
|
+
position: relative;
|
72
|
+
left: 0;
|
73
|
+
transition: left 0.3s ease;
|
74
|
+
}
|
75
|
+
.hamburger-menu {
|
76
|
+
display: block;
|
77
|
+
}
|
78
|
+
aside {
|
79
|
+
left: -300px;
|
80
|
+
top: 0;
|
81
|
+
bottom: 0;
|
82
|
+
z-index: 1000;
|
83
|
+
transition: left 0.3s ease;
|
84
|
+
padding-top: 4rem;
|
85
|
+
}
|
86
|
+
.layout-container.menu-active {
|
87
|
+
margin-left: 300px;
|
88
|
+
/*left: 300px;*/
|
89
|
+
}
|
90
|
+
.layout-container.menu-active aside {
|
91
|
+
left: 0;
|
92
|
+
}
|
93
|
+
.content {
|
94
|
+
padding-top: 4rem;
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
@media (min-width: 769px) {
|
99
|
+
.content-wrapper {
|
100
|
+
margin-left: 300px;
|
101
|
+
}
|
102
|
+
}
|
@@ -4,31 +4,86 @@
|
|
4
4
|
<meta charset="utf-8">
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
6
6
|
<meta name="color-scheme" content="light dark">
|
7
|
-
|
7
|
+
<link rel="stylesheet" href="{{pico_css_path}}">
|
8
|
+
<link rel="stylesheet" href="/static/default/pico-style.css">
|
9
|
+
<link rel="stylesheet" href="/static/default/style.css">
|
10
|
+
{% for css_path in css_path_list -%}
|
8
11
|
<link rel="stylesheet" href="{{css_path}}">
|
9
12
|
{% endfor %}
|
10
|
-
<link rel="icon" href="{{
|
11
|
-
<title>{{
|
13
|
+
<link rel="icon" href="{{favicon_path}}" sizes="32x32" type="image/png">
|
14
|
+
<title>{{title}}</title>
|
12
15
|
</head>
|
13
16
|
<body>
|
14
|
-
<
|
15
|
-
|
16
|
-
|
17
|
-
<p>{{partials.subtitle}}</p>
|
17
|
+
<button class="hamburger-menu" aria-label="Menu">☰</button>
|
18
|
+
<div class="layout-container">
|
19
|
+
<aside>
|
18
20
|
<nav>
|
19
21
|
<ul>
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
{% for navigation in navigations %}
|
23
|
+
{% if navigation|attr("pages") %}
|
24
|
+
<li>
|
25
|
+
<details {% if navigation.active %}open{% endif %}>
|
26
|
+
<summary>{{navigation.caption}}</summary>
|
27
|
+
<ul>
|
28
|
+
{% for page in navigation.pages %}
|
29
|
+
<li><a href="{{page.url}}" class="{{ 'active-link' if page.active else '' }}">{{page.caption}}</a></li>
|
30
|
+
{% endfor %}
|
31
|
+
</ul>
|
32
|
+
</details>
|
33
|
+
</li>
|
34
|
+
{% else %}
|
35
|
+
<li><a href="{{navigation.url}}" class="{{ 'active-link' if navigation.active else '' }}">{{navigation.caption}}</a></li>
|
36
|
+
{% endif %}
|
37
|
+
{% endfor %}
|
25
38
|
</ul>
|
26
39
|
</nav>
|
27
|
-
|
28
|
-
|
29
|
-
|
40
|
+
<div class="theme-switcher">
|
41
|
+
<label for="theme-select">Theme:</label>
|
42
|
+
<select id="theme-select">
|
43
|
+
<option value="auto">🌗 Auto</option>
|
44
|
+
<option value="light">☀️ Light</option>
|
45
|
+
<option value="dark">🌙 Dark</option>
|
46
|
+
</select>
|
47
|
+
</div>
|
48
|
+
</aside>
|
49
|
+
<div class="content-wrapper">
|
50
|
+
<main class="content">
|
51
|
+
<header>
|
52
|
+
<hgroup>
|
53
|
+
<h1>{{title}}</h1>
|
54
|
+
<p>{{subtitle}}</p>
|
55
|
+
</hgroup>
|
56
|
+
{% if show_user_info %}
|
57
|
+
<nav>
|
58
|
+
<ul></ul>
|
59
|
+
<ul>
|
60
|
+
<li>
|
61
|
+
{% if current_user is none %}
|
62
|
+
<p>Hi Visitor <a href="/login">🔓</a></p>
|
63
|
+
{% elif current_user.is_guest %}
|
64
|
+
<p>Hi {{current_user.username}} <a href="/login">🔓</a></p>
|
65
|
+
{% else %}
|
66
|
+
<p>Hi {{current_user.username}} <a href="/logout">🔒</a></p>
|
67
|
+
{% endif %}
|
68
|
+
</li>
|
69
|
+
</ul>
|
70
|
+
</nav>
|
71
|
+
{% endif %}
|
72
|
+
</header>
|
73
|
+
<script src="/static/common/util.js"></script>
|
74
|
+
{{content}}
|
75
|
+
<footer>{{footer}}</footer>
|
76
|
+
</main>
|
77
|
+
</div>
|
78
|
+
</div>
|
79
|
+
{% for js_path in js_path_list -%}
|
80
|
+
<script src="{{js_path}}"></script>
|
81
|
+
{% endfor %}
|
82
|
+
<script src="/static/default/script.js"></script>
|
83
|
+
<script>
|
84
|
+
{% if should_refresh_session %}
|
85
|
+
UTIL.refreshAccessTokenPeriodically({{refresh_session_interval_seconds}});
|
86
|
+
{% endif %}
|
87
|
+
</script>
|
30
88
|
</body>
|
31
|
-
{% for js_path in partials.js_path_list -%}
|
32
|
-
<script src="{{js_path}}"></script>
|
33
|
-
{% endfor %}
|
34
89
|
</html>
|
@@ -2,7 +2,7 @@ import os
|
|
2
2
|
|
3
3
|
from fastapi.testclient import TestClient
|
4
4
|
from my_app_name.main import app
|
5
|
-
from my_app_name.module.gateway.util.view import
|
5
|
+
from my_app_name.module.gateway.util.view import render_content
|
6
6
|
|
7
7
|
|
8
8
|
def test_homepage():
|
@@ -12,6 +12,4 @@ def test_homepage():
|
|
12
12
|
view_path = os.path.join(
|
13
13
|
os.path.dirname(os.path.dirname(__file__)), "module", "gateway", "view"
|
14
14
|
)
|
15
|
-
assert response.text ==
|
16
|
-
os.path.join(view_path, "content", "homepage.html")
|
17
|
-
).body.decode("utf-8")
|
15
|
+
assert response.text == render_content("homepage.html").body.decode("utf-8")
|
@@ -15,13 +15,13 @@ scaffold_project = Scaffolder(
|
|
15
15
|
name="project-dir",
|
16
16
|
description="Project directory",
|
17
17
|
prompt="Project directory",
|
18
|
-
|
18
|
+
default=lambda _: os.getcwd(),
|
19
19
|
),
|
20
20
|
StrInput(
|
21
21
|
name="project",
|
22
22
|
description="Project name",
|
23
23
|
prompt="Project name",
|
24
|
-
|
24
|
+
default=lambda ctx: os.path.basename(ctx.input.project_dir),
|
25
25
|
),
|
26
26
|
],
|
27
27
|
source_path=os.path.join(_DIR, "project-template"),
|
zrb/builtin/random.py
CHANGED
@@ -15,13 +15,13 @@ from zrb.task.make_task import make_task
|
|
15
15
|
name="side",
|
16
16
|
description="Number of sides",
|
17
17
|
prompt="How many sides (comma separated)",
|
18
|
-
|
18
|
+
default="6",
|
19
19
|
),
|
20
20
|
IntInput(
|
21
21
|
name="num-rolls",
|
22
22
|
description="Number of rolls",
|
23
23
|
prompt="How many rolls",
|
24
|
-
|
24
|
+
default="1",
|
25
25
|
),
|
26
26
|
],
|
27
27
|
retries=0,
|
@@ -48,7 +48,7 @@ def throw_dice(ctx: AnyContext) -> str:
|
|
48
48
|
name="values",
|
49
49
|
description="Value to be shuffled",
|
50
50
|
prompt="List of values (comma separated)",
|
51
|
-
|
51
|
+
default="🪙, 🪄, ⚔️, 🍷",
|
52
52
|
),
|
53
53
|
retries=0,
|
54
54
|
group=random_group,
|
@@ -6,30 +6,30 @@ package_manager_input = OptionInput(
|
|
6
6
|
description="Your package manager",
|
7
7
|
prompt="Your package manager",
|
8
8
|
options=["apt", "dnf", "pacman", "zypper", "pkg", "brew", "spack"],
|
9
|
-
|
9
|
+
default="apt",
|
10
10
|
)
|
11
11
|
|
12
12
|
use_sudo_input = BoolInput(
|
13
13
|
name="use-sudo",
|
14
14
|
description="Use sudo or not",
|
15
15
|
prompt="Need sudo",
|
16
|
-
|
16
|
+
default=True,
|
17
17
|
)
|
18
18
|
|
19
19
|
setup_bash_input = BoolInput(
|
20
20
|
name="setup-bash",
|
21
21
|
description="Setup bash",
|
22
22
|
prompt="Setup bash",
|
23
|
-
|
23
|
+
default=True,
|
24
24
|
)
|
25
25
|
|
26
26
|
setup_zsh_input = BoolInput(
|
27
|
-
name="setup-zsh", description="Setup zsh", prompt="Setup zsh",
|
27
|
+
name="setup-zsh", description="Setup zsh", prompt="Setup zsh", default=True
|
28
28
|
)
|
29
29
|
|
30
30
|
setup_powershell_input = BoolInput(
|
31
31
|
name="setup-powershell",
|
32
32
|
description="Setup powershell",
|
33
33
|
prompt="Setup powershell",
|
34
|
-
|
34
|
+
default=False,
|
35
35
|
)
|
zrb/builtin/setup/tmux/tmux.py
CHANGED
zrb/builtin/setup/zsh/zsh.py
CHANGED
zrb/builtin/todo.py
CHANGED
@@ -37,7 +37,7 @@ from zrb.util.todo import (
|
|
37
37
|
name="priority",
|
38
38
|
description="Task priority",
|
39
39
|
prompt="Task priority",
|
40
|
-
|
40
|
+
default="E",
|
41
41
|
),
|
42
42
|
StrInput(
|
43
43
|
name="project",
|
@@ -208,13 +208,13 @@ def archive_todo(ctx: AnyContext):
|
|
208
208
|
name="duration",
|
209
209
|
prompt="Working duration",
|
210
210
|
description="Working duration",
|
211
|
-
|
211
|
+
default="30m",
|
212
212
|
),
|
213
213
|
StrInput(
|
214
214
|
name="stop",
|
215
215
|
prompt="Working stop time (%Y-%m-%d %H:%M:%S)",
|
216
216
|
description="Working stop time",
|
217
|
-
|
217
|
+
default=lambda _: _get_default_stop_work_time_str(),
|
218
218
|
),
|
219
219
|
],
|
220
220
|
description="🕒 Log work todo",
|
@@ -293,7 +293,7 @@ def _get_default_stop_work_time_str() -> str:
|
|
293
293
|
name="text",
|
294
294
|
description="Todo.txt content",
|
295
295
|
prompt="Todo.txt content (will override existing)",
|
296
|
-
|
296
|
+
default=lambda _: _get_todo_txt_content(),
|
297
297
|
allow_positional_parsing=False,
|
298
298
|
),
|
299
299
|
],
|
zrb/input/base_input.py
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
from typing import Any
|
2
2
|
|
3
|
-
from zrb.attr.type import
|
3
|
+
from zrb.attr.type import AnyAttr
|
4
4
|
from zrb.context.any_shared_context import AnySharedContext
|
5
5
|
from zrb.input.any_input import AnyInput
|
6
|
-
from zrb.util.attr import
|
6
|
+
from zrb.util.attr import get_attr
|
7
7
|
from zrb.util.string.conversion import to_snake_case
|
8
8
|
|
9
9
|
|
@@ -13,7 +13,7 @@ class BaseInput(AnyInput):
|
|
13
13
|
name: str,
|
14
14
|
description: str | None = None,
|
15
15
|
prompt: str | None = None,
|
16
|
-
|
16
|
+
default: AnyAttr = "",
|
17
17
|
auto_render: bool = True,
|
18
18
|
allow_empty: bool = False,
|
19
19
|
allow_positional_parsing: bool = True,
|
@@ -21,7 +21,7 @@ class BaseInput(AnyInput):
|
|
21
21
|
self._name = name
|
22
22
|
self._description = description
|
23
23
|
self._prompt = prompt
|
24
|
-
self.
|
24
|
+
self._default_value = default
|
25
25
|
self._auto_render = auto_render
|
26
26
|
self._allow_empty = allow_empty
|
27
27
|
self._allow_positional_parsing = allow_positional_parsing
|
@@ -45,10 +45,10 @@ class BaseInput(AnyInput):
|
|
45
45
|
def allow_positional_parsing(self) -> bool:
|
46
46
|
return self._allow_positional_parsing
|
47
47
|
|
48
|
-
def to_html(self,
|
48
|
+
def to_html(self, shared_ctx: AnySharedContext) -> str:
|
49
49
|
name = self.name
|
50
50
|
description = self.description
|
51
|
-
default = self.get_default_str(
|
51
|
+
default = self.get_default_str(shared_ctx)
|
52
52
|
return f'<input name="{name}" placeholder="{description}" value="{default}" />'
|
53
53
|
|
54
54
|
def update_shared_context(
|
@@ -74,6 +74,7 @@ class BaseInput(AnyInput):
|
|
74
74
|
return str_value
|
75
75
|
|
76
76
|
def prompt_cli_str(self, shared_ctx: AnySharedContext) -> str:
|
77
|
+
"""Prompting user to input the value"""
|
77
78
|
value = self._prompt_cli_str(shared_ctx)
|
78
79
|
while not self._allow_empty and value == "":
|
79
80
|
value = self._prompt_cli_str(shared_ctx)
|
@@ -81,16 +82,20 @@ class BaseInput(AnyInput):
|
|
81
82
|
|
82
83
|
def _prompt_cli_str(self, shared_ctx: AnySharedContext) -> str:
|
83
84
|
prompt_message = self.prompt_message
|
84
|
-
|
85
|
-
if
|
86
|
-
prompt_message = f"{prompt_message} [{
|
85
|
+
default_str = self.get_default_str(shared_ctx)
|
86
|
+
if default_str != "":
|
87
|
+
prompt_message = f"{prompt_message} [{default_str}]"
|
87
88
|
print(f"{prompt_message}: ", end="")
|
88
89
|
value = input()
|
89
90
|
if value.strip() == "":
|
90
|
-
value =
|
91
|
+
value = default_str
|
91
92
|
return value
|
92
93
|
|
93
94
|
def get_default_str(self, shared_ctx: AnySharedContext) -> str:
|
94
|
-
|
95
|
-
|
95
|
+
"""Get default value as str"""
|
96
|
+
default_value = get_attr(
|
97
|
+
shared_ctx, self._default_value, default="", auto_render=self._auto_render
|
96
98
|
)
|
99
|
+
if not isinstance(default_value, str):
|
100
|
+
return str(default_value)
|
101
|
+
return default_value
|
zrb/input/bool_input.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
from zrb.attr.type import
|
1
|
+
from zrb.attr.type import BoolAttr
|
2
2
|
from zrb.context.any_shared_context import AnySharedContext
|
3
3
|
from zrb.input.base_input import BaseInput
|
4
|
+
from zrb.util.attr import get_bool_attr
|
4
5
|
from zrb.util.string.conversion import to_boolean
|
5
6
|
|
6
7
|
|
@@ -10,7 +11,7 @@ class BoolInput(BaseInput):
|
|
10
11
|
name: str,
|
11
12
|
description: str | None = None,
|
12
13
|
prompt: str | None = None,
|
13
|
-
|
14
|
+
default: BoolAttr = False,
|
14
15
|
auto_render: bool = True,
|
15
16
|
allow_empty: bool = False,
|
16
17
|
allow_positional_parsing: bool = True,
|
@@ -19,16 +20,16 @@ class BoolInput(BaseInput):
|
|
19
20
|
name=name,
|
20
21
|
description=description,
|
21
22
|
prompt=prompt,
|
22
|
-
|
23
|
+
default=default,
|
23
24
|
auto_render=auto_render,
|
24
25
|
allow_empty=allow_empty,
|
25
26
|
allow_positional_parsing=allow_positional_parsing,
|
26
27
|
)
|
27
28
|
|
28
|
-
def to_html(self,
|
29
|
+
def to_html(self, shared_ctx: AnySharedContext) -> str:
|
29
30
|
name = self.name
|
30
31
|
description = self.description
|
31
|
-
default = to_boolean(self.get_default_str(
|
32
|
+
default = to_boolean(self.get_default_str(shared_ctx))
|
32
33
|
selected_true = "selected" if default else ""
|
33
34
|
selected_false = "selected" if not default else ""
|
34
35
|
return "\n".join(
|
@@ -40,5 +41,11 @@ class BoolInput(BaseInput):
|
|
40
41
|
]
|
41
42
|
)
|
42
43
|
|
44
|
+
def get_default_str(self, shared_ctx: AnySharedContext) -> str:
|
45
|
+
default_value = get_bool_attr(
|
46
|
+
shared_ctx, self._default_value, auto_render=self._auto_render
|
47
|
+
)
|
48
|
+
return f"{default_value}"
|
49
|
+
|
43
50
|
def _parse_str_value(self, str_value: str) -> bool:
|
44
51
|
return to_boolean(str_value)
|