djinn-ui 0.1.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.
- djinn_ui-0.1.0/LICENSE +21 -0
- djinn_ui-0.1.0/PKG-INFO +151 -0
- djinn_ui-0.1.0/README.md +102 -0
- djinn_ui-0.1.0/djinn/__init__.py +0 -0
- djinn_ui-0.1.0/djinn/__main__.py +4 -0
- djinn_ui-0.1.0/djinn/cli/__init__.py +0 -0
- djinn_ui-0.1.0/djinn/cli/commands/__init__.py +0 -0
- djinn_ui-0.1.0/djinn/cli/commands/add.py +36 -0
- djinn_ui-0.1.0/djinn/cli/commands/diff.py +67 -0
- djinn_ui-0.1.0/djinn/cli/commands/init.py +50 -0
- djinn_ui-0.1.0/djinn/cli/commands/list.py +27 -0
- djinn_ui-0.1.0/djinn/cli/main.py +19 -0
- djinn_ui-0.1.0/djinn/core/__init__.py +0 -0
- djinn_ui-0.1.0/djinn/core/config.py +57 -0
- djinn_ui-0.1.0/djinn/core/installer.py +52 -0
- djinn_ui-0.1.0/djinn/core/registry.py +105 -0
- djinn_ui-0.1.0/djinn/core/utils.py +21 -0
- djinn_ui-0.1.0/djinn/registry/__init__.py +1 -0
- djinn_ui-0.1.0/djinn/registry/components/accordion/accordion.html +3 -0
- djinn_ui-0.1.0/djinn/registry/components/accordion/preview.md +22 -0
- djinn_ui-0.1.0/djinn/registry/components/accordion/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/activity-feed/activity-feed.html +6 -0
- djinn_ui-0.1.0/djinn/registry/components/activity-feed/preview.md +17 -0
- djinn_ui-0.1.0/djinn/registry/components/activity-feed/registry.json +13 -0
- djinn_ui-0.1.0/djinn/registry/components/alert/alert.html +13 -0
- djinn_ui-0.1.0/djinn/registry/components/alert/preview.md +11 -0
- djinn_ui-0.1.0/djinn/registry/components/alert/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/alert-dialog/alert-dialog.html +7 -0
- djinn_ui-0.1.0/djinn/registry/components/alert-dialog/preview.md +9 -0
- djinn_ui-0.1.0/djinn/registry/components/alert-dialog/registry.json +13 -0
- djinn_ui-0.1.0/djinn/registry/components/analytics-card/analytics-card.html +27 -0
- djinn_ui-0.1.0/djinn/registry/components/analytics-card/preview.md +9 -0
- djinn_ui-0.1.0/djinn/registry/components/analytics-card/registry.json +13 -0
- djinn_ui-0.1.0/djinn/registry/components/avatar/avatar.html +9 -0
- djinn_ui-0.1.0/djinn/registry/components/avatar/preview.md +10 -0
- djinn_ui-0.1.0/djinn/registry/components/avatar/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/badge/badge.html +9 -0
- djinn_ui-0.1.0/djinn/registry/components/badge/preview.md +9 -0
- djinn_ui-0.1.0/djinn/registry/components/badge/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/breadcrumbs/breadcrumbs.html +5 -0
- djinn_ui-0.1.0/djinn/registry/components/breadcrumbs/preview.md +19 -0
- djinn_ui-0.1.0/djinn/registry/components/breadcrumbs/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/button/button.html +21 -0
- djinn_ui-0.1.0/djinn/registry/components/button/button.py +26 -0
- djinn_ui-0.1.0/djinn/registry/components/button/preview.md +36 -0
- djinn_ui-0.1.0/djinn/registry/components/button/registry.json +16 -0
- djinn_ui-0.1.0/djinn/registry/components/card/card.html +22 -0
- djinn_ui-0.1.0/djinn/registry/components/card/preview.md +14 -0
- djinn_ui-0.1.0/djinn/registry/components/card/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/checkbox/checkbox.html +19 -0
- djinn_ui-0.1.0/djinn/registry/components/checkbox/preview.md +9 -0
- djinn_ui-0.1.0/djinn/registry/components/checkbox/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/checkout-summary/checkout-summary.html +22 -0
- djinn_ui-0.1.0/djinn/registry/components/checkout-summary/preview.md +14 -0
- djinn_ui-0.1.0/djinn/registry/components/checkout-summary/registry.json +13 -0
- djinn_ui-0.1.0/djinn/registry/components/combobox/combobox.html +29 -0
- djinn_ui-0.1.0/djinn/registry/components/combobox/preview.md +22 -0
- djinn_ui-0.1.0/djinn/registry/components/combobox/registry.json +13 -0
- djinn_ui-0.1.0/djinn/registry/components/confirmation-dialog/confirmation-dialog.html +7 -0
- djinn_ui-0.1.0/djinn/registry/components/confirmation-dialog/preview.md +11 -0
- djinn_ui-0.1.0/djinn/registry/components/confirmation-dialog/registry.json +13 -0
- djinn_ui-0.1.0/djinn/registry/components/container/container.html +3 -0
- djinn_ui-0.1.0/djinn/registry/components/container/preview.md +11 -0
- djinn_ui-0.1.0/djinn/registry/components/container/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/dashboard-sidebar/dashboard-sidebar.html +20 -0
- djinn_ui-0.1.0/djinn/registry/components/dashboard-sidebar/preview.md +16 -0
- djinn_ui-0.1.0/djinn/registry/components/dashboard-sidebar/registry.json +13 -0
- djinn_ui-0.1.0/djinn/registry/components/drawer/drawer.html +58 -0
- djinn_ui-0.1.0/djinn/registry/components/drawer/preview.md +11 -0
- djinn_ui-0.1.0/djinn/registry/components/drawer/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/feature-card/feature-card.html +9 -0
- djinn_ui-0.1.0/djinn/registry/components/feature-card/preview.md +9 -0
- djinn_ui-0.1.0/djinn/registry/components/feature-card/registry.json +13 -0
- djinn_ui-0.1.0/djinn/registry/components/file-input/file-input.html +7 -0
- djinn_ui-0.1.0/djinn/registry/components/file-input/preview.md +9 -0
- djinn_ui-0.1.0/djinn/registry/components/file-input/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/form-group/form-group.html +15 -0
- djinn_ui-0.1.0/djinn/registry/components/form-group/preview.md +11 -0
- djinn_ui-0.1.0/djinn/registry/components/form-group/registry.json +13 -0
- djinn_ui-0.1.0/djinn/registry/components/grid/grid.html +9 -0
- djinn_ui-0.1.0/djinn/registry/components/grid/preview.md +13 -0
- djinn_ui-0.1.0/djinn/registry/components/grid/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/icon-button/icon-button.html +1 -0
- djinn_ui-0.1.0/djinn/registry/components/icon-button/preview.md +9 -0
- djinn_ui-0.1.0/djinn/registry/components/icon-button/registry.json +13 -0
- djinn_ui-0.1.0/djinn/registry/components/input/input.html +11 -0
- djinn_ui-0.1.0/djinn/registry/components/input/preview.md +9 -0
- djinn_ui-0.1.0/djinn/registry/components/input/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/label/label.html +8 -0
- djinn_ui-0.1.0/djinn/registry/components/label/preview.md +9 -0
- djinn_ui-0.1.0/djinn/registry/components/label/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/login-form/login-form.html +19 -0
- djinn_ui-0.1.0/djinn/registry/components/login-form/preview.md +9 -0
- djinn_ui-0.1.0/djinn/registry/components/login-form/registry.json +13 -0
- djinn_ui-0.1.0/djinn/registry/components/metric-grid/metric-grid.html +3 -0
- djinn_ui-0.1.0/djinn/registry/components/metric-grid/preview.md +14 -0
- djinn_ui-0.1.0/djinn/registry/components/metric-grid/registry.json +13 -0
- djinn_ui-0.1.0/djinn/registry/components/modal/modal.html +65 -0
- djinn_ui-0.1.0/djinn/registry/components/modal/preview.md +19 -0
- djinn_ui-0.1.0/djinn/registry/components/modal/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/navbar/navbar.html +18 -0
- djinn_ui-0.1.0/djinn/registry/components/navbar/preview.md +15 -0
- djinn_ui-0.1.0/djinn/registry/components/navbar/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/pagination/pagination.html +11 -0
- djinn_ui-0.1.0/djinn/registry/components/pagination/preview.md +12 -0
- djinn_ui-0.1.0/djinn/registry/components/pagination/registry.json +13 -0
- djinn_ui-0.1.0/djinn/registry/components/password-input/password-input.html +20 -0
- djinn_ui-0.1.0/djinn/registry/components/password-input/preview.md +9 -0
- djinn_ui-0.1.0/djinn/registry/components/password-input/registry.json +13 -0
- djinn_ui-0.1.0/djinn/registry/components/password-reset-form/password-reset-form.html +15 -0
- djinn_ui-0.1.0/djinn/registry/components/password-reset-form/preview.md +9 -0
- djinn_ui-0.1.0/djinn/registry/components/password-reset-form/registry.json +13 -0
- djinn_ui-0.1.0/djinn/registry/components/pricing-card/preview.md +14 -0
- djinn_ui-0.1.0/djinn/registry/components/pricing-card/pricing-card.html +14 -0
- djinn_ui-0.1.0/djinn/registry/components/pricing-card/registry.json +13 -0
- djinn_ui-0.1.0/djinn/registry/components/product-card/preview.md +9 -0
- djinn_ui-0.1.0/djinn/registry/components/product-card/product-card.html +17 -0
- djinn_ui-0.1.0/djinn/registry/components/product-card/registry.json +13 -0
- djinn_ui-0.1.0/djinn/registry/components/profile-card/preview.md +12 -0
- djinn_ui-0.1.0/djinn/registry/components/profile-card/profile-card.html +16 -0
- djinn_ui-0.1.0/djinn/registry/components/profile-card/registry.json +13 -0
- djinn_ui-0.1.0/djinn/registry/components/progress/preview.md +9 -0
- djinn_ui-0.1.0/djinn/registry/components/progress/progress.html +12 -0
- djinn_ui-0.1.0/djinn/registry/components/progress/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/radio/preview.md +9 -0
- djinn_ui-0.1.0/djinn/registry/components/radio/radio.html +20 -0
- djinn_ui-0.1.0/djinn/registry/components/radio/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/search-input/preview.md +9 -0
- djinn_ui-0.1.0/djinn/registry/components/search-input/registry.json +13 -0
- djinn_ui-0.1.0/djinn/registry/components/search-input/search-input.html +11 -0
- djinn_ui-0.1.0/djinn/registry/components/section/preview.md +12 -0
- djinn_ui-0.1.0/djinn/registry/components/section/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/section/section.html +3 -0
- djinn_ui-0.1.0/djinn/registry/components/select/preview.md +12 -0
- djinn_ui-0.1.0/djinn/registry/components/select/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/select/select.html +17 -0
- djinn_ui-0.1.0/djinn/registry/components/separator/preview.md +10 -0
- djinn_ui-0.1.0/djinn/registry/components/separator/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/separator/separator.html +7 -0
- djinn_ui-0.1.0/djinn/registry/components/sheet/preview.md +11 -0
- djinn_ui-0.1.0/djinn/registry/components/sheet/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/sheet/sheet.html +58 -0
- djinn_ui-0.1.0/djinn/registry/components/sidebar/preview.md +12 -0
- djinn_ui-0.1.0/djinn/registry/components/sidebar/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/sidebar/sidebar.html +14 -0
- djinn_ui-0.1.0/djinn/registry/components/signup-form/preview.md +9 -0
- djinn_ui-0.1.0/djinn/registry/components/signup-form/registry.json +13 -0
- djinn_ui-0.1.0/djinn/registry/components/signup-form/signup-form.html +23 -0
- djinn_ui-0.1.0/djinn/registry/components/skeleton/preview.md +10 -0
- djinn_ui-0.1.0/djinn/registry/components/skeleton/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/skeleton/skeleton.html +1 -0
- djinn_ui-0.1.0/djinn/registry/components/spinner/preview.md +9 -0
- djinn_ui-0.1.0/djinn/registry/components/spinner/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/spinner/spinner.html +14 -0
- djinn_ui-0.1.0/djinn/registry/components/split-button/preview.md +9 -0
- djinn_ui-0.1.0/djinn/registry/components/split-button/registry.json +13 -0
- djinn_ui-0.1.0/djinn/registry/components/split-button/split-button.html +4 -0
- djinn_ui-0.1.0/djinn/registry/components/stack/preview.md +12 -0
- djinn_ui-0.1.0/djinn/registry/components/stack/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/stack/stack.html +7 -0
- djinn_ui-0.1.0/djinn/registry/components/stats-card/preview.md +9 -0
- djinn_ui-0.1.0/djinn/registry/components/stats-card/registry.json +13 -0
- djinn_ui-0.1.0/djinn/registry/components/stats-card/stats-card.html +16 -0
- djinn_ui-0.1.0/djinn/registry/components/switch/preview.md +9 -0
- djinn_ui-0.1.0/djinn/registry/components/switch/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/switch/switch.html +20 -0
- djinn_ui-0.1.0/djinn/registry/components/table/preview.md +24 -0
- djinn_ui-0.1.0/djinn/registry/components/table/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/table/table.html +5 -0
- djinn_ui-0.1.0/djinn/registry/components/tabs/preview.md +29 -0
- djinn_ui-0.1.0/djinn/registry/components/tabs/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/tabs/tabs.html +8 -0
- djinn_ui-0.1.0/djinn/registry/components/textarea/preview.md +9 -0
- djinn_ui-0.1.0/djinn/registry/components/textarea/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/textarea/textarea.html +10 -0
- djinn_ui-0.1.0/djinn/registry/components/timeline/preview.md +19 -0
- djinn_ui-0.1.0/djinn/registry/components/timeline/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/timeline/timeline.html +3 -0
- djinn_ui-0.1.0/djinn/registry/components/toast/preview.md +12 -0
- djinn_ui-0.1.0/djinn/registry/components/toast/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/toast/toast.html +26 -0
- djinn_ui-0.1.0/djinn/registry/components/tooltip/preview.md +11 -0
- djinn_ui-0.1.0/djinn/registry/components/tooltip/registry.json +12 -0
- djinn_ui-0.1.0/djinn/registry/components/tooltip/tooltip.html +24 -0
- djinn_ui-0.1.0/djinn/registry/index.json +59 -0
- djinn_ui-0.1.0/djinn/tests/__init__.py +0 -0
- djinn_ui-0.1.0/djinn/tests/test_cli.py +53 -0
- djinn_ui-0.1.0/djinn/tests/test_core.py +62 -0
- djinn_ui-0.1.0/djinn_ui.egg-info/PKG-INFO +151 -0
- djinn_ui-0.1.0/djinn_ui.egg-info/SOURCES.txt +194 -0
- djinn_ui-0.1.0/djinn_ui.egg-info/dependency_links.txt +1 -0
- djinn_ui-0.1.0/djinn_ui.egg-info/entry_points.txt +2 -0
- djinn_ui-0.1.0/djinn_ui.egg-info/requires.txt +5 -0
- djinn_ui-0.1.0/djinn_ui.egg-info/top_level.txt +1 -0
- djinn_ui-0.1.0/pyproject.toml +49 -0
- djinn_ui-0.1.0/setup.cfg +4 -0
djinn_ui-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SoJab Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
djinn_ui-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: djinn-ui
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A shadcn-style component installer for Django.
|
|
5
|
+
Author-email: Djinn Team <hello@djinn.dev>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 SoJab Team
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Homepage, https://github.com/josephjbassey/djinn
|
|
29
|
+
Project-URL: Repository, https://github.com/josephjbassey/djinn
|
|
30
|
+
Project-URL: Bug Tracker, https://github.com/josephjbassey/djinn/issues
|
|
31
|
+
Keywords: django,shadcn,components,cli,frontend
|
|
32
|
+
Classifier: Development Status :: 4 - Beta
|
|
33
|
+
Classifier: Intended Audience :: Developers
|
|
34
|
+
Classifier: Framework :: Django
|
|
35
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
36
|
+
Classifier: Programming Language :: Python :: 3
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
41
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
42
|
+
Requires-Python: >=3.8
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
License-File: LICENSE
|
|
45
|
+
Requires-Dist: click>=8.4.1
|
|
46
|
+
Requires-Dist: tabulate>=0.10.0
|
|
47
|
+
Requires-Dist: importlib-resources; python_version < "3.9"
|
|
48
|
+
Dynamic: license-file
|
|
49
|
+
|
|
50
|
+
# Djinn UI
|
|
51
|
+
|
|
52
|
+
**Djinn is a shadcn-style component system for Django.**
|
|
53
|
+
|
|
54
|
+
It lets developers visually build, install, and own UI components directly inside their Django projects using a simple CLI.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## ✨ Philosophy
|
|
59
|
+
|
|
60
|
+
Djinn is NOT a UI framework.
|
|
61
|
+
|
|
62
|
+
Djinn is a **source-code delivery system**.
|
|
63
|
+
|
|
64
|
+
Once a component is installed, it belongs to your project — fully editable, fully owned.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## ⚡ Features
|
|
69
|
+
|
|
70
|
+
* CLI-based component installation
|
|
71
|
+
* Registry-driven architecture
|
|
72
|
+
* Copy-paste ownership model (like shadcn/ui)
|
|
73
|
+
* Django-native templates
|
|
74
|
+
* TailwindCSS-based styling
|
|
75
|
+
* Alpine.js / HTMX support
|
|
76
|
+
* Versioned components
|
|
77
|
+
* Diff and update system
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 📦 Installation (CLI)
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
pip install djinn-ui
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 🚀 Quick Start
|
|
90
|
+
|
|
91
|
+
Initialize Djinn in your Django project:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
djinn init
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Add a component:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
djinn add button
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 🧩 Example Component Usage
|
|
106
|
+
|
|
107
|
+
```django
|
|
108
|
+
{% include "components/button.html" %}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 📁 Registry System
|
|
114
|
+
|
|
115
|
+
Djinn components are stored in a remote registry:
|
|
116
|
+
|
|
117
|
+
* Each component is versioned
|
|
118
|
+
* Includes templates, logic, and metadata
|
|
119
|
+
* CLI fetches and installs directly into your project
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 🏗 Architecture
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
Registry → CLI → Django Project
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
No runtime dependency.
|
|
130
|
+
No locked library.
|
|
131
|
+
You own the code.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 🔮 Roadmap
|
|
136
|
+
|
|
137
|
+
* Visual Component Builder (Djinn Canvas)
|
|
138
|
+
* AI component generator
|
|
139
|
+
* Component marketplace
|
|
140
|
+
* Figma import
|
|
141
|
+
* Team registries
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## 🤝 Contribution
|
|
146
|
+
|
|
147
|
+
We welcome contributions to:
|
|
148
|
+
|
|
149
|
+
* Registry components
|
|
150
|
+
* CLI improvements
|
|
151
|
+
* Documentation
|
djinn_ui-0.1.0/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Djinn UI
|
|
2
|
+
|
|
3
|
+
**Djinn is a shadcn-style component system for Django.**
|
|
4
|
+
|
|
5
|
+
It lets developers visually build, install, and own UI components directly inside their Django projects using a simple CLI.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## ✨ Philosophy
|
|
10
|
+
|
|
11
|
+
Djinn is NOT a UI framework.
|
|
12
|
+
|
|
13
|
+
Djinn is a **source-code delivery system**.
|
|
14
|
+
|
|
15
|
+
Once a component is installed, it belongs to your project — fully editable, fully owned.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## ⚡ Features
|
|
20
|
+
|
|
21
|
+
* CLI-based component installation
|
|
22
|
+
* Registry-driven architecture
|
|
23
|
+
* Copy-paste ownership model (like shadcn/ui)
|
|
24
|
+
* Django-native templates
|
|
25
|
+
* TailwindCSS-based styling
|
|
26
|
+
* Alpine.js / HTMX support
|
|
27
|
+
* Versioned components
|
|
28
|
+
* Diff and update system
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 📦 Installation (CLI)
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install djinn-ui
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 🚀 Quick Start
|
|
41
|
+
|
|
42
|
+
Initialize Djinn in your Django project:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
djinn init
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Add a component:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
djinn add button
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 🧩 Example Component Usage
|
|
57
|
+
|
|
58
|
+
```django
|
|
59
|
+
{% include "components/button.html" %}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 📁 Registry System
|
|
65
|
+
|
|
66
|
+
Djinn components are stored in a remote registry:
|
|
67
|
+
|
|
68
|
+
* Each component is versioned
|
|
69
|
+
* Includes templates, logic, and metadata
|
|
70
|
+
* CLI fetches and installs directly into your project
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 🏗 Architecture
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
Registry → CLI → Django Project
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
No runtime dependency.
|
|
81
|
+
No locked library.
|
|
82
|
+
You own the code.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 🔮 Roadmap
|
|
87
|
+
|
|
88
|
+
* Visual Component Builder (Djinn Canvas)
|
|
89
|
+
* AI component generator
|
|
90
|
+
* Component marketplace
|
|
91
|
+
* Figma import
|
|
92
|
+
* Team registries
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 🤝 Contribution
|
|
97
|
+
|
|
98
|
+
We welcome contributions to:
|
|
99
|
+
|
|
100
|
+
* Registry components
|
|
101
|
+
* CLI improvements
|
|
102
|
+
* Documentation
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from djinn.core.config import Config, DEFAULT_CONFIG
|
|
3
|
+
from djinn.core.registry import Registry
|
|
4
|
+
from djinn.core.installer import Installer
|
|
5
|
+
|
|
6
|
+
@click.command()
|
|
7
|
+
@click.argument("component")
|
|
8
|
+
@click.option("--force", is_flag=True, help="Overwrite existing files.")
|
|
9
|
+
def add(component, force):
|
|
10
|
+
"""Add a component to the project."""
|
|
11
|
+
config = Config.load()
|
|
12
|
+
if not config:
|
|
13
|
+
# Fallback to bundled registry if not initialized
|
|
14
|
+
registry_url = DEFAULT_CONFIG["registry_url"]
|
|
15
|
+
config = Config(
|
|
16
|
+
registry_url=registry_url,
|
|
17
|
+
output=DEFAULT_CONFIG["output"]
|
|
18
|
+
)
|
|
19
|
+
click.echo("Djinn not initialized. Using bundled registry defaults.")
|
|
20
|
+
else:
|
|
21
|
+
registry_url = config.registry_url
|
|
22
|
+
|
|
23
|
+
registry = Registry(registry_url)
|
|
24
|
+
installer = Installer(config, registry)
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
installed_files = installer.install(component, force=force)
|
|
28
|
+
click.echo(f"Successfully installed '{component}':")
|
|
29
|
+
for _, path in installed_files:
|
|
30
|
+
click.echo(f" - {path}")
|
|
31
|
+
except FileExistsError as e:
|
|
32
|
+
raise click.ClickException(f"{e} Use --force to overwrite.")
|
|
33
|
+
except (ValueError, FileNotFoundError) as e:
|
|
34
|
+
raise click.ClickException(str(e))
|
|
35
|
+
except Exception as e:
|
|
36
|
+
raise click.ClickException(f"An unexpected error occurred: {e}")
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from djinn.core.config import Config, DEFAULT_CONFIG
|
|
4
|
+
from djinn.core.registry import Registry
|
|
5
|
+
from djinn.core.utils import calculate_hash
|
|
6
|
+
|
|
7
|
+
@click.command()
|
|
8
|
+
@click.argument("component")
|
|
9
|
+
def diff(component):
|
|
10
|
+
"""Compare registry version vs local installed version."""
|
|
11
|
+
config = Config.load()
|
|
12
|
+
if not config:
|
|
13
|
+
registry_url = DEFAULT_CONFIG["registry_url"]
|
|
14
|
+
config = Config(registry_url=registry_url, output=DEFAULT_CONFIG["output"])
|
|
15
|
+
click.echo("Djinn not initialized. Diffing against bundled registry.")
|
|
16
|
+
else:
|
|
17
|
+
registry_url = config.registry_url
|
|
18
|
+
|
|
19
|
+
registry = Registry(registry_url)
|
|
20
|
+
metadata = registry.load_component(component)
|
|
21
|
+
|
|
22
|
+
if not metadata:
|
|
23
|
+
raise click.ClickException(f"Component '{component}' not found in registry.")
|
|
24
|
+
|
|
25
|
+
click.echo(f"Diffing '{component}' (Registry Version: {metadata.version}):")
|
|
26
|
+
|
|
27
|
+
any_diff = False
|
|
28
|
+
for file_type, file_name in metadata.files.items():
|
|
29
|
+
if file_type == "template":
|
|
30
|
+
dest_base = Path(metadata.install.get("template_path", config.get_template_path()))
|
|
31
|
+
elif file_type == "python":
|
|
32
|
+
dest_base = Path(metadata.install.get("python_path", config.get_python_path()))
|
|
33
|
+
else:
|
|
34
|
+
click.echo(f" [WARNING] Unknown file type '{file_type}' for {file_name}. Skipping.")
|
|
35
|
+
continue
|
|
36
|
+
|
|
37
|
+
dest_path = dest_base / file_name
|
|
38
|
+
registry_file_path = registry.get_component_file_path(component, file_name)
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
# Fetch registry content
|
|
42
|
+
src_content = registry.fetch_content(registry_file_path)
|
|
43
|
+
src_hash = calculate_hash(src_content)
|
|
44
|
+
except Exception:
|
|
45
|
+
click.echo(f" [MISSING_REGISTRY] {registry_file_path}")
|
|
46
|
+
any_diff = True
|
|
47
|
+
continue
|
|
48
|
+
|
|
49
|
+
if not dest_path.exists():
|
|
50
|
+
click.echo(f" [MISSING_LOCAL] {dest_path}")
|
|
51
|
+
any_diff = True
|
|
52
|
+
continue
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
dest_hash = calculate_hash(dest_path)
|
|
56
|
+
|
|
57
|
+
if src_hash != dest_hash:
|
|
58
|
+
click.echo(f" [CHANGED] {dest_path}")
|
|
59
|
+
any_diff = True
|
|
60
|
+
else:
|
|
61
|
+
click.echo(f" [MATCH] {dest_path}")
|
|
62
|
+
except Exception as e:
|
|
63
|
+
click.echo(f" [ERROR] Could not diff {dest_path}: {e}")
|
|
64
|
+
any_diff = True
|
|
65
|
+
|
|
66
|
+
if not any_diff:
|
|
67
|
+
click.echo("Local version matches registry version.")
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import os
|
|
3
|
+
from djinn.core.config import Config, DEFAULT_CONFIG
|
|
4
|
+
|
|
5
|
+
@click.command()
|
|
6
|
+
@click.option("--yes", "-y", is_flag=True, help="Skip interactive prompts and use defaults.")
|
|
7
|
+
@click.option("--defaults", is_flag=True, help="Skip interactive prompts and use defaults.")
|
|
8
|
+
def init(yes, defaults):
|
|
9
|
+
"""Initialize Djinn in the current project."""
|
|
10
|
+
if Config.load():
|
|
11
|
+
if not click.confirm("Djinn is already initialized. Overwrite existing configuration?", default=False):
|
|
12
|
+
return
|
|
13
|
+
|
|
14
|
+
if yes or defaults:
|
|
15
|
+
config = Config.init()
|
|
16
|
+
click.echo(f"Initialized djinn.config.json with defaults.")
|
|
17
|
+
return
|
|
18
|
+
|
|
19
|
+
# Interactive prompts
|
|
20
|
+
registry_url = click.prompt(
|
|
21
|
+
"Registry URL (or path)",
|
|
22
|
+
default=DEFAULT_CONFIG["registry_url"]
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
template_path = click.prompt(
|
|
26
|
+
"Where should Django templates be installed?",
|
|
27
|
+
default=DEFAULT_CONFIG["output"]["templates"]
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
python_path = click.prompt(
|
|
31
|
+
"Where should Python component files be installed?",
|
|
32
|
+
default=DEFAULT_CONFIG["output"]["python"]
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
config_data = {
|
|
36
|
+
"registry_url": registry_url,
|
|
37
|
+
"output": {
|
|
38
|
+
"templates": template_path,
|
|
39
|
+
"python": python_path
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
config = Config(
|
|
44
|
+
registry_url=config_data["registry_url"],
|
|
45
|
+
output=config_data["output"]
|
|
46
|
+
)
|
|
47
|
+
config.save()
|
|
48
|
+
|
|
49
|
+
click.echo("\nDjinn initialized successfully!")
|
|
50
|
+
click.echo(f"Config saved to djinn.config.json")
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from tabulate import tabulate
|
|
3
|
+
from djinn.core.config import Config, DEFAULT_CONFIG
|
|
4
|
+
from djinn.core.registry import Registry
|
|
5
|
+
|
|
6
|
+
@click.command()
|
|
7
|
+
def list_components():
|
|
8
|
+
"""List available components in the registry."""
|
|
9
|
+
config = Config.load()
|
|
10
|
+
if config:
|
|
11
|
+
registry_url = config.registry_url
|
|
12
|
+
else:
|
|
13
|
+
registry_url = DEFAULT_CONFIG["registry_url"]
|
|
14
|
+
click.echo("Djinn not initialized. Showing components from bundled registry.")
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
registry = Registry(registry_url)
|
|
18
|
+
components = registry.list_components()
|
|
19
|
+
|
|
20
|
+
if not components:
|
|
21
|
+
click.echo(f"No components found in registry: {registry_url}")
|
|
22
|
+
return
|
|
23
|
+
|
|
24
|
+
table_data = [[c.name, c.version] for c in components]
|
|
25
|
+
click.echo(tabulate(table_data, headers=["Component", "Version"], tablefmt="simple"))
|
|
26
|
+
except Exception as e:
|
|
27
|
+
raise click.ClickException(f"Failed to list components: {e}")
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from djinn.cli.commands.init import init
|
|
3
|
+
from djinn.cli.commands.add import add
|
|
4
|
+
from djinn.cli.commands.list import list_components
|
|
5
|
+
from djinn.cli.commands.diff import diff
|
|
6
|
+
|
|
7
|
+
@click.group()
|
|
8
|
+
@click.version_option()
|
|
9
|
+
def cli():
|
|
10
|
+
"""Djinn: A shadcn-style component installer for Django."""
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
cli.add_command(init)
|
|
14
|
+
cli.add_command(add)
|
|
15
|
+
cli.add_command(list_components, name="list")
|
|
16
|
+
cli.add_command(diff)
|
|
17
|
+
|
|
18
|
+
if __name__ == "__main__":
|
|
19
|
+
cli()
|
|
File without changes
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Dict, Any, Optional
|
|
5
|
+
|
|
6
|
+
CONFIG_FILE = "djinn.config.json"
|
|
7
|
+
|
|
8
|
+
DEFAULT_CONFIG = {
|
|
9
|
+
"registry_url": "__bundled__",
|
|
10
|
+
"output": {
|
|
11
|
+
"templates": "templates/components",
|
|
12
|
+
"python": "components"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
class Config:
|
|
17
|
+
def __init__(self, registry_url: str, output: Dict[str, str]):
|
|
18
|
+
self.registry_url = registry_url
|
|
19
|
+
self.output = output
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def load(cls) -> Optional['Config']:
|
|
23
|
+
if not os.path.exists(CONFIG_FILE):
|
|
24
|
+
return None
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
with open(CONFIG_FILE, 'r') as f:
|
|
28
|
+
data = json.load(f)
|
|
29
|
+
return cls(
|
|
30
|
+
registry_url=data.get("registry_url", DEFAULT_CONFIG["registry_url"]),
|
|
31
|
+
output=data.get("output", DEFAULT_CONFIG["output"])
|
|
32
|
+
)
|
|
33
|
+
except (json.JSONDecodeError, IOError):
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
def save(self):
|
|
37
|
+
data = {
|
|
38
|
+
"registry_url": self.registry_url,
|
|
39
|
+
"output": self.output
|
|
40
|
+
}
|
|
41
|
+
with open(CONFIG_FILE, 'w') as f:
|
|
42
|
+
json.dump(data, f, indent=2)
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def init(cls) -> 'Config':
|
|
46
|
+
config = cls(
|
|
47
|
+
registry_url=DEFAULT_CONFIG["registry_url"],
|
|
48
|
+
output=DEFAULT_CONFIG["output"]
|
|
49
|
+
)
|
|
50
|
+
config.save()
|
|
51
|
+
return config
|
|
52
|
+
|
|
53
|
+
def get_template_path(self) -> Path:
|
|
54
|
+
return Path(self.output.get("templates", DEFAULT_CONFIG["output"]["templates"]))
|
|
55
|
+
|
|
56
|
+
def get_python_path(self) -> Path:
|
|
57
|
+
return Path(self.output.get("python", DEFAULT_CONFIG["output"]["python"]))
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import List, Tuple
|
|
4
|
+
from djinn.core.config import Config
|
|
5
|
+
from djinn.core.registry import ComponentMetadata, Registry
|
|
6
|
+
|
|
7
|
+
class Installer:
|
|
8
|
+
def __init__(self, config: Config, registry: Registry):
|
|
9
|
+
self.config = config
|
|
10
|
+
self.registry = registry
|
|
11
|
+
|
|
12
|
+
def install(self, component_name: str, force: bool = False) -> List[Tuple[str, Path]]:
|
|
13
|
+
metadata = self.registry.load_component(component_name)
|
|
14
|
+
if not metadata:
|
|
15
|
+
raise ValueError(f"Component '{component_name}' not found in registry.")
|
|
16
|
+
|
|
17
|
+
# Phase 1: Validation
|
|
18
|
+
files_to_install = []
|
|
19
|
+
for file_type, file_name in metadata.files.items():
|
|
20
|
+
if file_type == "template":
|
|
21
|
+
dest_base = Path(metadata.install.get("template_path", self.config.get_template_path()))
|
|
22
|
+
elif file_type == "python":
|
|
23
|
+
dest_base = Path(metadata.install.get("python_path", self.config.get_python_path()))
|
|
24
|
+
else:
|
|
25
|
+
dest_base = Path("components")
|
|
26
|
+
|
|
27
|
+
dest_path = dest_base / file_name
|
|
28
|
+
|
|
29
|
+
# Check if local file exists
|
|
30
|
+
if dest_path.exists() and not force:
|
|
31
|
+
raise FileExistsError(f"File '{dest_path}' already exists. Use --force to overwrite.")
|
|
32
|
+
|
|
33
|
+
# Relative path in registry
|
|
34
|
+
registry_file_path = self.registry.get_component_file_path(component_name, file_name)
|
|
35
|
+
|
|
36
|
+
files_to_install.append((file_type, registry_file_path, dest_path))
|
|
37
|
+
|
|
38
|
+
# Phase 2: Installation
|
|
39
|
+
installed_files = []
|
|
40
|
+
for file_type, registry_file_path, dest_path in files_to_install:
|
|
41
|
+
try:
|
|
42
|
+
content = self.registry.fetch_content(registry_file_path)
|
|
43
|
+
except FileNotFoundError:
|
|
44
|
+
raise FileNotFoundError(f"Source file '{registry_file_path}' not found in registry.")
|
|
45
|
+
|
|
46
|
+
dest_path.parent.mkdir(parents=True, exist_ok=True)
|
|
47
|
+
with open(dest_path, "wb") as f:
|
|
48
|
+
f.write(content)
|
|
49
|
+
|
|
50
|
+
installed_files.append((file_type, dest_path))
|
|
51
|
+
|
|
52
|
+
return installed_files
|