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.
Files changed (61) hide show
  1. zrb/builtin/git.py +8 -8
  2. zrb/builtin/llm/llm_chat.py +3 -3
  3. zrb/builtin/project/add/fastapp/fastapp_input.py +1 -1
  4. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/column/add_column_task.py +99 -55
  5. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/column/add_column_util.py +301 -0
  6. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_task.py +24 -1
  7. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_util.py +61 -1
  8. 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
  9. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/gateway_subroute.py +24 -0
  10. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/navigation_config_file.py +8 -0
  11. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/input.py +3 -3
  12. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_task.py +8 -0
  13. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_util.py +40 -1
  14. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/gateway/subroute/my_module.py +2 -0
  15. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/navigation_config_file.py +6 -0
  16. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/parser.py +2 -2
  17. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/view.py +1 -1
  18. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/config.py +18 -8
  19. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/config/navigation.py +39 -0
  20. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/route.py +52 -11
  21. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/schema/navigation.py +95 -0
  22. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/subroute/auth.py +91 -8
  23. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/util/auth.py +9 -0
  24. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/util/view.py +33 -8
  25. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/auth/permission.html +311 -0
  26. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/auth/role.html +0 -0
  27. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/auth/user.html +0 -0
  28. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/error.html +4 -1
  29. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/login.html +67 -0
  30. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/logout.html +49 -0
  31. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/common/util.js +160 -0
  32. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/crud/style.css +14 -0
  33. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/crud/util.js +94 -0
  34. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/default/pico-style.css +23 -0
  35. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/default/script.js +44 -0
  36. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/default/style.css +102 -0
  37. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/template/default.html +73 -18
  38. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/requirements.txt +1 -1
  39. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_homepage.py +2 -4
  40. zrb/builtin/project/create/project_task.py +2 -2
  41. zrb/builtin/random.py +3 -3
  42. zrb/builtin/setup/common_input.py +5 -5
  43. zrb/builtin/setup/tmux/tmux.py +1 -1
  44. zrb/builtin/setup/zsh/zsh.py +1 -1
  45. zrb/builtin/todo.py +4 -4
  46. zrb/input/base_input.py +17 -12
  47. zrb/input/bool_input.py +12 -5
  48. zrb/input/float_input.py +12 -5
  49. zrb/input/int_input.py +12 -5
  50. zrb/input/option_input.py +5 -5
  51. zrb/input/password_input.py +5 -5
  52. zrb/input/text_input.py +4 -4
  53. zrb/runner/web_route/refresh_token_api_route.py +1 -1
  54. zrb/runner/web_route/static/refresh-token.template.js +9 -0
  55. zrb/runner/web_route/static/static_route.py +1 -1
  56. zrb/util/load.py +13 -7
  57. {zrb-1.0.0b10.dist-info → zrb-1.2.0.dist-info}/METADATA +2 -2
  58. {zrb-1.0.0b10.dist-info → zrb-1.2.0.dist-info}/RECORD +60 -44
  59. zrb/util/llm/tool.py +0 -87
  60. {zrb-1.0.0b10.dist-info → zrb-1.2.0.dist-info}/WHEEL +0 -0
  61. {zrb-1.0.0b10.dist-info → zrb-1.2.0.dist-info}/entry_points.txt +0 -0
@@ -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)">&laquo;</button>`;
12
+ paginationHTML += `<button class="secondary" onclick="${fetchFunction}(${crudState.currentPage - 1})">&lt;</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})">&gt;</button>`;
43
+ paginationHTML += `<button class="secondary" onclick="${fetchFunction}(${totalPages})">&raquo;</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
- {% for css_path in partials.css_path_list -%}
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="{{partials.favicon_path}}" sizes="32x32" type="image/png">
11
- <title>{{partials.title}}</title>
13
+ <link rel="icon" href="{{favicon_path}}" sizes="32x32" type="image/png">
14
+ <title>{{title}}</title>
12
15
  </head>
13
16
  <body>
14
- <header class="container">
15
- <hgroup>
16
- <h1>{{partials.title}}</h1>
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
- <li><a href="/">🏠 Home</a></li>
21
- <li><a href="/docs">💻 API Documentation</a></li>
22
- </ul>
23
- <ul>
24
- <li>Hi</li>
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
- </hgroup>
28
- </header>
29
- {{content}}
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>
@@ -4,7 +4,7 @@ sqlmodel~=0.0.22
4
4
  ulid-py~=1.1.0
5
5
  passlib~=1.7.4
6
6
  Jinja2~=3.1.5
7
- python-jose~=3.3.0
7
+ python-jose~=3.4.0
8
8
  passlib~=1.7.4
9
9
 
10
10
  pytest~=8.3.4
@@ -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 render
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 == render(
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
- default_str=lambda _: os.getcwd(),
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
- default_str=lambda ctx: os.path.basename(ctx.input.project_dir),
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
- default_str="6",
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
- default_str="1",
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
- default_str="🪙, 🪄, ⚔️, 🍷",
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
- default_str="apt",
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
- default_str="yes",
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
- default_str="yes",
23
+ default=True,
24
24
  )
25
25
 
26
26
  setup_zsh_input = BoolInput(
27
- name="setup-zsh", description="Setup zsh", prompt="Setup zsh", default_str="yes"
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
- default_str="no",
34
+ default=False,
35
35
  )
@@ -22,7 +22,7 @@ install_tmux = CmdTask(
22
22
  name="tmux-config",
23
23
  description="Tmux config file",
24
24
  prompt="Tmux config file",
25
- default_str="~/.tmux.conf",
25
+ default="~/.tmux.conf",
26
26
  ),
27
27
  description="📺 Setup `tmux`.",
28
28
  group=setup_group,
@@ -34,7 +34,7 @@ install_zinit = CmdTask(
34
34
  name="zsh-config",
35
35
  description="zsh config file",
36
36
  prompt="zsh config file",
37
- default_str="~/.zshrc",
37
+ default="~/.zshrc",
38
38
  ),
39
39
  upstream=[install_omz, install_zinit],
40
40
  description="💻 Setup `zsh`.",
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
- default_str="E",
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
- default_str="30m",
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
- default_str=lambda _: _get_default_stop_work_time_str(),
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
- default_str=lambda _: _get_todo_txt_content(),
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 StrAttr
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 get_str_attr
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
- default_str: StrAttr = "",
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._default_str = default_str
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, ctx: AnySharedContext) -> str:
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(ctx)
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
- default_value = self.get_default_str(shared_ctx)
85
- if default_value != "":
86
- prompt_message = f"{prompt_message} [{default_value}]"
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 = default_value
91
+ value = default_str
91
92
  return value
92
93
 
93
94
  def get_default_str(self, shared_ctx: AnySharedContext) -> str:
94
- return get_str_attr(
95
- shared_ctx, self._default_str, auto_render=self._auto_render
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 StrAttr
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
- default_str: StrAttr = "False",
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
- default_str=default_str,
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, ctx: AnySharedContext) -> str:
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(ctx))
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)