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.
Files changed (196) hide show
  1. djinn_ui-0.1.0/LICENSE +21 -0
  2. djinn_ui-0.1.0/PKG-INFO +151 -0
  3. djinn_ui-0.1.0/README.md +102 -0
  4. djinn_ui-0.1.0/djinn/__init__.py +0 -0
  5. djinn_ui-0.1.0/djinn/__main__.py +4 -0
  6. djinn_ui-0.1.0/djinn/cli/__init__.py +0 -0
  7. djinn_ui-0.1.0/djinn/cli/commands/__init__.py +0 -0
  8. djinn_ui-0.1.0/djinn/cli/commands/add.py +36 -0
  9. djinn_ui-0.1.0/djinn/cli/commands/diff.py +67 -0
  10. djinn_ui-0.1.0/djinn/cli/commands/init.py +50 -0
  11. djinn_ui-0.1.0/djinn/cli/commands/list.py +27 -0
  12. djinn_ui-0.1.0/djinn/cli/main.py +19 -0
  13. djinn_ui-0.1.0/djinn/core/__init__.py +0 -0
  14. djinn_ui-0.1.0/djinn/core/config.py +57 -0
  15. djinn_ui-0.1.0/djinn/core/installer.py +52 -0
  16. djinn_ui-0.1.0/djinn/core/registry.py +105 -0
  17. djinn_ui-0.1.0/djinn/core/utils.py +21 -0
  18. djinn_ui-0.1.0/djinn/registry/__init__.py +1 -0
  19. djinn_ui-0.1.0/djinn/registry/components/accordion/accordion.html +3 -0
  20. djinn_ui-0.1.0/djinn/registry/components/accordion/preview.md +22 -0
  21. djinn_ui-0.1.0/djinn/registry/components/accordion/registry.json +12 -0
  22. djinn_ui-0.1.0/djinn/registry/components/activity-feed/activity-feed.html +6 -0
  23. djinn_ui-0.1.0/djinn/registry/components/activity-feed/preview.md +17 -0
  24. djinn_ui-0.1.0/djinn/registry/components/activity-feed/registry.json +13 -0
  25. djinn_ui-0.1.0/djinn/registry/components/alert/alert.html +13 -0
  26. djinn_ui-0.1.0/djinn/registry/components/alert/preview.md +11 -0
  27. djinn_ui-0.1.0/djinn/registry/components/alert/registry.json +12 -0
  28. djinn_ui-0.1.0/djinn/registry/components/alert-dialog/alert-dialog.html +7 -0
  29. djinn_ui-0.1.0/djinn/registry/components/alert-dialog/preview.md +9 -0
  30. djinn_ui-0.1.0/djinn/registry/components/alert-dialog/registry.json +13 -0
  31. djinn_ui-0.1.0/djinn/registry/components/analytics-card/analytics-card.html +27 -0
  32. djinn_ui-0.1.0/djinn/registry/components/analytics-card/preview.md +9 -0
  33. djinn_ui-0.1.0/djinn/registry/components/analytics-card/registry.json +13 -0
  34. djinn_ui-0.1.0/djinn/registry/components/avatar/avatar.html +9 -0
  35. djinn_ui-0.1.0/djinn/registry/components/avatar/preview.md +10 -0
  36. djinn_ui-0.1.0/djinn/registry/components/avatar/registry.json +12 -0
  37. djinn_ui-0.1.0/djinn/registry/components/badge/badge.html +9 -0
  38. djinn_ui-0.1.0/djinn/registry/components/badge/preview.md +9 -0
  39. djinn_ui-0.1.0/djinn/registry/components/badge/registry.json +12 -0
  40. djinn_ui-0.1.0/djinn/registry/components/breadcrumbs/breadcrumbs.html +5 -0
  41. djinn_ui-0.1.0/djinn/registry/components/breadcrumbs/preview.md +19 -0
  42. djinn_ui-0.1.0/djinn/registry/components/breadcrumbs/registry.json +12 -0
  43. djinn_ui-0.1.0/djinn/registry/components/button/button.html +21 -0
  44. djinn_ui-0.1.0/djinn/registry/components/button/button.py +26 -0
  45. djinn_ui-0.1.0/djinn/registry/components/button/preview.md +36 -0
  46. djinn_ui-0.1.0/djinn/registry/components/button/registry.json +16 -0
  47. djinn_ui-0.1.0/djinn/registry/components/card/card.html +22 -0
  48. djinn_ui-0.1.0/djinn/registry/components/card/preview.md +14 -0
  49. djinn_ui-0.1.0/djinn/registry/components/card/registry.json +12 -0
  50. djinn_ui-0.1.0/djinn/registry/components/checkbox/checkbox.html +19 -0
  51. djinn_ui-0.1.0/djinn/registry/components/checkbox/preview.md +9 -0
  52. djinn_ui-0.1.0/djinn/registry/components/checkbox/registry.json +12 -0
  53. djinn_ui-0.1.0/djinn/registry/components/checkout-summary/checkout-summary.html +22 -0
  54. djinn_ui-0.1.0/djinn/registry/components/checkout-summary/preview.md +14 -0
  55. djinn_ui-0.1.0/djinn/registry/components/checkout-summary/registry.json +13 -0
  56. djinn_ui-0.1.0/djinn/registry/components/combobox/combobox.html +29 -0
  57. djinn_ui-0.1.0/djinn/registry/components/combobox/preview.md +22 -0
  58. djinn_ui-0.1.0/djinn/registry/components/combobox/registry.json +13 -0
  59. djinn_ui-0.1.0/djinn/registry/components/confirmation-dialog/confirmation-dialog.html +7 -0
  60. djinn_ui-0.1.0/djinn/registry/components/confirmation-dialog/preview.md +11 -0
  61. djinn_ui-0.1.0/djinn/registry/components/confirmation-dialog/registry.json +13 -0
  62. djinn_ui-0.1.0/djinn/registry/components/container/container.html +3 -0
  63. djinn_ui-0.1.0/djinn/registry/components/container/preview.md +11 -0
  64. djinn_ui-0.1.0/djinn/registry/components/container/registry.json +12 -0
  65. djinn_ui-0.1.0/djinn/registry/components/dashboard-sidebar/dashboard-sidebar.html +20 -0
  66. djinn_ui-0.1.0/djinn/registry/components/dashboard-sidebar/preview.md +16 -0
  67. djinn_ui-0.1.0/djinn/registry/components/dashboard-sidebar/registry.json +13 -0
  68. djinn_ui-0.1.0/djinn/registry/components/drawer/drawer.html +58 -0
  69. djinn_ui-0.1.0/djinn/registry/components/drawer/preview.md +11 -0
  70. djinn_ui-0.1.0/djinn/registry/components/drawer/registry.json +12 -0
  71. djinn_ui-0.1.0/djinn/registry/components/feature-card/feature-card.html +9 -0
  72. djinn_ui-0.1.0/djinn/registry/components/feature-card/preview.md +9 -0
  73. djinn_ui-0.1.0/djinn/registry/components/feature-card/registry.json +13 -0
  74. djinn_ui-0.1.0/djinn/registry/components/file-input/file-input.html +7 -0
  75. djinn_ui-0.1.0/djinn/registry/components/file-input/preview.md +9 -0
  76. djinn_ui-0.1.0/djinn/registry/components/file-input/registry.json +12 -0
  77. djinn_ui-0.1.0/djinn/registry/components/form-group/form-group.html +15 -0
  78. djinn_ui-0.1.0/djinn/registry/components/form-group/preview.md +11 -0
  79. djinn_ui-0.1.0/djinn/registry/components/form-group/registry.json +13 -0
  80. djinn_ui-0.1.0/djinn/registry/components/grid/grid.html +9 -0
  81. djinn_ui-0.1.0/djinn/registry/components/grid/preview.md +13 -0
  82. djinn_ui-0.1.0/djinn/registry/components/grid/registry.json +12 -0
  83. djinn_ui-0.1.0/djinn/registry/components/icon-button/icon-button.html +1 -0
  84. djinn_ui-0.1.0/djinn/registry/components/icon-button/preview.md +9 -0
  85. djinn_ui-0.1.0/djinn/registry/components/icon-button/registry.json +13 -0
  86. djinn_ui-0.1.0/djinn/registry/components/input/input.html +11 -0
  87. djinn_ui-0.1.0/djinn/registry/components/input/preview.md +9 -0
  88. djinn_ui-0.1.0/djinn/registry/components/input/registry.json +12 -0
  89. djinn_ui-0.1.0/djinn/registry/components/label/label.html +8 -0
  90. djinn_ui-0.1.0/djinn/registry/components/label/preview.md +9 -0
  91. djinn_ui-0.1.0/djinn/registry/components/label/registry.json +12 -0
  92. djinn_ui-0.1.0/djinn/registry/components/login-form/login-form.html +19 -0
  93. djinn_ui-0.1.0/djinn/registry/components/login-form/preview.md +9 -0
  94. djinn_ui-0.1.0/djinn/registry/components/login-form/registry.json +13 -0
  95. djinn_ui-0.1.0/djinn/registry/components/metric-grid/metric-grid.html +3 -0
  96. djinn_ui-0.1.0/djinn/registry/components/metric-grid/preview.md +14 -0
  97. djinn_ui-0.1.0/djinn/registry/components/metric-grid/registry.json +13 -0
  98. djinn_ui-0.1.0/djinn/registry/components/modal/modal.html +65 -0
  99. djinn_ui-0.1.0/djinn/registry/components/modal/preview.md +19 -0
  100. djinn_ui-0.1.0/djinn/registry/components/modal/registry.json +12 -0
  101. djinn_ui-0.1.0/djinn/registry/components/navbar/navbar.html +18 -0
  102. djinn_ui-0.1.0/djinn/registry/components/navbar/preview.md +15 -0
  103. djinn_ui-0.1.0/djinn/registry/components/navbar/registry.json +12 -0
  104. djinn_ui-0.1.0/djinn/registry/components/pagination/pagination.html +11 -0
  105. djinn_ui-0.1.0/djinn/registry/components/pagination/preview.md +12 -0
  106. djinn_ui-0.1.0/djinn/registry/components/pagination/registry.json +13 -0
  107. djinn_ui-0.1.0/djinn/registry/components/password-input/password-input.html +20 -0
  108. djinn_ui-0.1.0/djinn/registry/components/password-input/preview.md +9 -0
  109. djinn_ui-0.1.0/djinn/registry/components/password-input/registry.json +13 -0
  110. djinn_ui-0.1.0/djinn/registry/components/password-reset-form/password-reset-form.html +15 -0
  111. djinn_ui-0.1.0/djinn/registry/components/password-reset-form/preview.md +9 -0
  112. djinn_ui-0.1.0/djinn/registry/components/password-reset-form/registry.json +13 -0
  113. djinn_ui-0.1.0/djinn/registry/components/pricing-card/preview.md +14 -0
  114. djinn_ui-0.1.0/djinn/registry/components/pricing-card/pricing-card.html +14 -0
  115. djinn_ui-0.1.0/djinn/registry/components/pricing-card/registry.json +13 -0
  116. djinn_ui-0.1.0/djinn/registry/components/product-card/preview.md +9 -0
  117. djinn_ui-0.1.0/djinn/registry/components/product-card/product-card.html +17 -0
  118. djinn_ui-0.1.0/djinn/registry/components/product-card/registry.json +13 -0
  119. djinn_ui-0.1.0/djinn/registry/components/profile-card/preview.md +12 -0
  120. djinn_ui-0.1.0/djinn/registry/components/profile-card/profile-card.html +16 -0
  121. djinn_ui-0.1.0/djinn/registry/components/profile-card/registry.json +13 -0
  122. djinn_ui-0.1.0/djinn/registry/components/progress/preview.md +9 -0
  123. djinn_ui-0.1.0/djinn/registry/components/progress/progress.html +12 -0
  124. djinn_ui-0.1.0/djinn/registry/components/progress/registry.json +12 -0
  125. djinn_ui-0.1.0/djinn/registry/components/radio/preview.md +9 -0
  126. djinn_ui-0.1.0/djinn/registry/components/radio/radio.html +20 -0
  127. djinn_ui-0.1.0/djinn/registry/components/radio/registry.json +12 -0
  128. djinn_ui-0.1.0/djinn/registry/components/search-input/preview.md +9 -0
  129. djinn_ui-0.1.0/djinn/registry/components/search-input/registry.json +13 -0
  130. djinn_ui-0.1.0/djinn/registry/components/search-input/search-input.html +11 -0
  131. djinn_ui-0.1.0/djinn/registry/components/section/preview.md +12 -0
  132. djinn_ui-0.1.0/djinn/registry/components/section/registry.json +12 -0
  133. djinn_ui-0.1.0/djinn/registry/components/section/section.html +3 -0
  134. djinn_ui-0.1.0/djinn/registry/components/select/preview.md +12 -0
  135. djinn_ui-0.1.0/djinn/registry/components/select/registry.json +12 -0
  136. djinn_ui-0.1.0/djinn/registry/components/select/select.html +17 -0
  137. djinn_ui-0.1.0/djinn/registry/components/separator/preview.md +10 -0
  138. djinn_ui-0.1.0/djinn/registry/components/separator/registry.json +12 -0
  139. djinn_ui-0.1.0/djinn/registry/components/separator/separator.html +7 -0
  140. djinn_ui-0.1.0/djinn/registry/components/sheet/preview.md +11 -0
  141. djinn_ui-0.1.0/djinn/registry/components/sheet/registry.json +12 -0
  142. djinn_ui-0.1.0/djinn/registry/components/sheet/sheet.html +58 -0
  143. djinn_ui-0.1.0/djinn/registry/components/sidebar/preview.md +12 -0
  144. djinn_ui-0.1.0/djinn/registry/components/sidebar/registry.json +12 -0
  145. djinn_ui-0.1.0/djinn/registry/components/sidebar/sidebar.html +14 -0
  146. djinn_ui-0.1.0/djinn/registry/components/signup-form/preview.md +9 -0
  147. djinn_ui-0.1.0/djinn/registry/components/signup-form/registry.json +13 -0
  148. djinn_ui-0.1.0/djinn/registry/components/signup-form/signup-form.html +23 -0
  149. djinn_ui-0.1.0/djinn/registry/components/skeleton/preview.md +10 -0
  150. djinn_ui-0.1.0/djinn/registry/components/skeleton/registry.json +12 -0
  151. djinn_ui-0.1.0/djinn/registry/components/skeleton/skeleton.html +1 -0
  152. djinn_ui-0.1.0/djinn/registry/components/spinner/preview.md +9 -0
  153. djinn_ui-0.1.0/djinn/registry/components/spinner/registry.json +12 -0
  154. djinn_ui-0.1.0/djinn/registry/components/spinner/spinner.html +14 -0
  155. djinn_ui-0.1.0/djinn/registry/components/split-button/preview.md +9 -0
  156. djinn_ui-0.1.0/djinn/registry/components/split-button/registry.json +13 -0
  157. djinn_ui-0.1.0/djinn/registry/components/split-button/split-button.html +4 -0
  158. djinn_ui-0.1.0/djinn/registry/components/stack/preview.md +12 -0
  159. djinn_ui-0.1.0/djinn/registry/components/stack/registry.json +12 -0
  160. djinn_ui-0.1.0/djinn/registry/components/stack/stack.html +7 -0
  161. djinn_ui-0.1.0/djinn/registry/components/stats-card/preview.md +9 -0
  162. djinn_ui-0.1.0/djinn/registry/components/stats-card/registry.json +13 -0
  163. djinn_ui-0.1.0/djinn/registry/components/stats-card/stats-card.html +16 -0
  164. djinn_ui-0.1.0/djinn/registry/components/switch/preview.md +9 -0
  165. djinn_ui-0.1.0/djinn/registry/components/switch/registry.json +12 -0
  166. djinn_ui-0.1.0/djinn/registry/components/switch/switch.html +20 -0
  167. djinn_ui-0.1.0/djinn/registry/components/table/preview.md +24 -0
  168. djinn_ui-0.1.0/djinn/registry/components/table/registry.json +12 -0
  169. djinn_ui-0.1.0/djinn/registry/components/table/table.html +5 -0
  170. djinn_ui-0.1.0/djinn/registry/components/tabs/preview.md +29 -0
  171. djinn_ui-0.1.0/djinn/registry/components/tabs/registry.json +12 -0
  172. djinn_ui-0.1.0/djinn/registry/components/tabs/tabs.html +8 -0
  173. djinn_ui-0.1.0/djinn/registry/components/textarea/preview.md +9 -0
  174. djinn_ui-0.1.0/djinn/registry/components/textarea/registry.json +12 -0
  175. djinn_ui-0.1.0/djinn/registry/components/textarea/textarea.html +10 -0
  176. djinn_ui-0.1.0/djinn/registry/components/timeline/preview.md +19 -0
  177. djinn_ui-0.1.0/djinn/registry/components/timeline/registry.json +12 -0
  178. djinn_ui-0.1.0/djinn/registry/components/timeline/timeline.html +3 -0
  179. djinn_ui-0.1.0/djinn/registry/components/toast/preview.md +12 -0
  180. djinn_ui-0.1.0/djinn/registry/components/toast/registry.json +12 -0
  181. djinn_ui-0.1.0/djinn/registry/components/toast/toast.html +26 -0
  182. djinn_ui-0.1.0/djinn/registry/components/tooltip/preview.md +11 -0
  183. djinn_ui-0.1.0/djinn/registry/components/tooltip/registry.json +12 -0
  184. djinn_ui-0.1.0/djinn/registry/components/tooltip/tooltip.html +24 -0
  185. djinn_ui-0.1.0/djinn/registry/index.json +59 -0
  186. djinn_ui-0.1.0/djinn/tests/__init__.py +0 -0
  187. djinn_ui-0.1.0/djinn/tests/test_cli.py +53 -0
  188. djinn_ui-0.1.0/djinn/tests/test_core.py +62 -0
  189. djinn_ui-0.1.0/djinn_ui.egg-info/PKG-INFO +151 -0
  190. djinn_ui-0.1.0/djinn_ui.egg-info/SOURCES.txt +194 -0
  191. djinn_ui-0.1.0/djinn_ui.egg-info/dependency_links.txt +1 -0
  192. djinn_ui-0.1.0/djinn_ui.egg-info/entry_points.txt +2 -0
  193. djinn_ui-0.1.0/djinn_ui.egg-info/requires.txt +5 -0
  194. djinn_ui-0.1.0/djinn_ui.egg-info/top_level.txt +1 -0
  195. djinn_ui-0.1.0/pyproject.toml +49 -0
  196. 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.
@@ -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
@@ -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
@@ -0,0 +1,4 @@
1
+ from djinn.cli.main import cli
2
+
3
+ if __name__ == "__main__":
4
+ cli()
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