quirl 0.0.1__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.
- quirl-0.0.1/AUTHORS.md +12 -0
- quirl-0.0.1/LICENSE +29 -0
- quirl-0.0.1/MANIFEST.in +7 -0
- quirl-0.0.1/PKG-INFO +194 -0
- quirl-0.0.1/README.md +159 -0
- quirl-0.0.1/pyproject.toml +66 -0
- quirl-0.0.1/quirl/__init__.py +18 -0
- quirl-0.0.1/quirl/__main__.py +36 -0
- quirl-0.0.1/quirl/components/__init__.py +63 -0
- quirl-0.0.1/quirl/components/alert.py +170 -0
- quirl-0.0.1/quirl/components/animation/__init__.py +10 -0
- quirl-0.0.1/quirl/components/animation/demo.py +340 -0
- quirl-0.0.1/quirl/components/avatar.py +168 -0
- quirl-0.0.1/quirl/components/badge.py +97 -0
- quirl-0.0.1/quirl/components/code_block.py +70 -0
- quirl-0.0.1/quirl/components/divider.py +103 -0
- quirl-0.0.1/quirl/components/documentor.py +433 -0
- quirl-0.0.1/quirl/components/skeleton.py +69 -0
- quirl-0.0.1/quirl/components/spinner.py +102 -0
- quirl-0.0.1/quirl/components/tag.py +131 -0
- quirl-0.0.1/quirl/styles.py +60 -0
- quirl-0.0.1/quirl/theme.py +206 -0
- quirl-0.0.1/quirl/version.py +10 -0
- quirl-0.0.1/quirl.egg-info/PKG-INFO +194 -0
- quirl-0.0.1/quirl.egg-info/SOURCES.txt +29 -0
- quirl-0.0.1/quirl.egg-info/dependency_links.txt +1 -0
- quirl-0.0.1/quirl.egg-info/entry_points.txt +2 -0
- quirl-0.0.1/quirl.egg-info/requires.txt +1 -0
- quirl-0.0.1/quirl.egg-info/top_level.txt +1 -0
- quirl-0.0.1/requirements.txt +1 -0
- quirl-0.0.1/setup.cfg +4 -0
quirl-0.0.1/AUTHORS.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Authors
|
|
2
|
+
|
|
3
|
+
Brian Musakwa <digreatbrian@gmail.com>
|
|
4
|
+
|
|
5
|
+
## Sole Developer and Owner
|
|
6
|
+
Brian Musakwa is the sole developer and owner of this software.
|
|
7
|
+
|
|
8
|
+
## Acknowledgements
|
|
9
|
+
Special thanks to Guido van Rossum for creating Python, which made this project possible.
|
|
10
|
+
|
|
11
|
+
## Project History
|
|
12
|
+
Quirl was created on 25 August 2026.
|
quirl-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
Copyright (c) 2026 Duck Framework
|
|
2
|
+
Copyright (c) 2026 Brian Musakwa
|
|
3
|
+
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without modification,
|
|
7
|
+
are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice,
|
|
10
|
+
this list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright
|
|
13
|
+
notice, this list of conditions and the following disclaimer in the
|
|
14
|
+
documentation and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of Duck nor the names of its contributors may be used
|
|
17
|
+
to endorse or promote products derived from this software without
|
|
18
|
+
specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
21
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
22
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
24
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
25
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
26
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
27
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
28
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
29
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
quirl-0.0.1/MANIFEST.in
ADDED
quirl-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: quirl
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A reusable UI component library for Duck Framework's Lively reactive components.
|
|
5
|
+
Author-email: Brian Musakwa <digreatbrian@gmail.com>
|
|
6
|
+
Maintainer-email: Brian Musakwa <digreatbrian@gmail.com>
|
|
7
|
+
License-Expression: BSD-3-Clause
|
|
8
|
+
Project-URL: Homepage, https://github.com/duckframework/quirl
|
|
9
|
+
Project-URL: Documentation, https://quirl.duckframework.com
|
|
10
|
+
Project-URL: Source, https://github.com/duckframework/quirl
|
|
11
|
+
Project-URL: Issues, https://github.com/duckframework/quirl/issues
|
|
12
|
+
Project-URL: Contribution, https://duckframework.com/contribute?pkg=quirl
|
|
13
|
+
Keywords: duck framework,duck,lively,lively components,reactive ui,server-side rendering,ui components,component library,web components,python ui
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Environment :: Web Environment
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
26
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
27
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
28
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
29
|
+
Classifier: Topic :: Software Development :: User Interfaces
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
License-File: LICENSE
|
|
32
|
+
License-File: AUTHORS.md
|
|
33
|
+
Requires-Dist: duckframework>=2.3.1
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# Quirl
|
|
37
|
+
|
|
38
|
+

|
|
39
|
+
|
|
40
|
+
A reusable UI component library for [Duck Framework's](https://duckframework.com) Lively reactive system.
|
|
41
|
+
|
|
42
|
+
Build consistent, themeable interfaces in pure Python — no separate frontend toolchain, no JavaScript bundling, no build steps for your UI logic.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## What is quirl?
|
|
47
|
+
|
|
48
|
+
quirl provides a structured foundation for creating and documenting Duck Lively components. It handles theming, animation demos, and static documentation generation so you can focus on building interfaces, not boilerplate.
|
|
49
|
+
|
|
50
|
+
- **Themeable by default** — Design tokens flow through CSS custom properties; every component inherits from a shared theme without hardcoded values.
|
|
51
|
+
- **Self-documenting** — Components declare their own usage docs via markdown docstrings. A build script turns those into a static gallery.
|
|
52
|
+
- **Animated demos without a server** — Wrap any component in a Demo container to generate pure HTML/CSS/JS showcases. Deploy to GitHub Pages with zero hosting cost.
|
|
53
|
+
- **No inheritance lock-in** — The demo system works with any Duck component, built-in or third-party, without requiring base class changes.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install quirl
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Requires Python 3.10+ and `duckframework>=2.3.1`.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Quick start
|
|
68
|
+
|
|
69
|
+
### 1. Define a theme
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from quirl.theme import Theme
|
|
73
|
+
|
|
74
|
+
dark = Theme(
|
|
75
|
+
name="dark",
|
|
76
|
+
accent_color="#F5C842",
|
|
77
|
+
surface_color="#111318",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Update the global theme
|
|
81
|
+
Theme.current = dark
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 2. Create a component
|
|
85
|
+
|
|
86
|
+
Inherit from Duck's built-in components. Write your docs as a markdown class docstring:
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from duck.html.components.label import Label
|
|
90
|
+
|
|
91
|
+
class StatusLabel(Label):
|
|
92
|
+
"""
|
|
93
|
+
A themed label for displaying status text.
|
|
94
|
+
|
|
95
|
+
Usage:
|
|
96
|
+
```python
|
|
97
|
+
StatusLabel(text="Active", color="green")
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Required Props:
|
|
101
|
+
- text: Display text
|
|
102
|
+
|
|
103
|
+
Optional Props:
|
|
104
|
+
- color: The text color for the label
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
# Preview kwargs for the demo
|
|
108
|
+
docs_preview_kwargs = {"text": "Preview", "color": "#3b82f6"}
|
|
109
|
+
|
|
110
|
+
# Demo animation steps
|
|
111
|
+
docs_animation_steps = [
|
|
112
|
+
{"delay": 1000, "cursor": {"top": "50%", "left": "50%"}},
|
|
113
|
+
{"delay": 500, "target": "span", "click": True},
|
|
114
|
+
]
|
|
115
|
+
|
|
116
|
+
def on_create(self):
|
|
117
|
+
super().on_create()
|
|
118
|
+
|
|
119
|
+
# Update style
|
|
120
|
+
self.style.update({
|
|
121
|
+
"border-radius": "9999px",
|
|
122
|
+
"padding": "4px 12px",
|
|
123
|
+
"font-weight": "500",
|
|
124
|
+
})
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 3. Render a static demo
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from duck.html.components.container import Container
|
|
131
|
+
from duck.html.components.button import Button
|
|
132
|
+
from duck.html.components.input import Input
|
|
133
|
+
|
|
134
|
+
from quirl.components import Demo
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
# Demo a built-in Button
|
|
138
|
+
button_demo = Demo(
|
|
139
|
+
component=Button(text="Submit", bg_color="blue", color="white"),
|
|
140
|
+
steps=[
|
|
141
|
+
{"delay": 1000, "cursor": {"target": "button"}},
|
|
142
|
+
{"delay": 500, "target": "button", "click": True},
|
|
143
|
+
{"delay": 2000},
|
|
144
|
+
],
|
|
145
|
+
title="Button",
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# Demo a built-in Input
|
|
149
|
+
input_demo = Demo(
|
|
150
|
+
component=Input(type="text", name="email", placeholder="Email"),
|
|
151
|
+
steps=[
|
|
152
|
+
{"delay": 800, "cursor": {"top": "50%", "left": "30%"}},
|
|
153
|
+
{"delay": 1000, "target": "input", "click": True},
|
|
154
|
+
{"delay": 1500},
|
|
155
|
+
],
|
|
156
|
+
title="Input",
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Group components
|
|
160
|
+
container = Container(children=[button_demo, input_demo])
|
|
161
|
+
|
|
162
|
+
# Render to static HTML
|
|
163
|
+
html = container.render()
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Deploy the output to GitHub Pages, Netlify, or any static host. No Python runtime required on the server.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Architecture
|
|
171
|
+
|
|
172
|
+
quirl is organized around three concerns: theming, documentation, and animation demos.
|
|
173
|
+
|
|
174
|
+
The theme system lives at the package root and provides extensible design tokens through a global class-level registry. Access the active theme anywhere via `Theme.current`.
|
|
175
|
+
|
|
176
|
+
The animation demo system lives under `quirl.components.animation.demo` and wraps any Duck component for static HTML output. It generates self-contained CSS animations and click simulations without a WebSocket connection or Python runtime on the client.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Development
|
|
181
|
+
|
|
182
|
+
**quirl** follows the Duck Framework code style:
|
|
183
|
+
|
|
184
|
+
- Google-style docstrings on every module, class, and method.
|
|
185
|
+
- Constructor-based initialization via kwargs only.
|
|
186
|
+
- No private-style method names (build_nav, not _build_nav).
|
|
187
|
+
- Components live in quirl/components/, themes and utilities at the package root.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## License
|
|
192
|
+
|
|
193
|
+
See [LICENSE](./LICENSE).
|
|
194
|
+
|
quirl-0.0.1/README.md
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Quirl
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
A reusable UI component library for [Duck Framework's](https://duckframework.com) Lively reactive system.
|
|
6
|
+
|
|
7
|
+
Build consistent, themeable interfaces in pure Python — no separate frontend toolchain, no JavaScript bundling, no build steps for your UI logic.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## What is quirl?
|
|
12
|
+
|
|
13
|
+
quirl provides a structured foundation for creating and documenting Duck Lively components. It handles theming, animation demos, and static documentation generation so you can focus on building interfaces, not boilerplate.
|
|
14
|
+
|
|
15
|
+
- **Themeable by default** — Design tokens flow through CSS custom properties; every component inherits from a shared theme without hardcoded values.
|
|
16
|
+
- **Self-documenting** — Components declare their own usage docs via markdown docstrings. A build script turns those into a static gallery.
|
|
17
|
+
- **Animated demos without a server** — Wrap any component in a Demo container to generate pure HTML/CSS/JS showcases. Deploy to GitHub Pages with zero hosting cost.
|
|
18
|
+
- **No inheritance lock-in** — The demo system works with any Duck component, built-in or third-party, without requiring base class changes.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install quirl
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Requires Python 3.10+ and `duckframework>=2.3.1`.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Quick start
|
|
33
|
+
|
|
34
|
+
### 1. Define a theme
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from quirl.theme import Theme
|
|
38
|
+
|
|
39
|
+
dark = Theme(
|
|
40
|
+
name="dark",
|
|
41
|
+
accent_color="#F5C842",
|
|
42
|
+
surface_color="#111318",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Update the global theme
|
|
46
|
+
Theme.current = dark
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 2. Create a component
|
|
50
|
+
|
|
51
|
+
Inherit from Duck's built-in components. Write your docs as a markdown class docstring:
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from duck.html.components.label import Label
|
|
55
|
+
|
|
56
|
+
class StatusLabel(Label):
|
|
57
|
+
"""
|
|
58
|
+
A themed label for displaying status text.
|
|
59
|
+
|
|
60
|
+
Usage:
|
|
61
|
+
```python
|
|
62
|
+
StatusLabel(text="Active", color="green")
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Required Props:
|
|
66
|
+
- text: Display text
|
|
67
|
+
|
|
68
|
+
Optional Props:
|
|
69
|
+
- color: The text color for the label
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
# Preview kwargs for the demo
|
|
73
|
+
docs_preview_kwargs = {"text": "Preview", "color": "#3b82f6"}
|
|
74
|
+
|
|
75
|
+
# Demo animation steps
|
|
76
|
+
docs_animation_steps = [
|
|
77
|
+
{"delay": 1000, "cursor": {"top": "50%", "left": "50%"}},
|
|
78
|
+
{"delay": 500, "target": "span", "click": True},
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
def on_create(self):
|
|
82
|
+
super().on_create()
|
|
83
|
+
|
|
84
|
+
# Update style
|
|
85
|
+
self.style.update({
|
|
86
|
+
"border-radius": "9999px",
|
|
87
|
+
"padding": "4px 12px",
|
|
88
|
+
"font-weight": "500",
|
|
89
|
+
})
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 3. Render a static demo
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from duck.html.components.container import Container
|
|
96
|
+
from duck.html.components.button import Button
|
|
97
|
+
from duck.html.components.input import Input
|
|
98
|
+
|
|
99
|
+
from quirl.components import Demo
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# Demo a built-in Button
|
|
103
|
+
button_demo = Demo(
|
|
104
|
+
component=Button(text="Submit", bg_color="blue", color="white"),
|
|
105
|
+
steps=[
|
|
106
|
+
{"delay": 1000, "cursor": {"target": "button"}},
|
|
107
|
+
{"delay": 500, "target": "button", "click": True},
|
|
108
|
+
{"delay": 2000},
|
|
109
|
+
],
|
|
110
|
+
title="Button",
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Demo a built-in Input
|
|
114
|
+
input_demo = Demo(
|
|
115
|
+
component=Input(type="text", name="email", placeholder="Email"),
|
|
116
|
+
steps=[
|
|
117
|
+
{"delay": 800, "cursor": {"top": "50%", "left": "30%"}},
|
|
118
|
+
{"delay": 1000, "target": "input", "click": True},
|
|
119
|
+
{"delay": 1500},
|
|
120
|
+
],
|
|
121
|
+
title="Input",
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# Group components
|
|
125
|
+
container = Container(children=[button_demo, input_demo])
|
|
126
|
+
|
|
127
|
+
# Render to static HTML
|
|
128
|
+
html = container.render()
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Deploy the output to GitHub Pages, Netlify, or any static host. No Python runtime required on the server.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Architecture
|
|
136
|
+
|
|
137
|
+
quirl is organized around three concerns: theming, documentation, and animation demos.
|
|
138
|
+
|
|
139
|
+
The theme system lives at the package root and provides extensible design tokens through a global class-level registry. Access the active theme anywhere via `Theme.current`.
|
|
140
|
+
|
|
141
|
+
The animation demo system lives under `quirl.components.animation.demo` and wraps any Duck component for static HTML output. It generates self-contained CSS animations and click simulations without a WebSocket connection or Python runtime on the client.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Development
|
|
146
|
+
|
|
147
|
+
**quirl** follows the Duck Framework code style:
|
|
148
|
+
|
|
149
|
+
- Google-style docstrings on every module, class, and method.
|
|
150
|
+
- Constructor-based initialization via kwargs only.
|
|
151
|
+
- No private-style method names (build_nav, not _build_nav).
|
|
152
|
+
- Components live in quirl/components/, themes and utilities at the package root.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## License
|
|
157
|
+
|
|
158
|
+
See [LICENSE](./LICENSE).
|
|
159
|
+
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77.0.3", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "quirl"
|
|
7
|
+
dynamic = ["version", "dependencies"]
|
|
8
|
+
description = "A reusable UI component library for Duck Framework's Lively reactive components."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "BSD-3-Clause"
|
|
11
|
+
license-files = ["LICENSE", "AUTHORS.md"]
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Brian Musakwa", email = "digreatbrian@gmail.com" },
|
|
14
|
+
]
|
|
15
|
+
maintainers = [
|
|
16
|
+
{ name = "Brian Musakwa", email = "digreatbrian@gmail.com" },
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 3 - Alpha",
|
|
21
|
+
"Environment :: Web Environment",
|
|
22
|
+
"Intended Audience :: Developers",
|
|
23
|
+
"Operating System :: OS Independent",
|
|
24
|
+
"Programming Language :: Python",
|
|
25
|
+
"Programming Language :: Python :: 3",
|
|
26
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
27
|
+
"Programming Language :: Python :: 3.10",
|
|
28
|
+
"Programming Language :: Python :: 3.11",
|
|
29
|
+
"Programming Language :: Python :: 3.12",
|
|
30
|
+
"Programming Language :: Python :: 3.13",
|
|
31
|
+
"Programming Language :: Python :: 3.14",
|
|
32
|
+
"Topic :: Internet :: WWW/HTTP",
|
|
33
|
+
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
|
|
34
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
35
|
+
"Topic :: Software Development :: User Interfaces",
|
|
36
|
+
]
|
|
37
|
+
keywords = [
|
|
38
|
+
"duck framework",
|
|
39
|
+
"duck",
|
|
40
|
+
"lively",
|
|
41
|
+
"lively components",
|
|
42
|
+
"reactive ui",
|
|
43
|
+
"server-side rendering",
|
|
44
|
+
"ui components",
|
|
45
|
+
"component library",
|
|
46
|
+
"web components",
|
|
47
|
+
"python ui",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[project.urls]
|
|
51
|
+
Homepage = "https://github.com/duckframework/quirl"
|
|
52
|
+
Documentation = "https://quirl.duckframework.com"
|
|
53
|
+
Source = "https://github.com/duckframework/quirl"
|
|
54
|
+
Issues = "https://github.com/duckframework/quirl/issues"
|
|
55
|
+
Contribution = "https://duckframework.com/contribute?pkg=quirl"
|
|
56
|
+
|
|
57
|
+
[project.scripts]
|
|
58
|
+
duck = "quirl.__main__:cli"
|
|
59
|
+
|
|
60
|
+
[tool.setuptools.dynamic]
|
|
61
|
+
version = {attr = "quirl.__version__"}
|
|
62
|
+
dependencies = { file = ["requirements.txt"] }
|
|
63
|
+
|
|
64
|
+
[tool.setuptools.packages.find]
|
|
65
|
+
include = ["quirl*"]
|
|
66
|
+
where = ["."]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Quirl — a modern, iOS-inspired premium component pack for Duck Framework.
|
|
3
|
+
|
|
4
|
+
Add the shared keyframes stylesheet once per page so animated components
|
|
5
|
+
work:
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
from quirl.styles import quirl_global_styles
|
|
9
|
+
|
|
10
|
+
page.add_to_head(quirl_global_styles())
|
|
11
|
+
```
|
|
12
|
+
"""
|
|
13
|
+
from quirl.version import version
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
__author__ = "Brian Musakwa"
|
|
17
|
+
__email__ = "digreatbrian@gmail.com"
|
|
18
|
+
__version__ = version
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
Main entry to Quirl command-line tool.
|
|
4
|
+
"""
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
import click
|
|
8
|
+
import setproctitle
|
|
9
|
+
|
|
10
|
+
from quirl.version import version
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@click.group(invoke_without_command=True)
|
|
14
|
+
@click.option('-V', '--version', is_flag=True, help="Show the version and exit.")
|
|
15
|
+
@click.pass_context
|
|
16
|
+
def cli(ctx, version):
|
|
17
|
+
"""
|
|
18
|
+
Quirl CLI.
|
|
19
|
+
"""
|
|
20
|
+
subcommand = ctx.invoked_subcommand
|
|
21
|
+
|
|
22
|
+
if subcommand:
|
|
23
|
+
# Set process name dynamically
|
|
24
|
+
setproctitle.setproctitle(f"quirl-{subcommand}")
|
|
25
|
+
|
|
26
|
+
if version:
|
|
27
|
+
# Show the version
|
|
28
|
+
click.echo(version)
|
|
29
|
+
|
|
30
|
+
elif not ctx.invoked_subcommand:
|
|
31
|
+
# Print usage if no subcommands are invoked
|
|
32
|
+
click.echo(ctx.get_help())
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
if __name__ == "__main__":
|
|
36
|
+
cli()
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Modern UI components for Duck Framework's Lively system.
|
|
3
|
+
|
|
4
|
+
Components are lazily imported on first access (PEP 562), so
|
|
5
|
+
`import quirl.components` stays cheap no matter how many components
|
|
6
|
+
the pack grows to — only the submodule you actually touch gets loaded.
|
|
7
|
+
|
|
8
|
+
Adding a new component only requires one line in _COMPONENT_MAP below;
|
|
9
|
+
no top-level import statement needed.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import importlib
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Maps public name -> "submodule:attribute" to import lazily
|
|
16
|
+
_COMPONENT_MAP = {
|
|
17
|
+
"Badge": "badge:Badge",
|
|
18
|
+
"Avatar": "avatar:Avatar",
|
|
19
|
+
"Alert": "alert:Alert",
|
|
20
|
+
"Skeleton": "skeleton:Skeleton",
|
|
21
|
+
"Tag": "tag:Tag",
|
|
22
|
+
"Spinner": "spinner:Spinner",
|
|
23
|
+
"Divider": "divider:Divider",
|
|
24
|
+
"CodeBlock": "code_block:CodeBlock",
|
|
25
|
+
"Demo": "animation.demo:Demo",
|
|
26
|
+
"Documentor": "documentor:Documentor",
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
__all__ = list(_COMPONENT_MAP)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def __getattr__(name: str):
|
|
33
|
+
"""
|
|
34
|
+
Lazily import and cache a component on first access.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
name: The attribute being accessed, e.g. "Badge".
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
The requested component class.
|
|
41
|
+
|
|
42
|
+
Raises:
|
|
43
|
+
AttributeError: If name isn't a known component.
|
|
44
|
+
"""
|
|
45
|
+
target = _COMPONENT_MAP.get(name)
|
|
46
|
+
|
|
47
|
+
if target is None:
|
|
48
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
49
|
+
|
|
50
|
+
module_path, attr_name = target.split(":")
|
|
51
|
+
module = importlib.import_module(f".{module_path}", __name__)
|
|
52
|
+
value = getattr(module, attr_name)
|
|
53
|
+
|
|
54
|
+
# Cache directly on the module so repeated access skips __getattr__
|
|
55
|
+
globals()[name] = value
|
|
56
|
+
return value
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def __dir__():
|
|
60
|
+
"""
|
|
61
|
+
Report the public component names for tab-completion and dir().
|
|
62
|
+
"""
|
|
63
|
+
return sorted(__all__)
|