miolo 3.0.0-beta.21 → 3.0.0-beta.210

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 (246) hide show
  1. package/bin/build/build.mjs +53 -0
  2. package/bin/build/build_bin.mjs +17 -0
  3. package/bin/build/cli/client.mjs +52 -0
  4. package/bin/build/cli/css.mjs +22 -0
  5. package/bin/build/cli/index.mjs +40 -0
  6. package/bin/build/cli/ssr.mjs +21 -0
  7. package/bin/build/server/aliases.mjs +112 -0
  8. package/bin/build/server/babel.config.js +24 -0
  9. package/bin/build/server/banner.mjs +20 -0
  10. package/bin/build/server/bundle.mjs +111 -0
  11. package/bin/build/server/fix.mjs +15 -0
  12. package/bin/build/server/index.mjs +69 -0
  13. package/bin/build/server/options.mjs +83 -0
  14. package/bin/create/auth.mjs +23 -0
  15. package/bin/create/copy.mjs +175 -0
  16. package/bin/create/docker.mjs +25 -0
  17. package/bin/create/index.mjs +137 -0
  18. package/bin/create/pkgjson.mjs +72 -0
  19. package/bin/create/prepare-template.mjs +158 -0
  20. package/bin/create/validation.mjs +27 -0
  21. package/bin/dev/dev.mjs +32 -23
  22. package/bin/dev/dev_start.mjs +6 -5
  23. package/bin/index.mjs +94 -52
  24. package/bin/prod-bin/create-bin.mjs +42 -27
  25. package/bin/prod-bin/run.mjs +13 -9
  26. package/bin/{prod-run → run}/pid.mjs +4 -4
  27. package/bin/run/restart.mjs +13 -0
  28. package/bin/run/start.mjs +18 -0
  29. package/bin/run/stop.mjs +20 -0
  30. package/bin/util.mjs +35 -11
  31. package/package.json +59 -39
  32. package/src/config/.env +34 -12
  33. package/src/config/defaults.mjs +253 -185
  34. package/src/config/env.mjs +40 -22
  35. package/src/config/index.mjs +19 -24
  36. package/src/config/util.mjs +25 -10
  37. package/src/db-conn.mjs +34 -0
  38. package/src/engines/cron/emails.mjs +10 -5
  39. package/src/engines/cron/index.mjs +45 -51
  40. package/src/engines/cron/init.mjs +16 -17
  41. package/src/engines/cron/ipsum.mjs +65 -60
  42. package/src/engines/cron/syscheck.mjs +30 -30
  43. package/src/engines/emailer/index.mjs +1 -2
  44. package/src/engines/emailer/queue.mjs +14 -20
  45. package/src/engines/emailer/transporter.mjs +86 -74
  46. package/src/engines/geoip/index.mjs +23 -28
  47. package/src/engines/http/index.mjs +26 -15
  48. package/src/engines/logger/buildErrorEmailBody.mjs +72 -0
  49. package/src/engines/logger/index.mjs +114 -122
  50. package/src/engines/logger/injectStackTrace.mjs +59 -0
  51. package/src/engines/logger/logger_mail.mjs +47 -61
  52. package/src/engines/logger/reopenTransportOnHupSignal.mjs +12 -13
  53. package/src/engines/parser/Parser.mjs +77 -60
  54. package/src/engines/parser/index.mjs +1 -1
  55. package/src/engines/schema/diffObjs.mjs +41 -0
  56. package/src/engines/schema/index.mjs +4 -0
  57. package/src/engines/schema/input.mjs +54 -0
  58. package/src/engines/schema/output.mjs +66 -0
  59. package/src/engines/socket/index.mjs +44 -46
  60. package/src/index.mjs +15 -10
  61. package/src/middleware/auth/basic.mjs +41 -40
  62. package/src/middleware/auth/custom.mjs +10 -13
  63. package/src/middleware/auth/guest.mjs +27 -27
  64. package/src/middleware/auth/passport/index.mjs +374 -0
  65. package/src/middleware/auth/passport/session/index.mjs +43 -0
  66. package/src/middleware/auth/{credentials → passport}/session/store.mjs +35 -15
  67. package/src/middleware/auth/passport/session/store_koa_redis.mjs +3 -0
  68. package/src/middleware/context/cache/index.mjs +78 -33
  69. package/src/middleware/context/cache/options.mjs +19 -21
  70. package/src/middleware/context/db.mjs +45 -20
  71. package/src/middleware/context/index.mjs +12 -12
  72. package/src/middleware/extra.mjs +4 -5
  73. package/src/middleware/http/body.mjs +25 -25
  74. package/src/middleware/http/catcher.mjs +81 -8
  75. package/src/middleware/http/custom_blacklist.mjs +19 -16
  76. package/src/middleware/http/headers.mjs +37 -34
  77. package/src/middleware/http/ratelimit.mjs +16 -23
  78. package/src/middleware/http/request.mjs +60 -65
  79. package/src/middleware/routes/catch_js_error.mjs +30 -23
  80. package/src/middleware/routes/robots.mjs +4 -7
  81. package/src/middleware/routes/router/crud/attachCrudRoutes.mjs +108 -90
  82. package/src/middleware/routes/router/crud/getCrudConfig.mjs +31 -55
  83. package/src/middleware/routes/router/defaults.mjs +6 -19
  84. package/src/middleware/routes/router/index.mjs +17 -21
  85. package/src/middleware/routes/router/queries/attachQueriesRoutes.mjs +227 -50
  86. package/src/middleware/routes/router/queries/getQueriesConfig.mjs +45 -55
  87. package/src/middleware/routes/router/utils.mjs +41 -26
  88. package/src/middleware/ssr/context.mjs +5 -7
  89. package/src/middleware/ssr/html.mjs +66 -43
  90. package/src/middleware/ssr/loader.mjs +11 -14
  91. package/src/middleware/ssr/ssr_render.mjs +39 -22
  92. package/src/middleware/static/index.mjs +33 -14
  93. package/src/middleware/vite/devserver.mjs +38 -22
  94. package/src/middleware/vite/watcher.mjs +12 -14
  95. package/src/server-cron.mjs +13 -8
  96. package/src/server-dev.mjs +13 -16
  97. package/src/server.mjs +49 -51
  98. package/template/.agent/skills/miolo-app-arch/SKILL.md +218 -0
  99. package/template/.agent/skills/miolo-auth/SKILL.md +450 -0
  100. package/template/.agent/skills/miolo-cli-router/SKILL.md +394 -0
  101. package/template/.agent/skills/miolo-database/SKILL.md +358 -0
  102. package/template/.agent/skills/miolo-react-patterns/SKILL.md +426 -0
  103. package/template/.agent/skills/miolo-routing/SKILL.md +326 -0
  104. package/template/.agent/skills/miolo-schemas/SKILL.md +329 -0
  105. package/template/.agent/skills/miolo-session-context/SKILL.md +397 -0
  106. package/template/.agent/skills/miolo-ssr/SKILL.md +433 -0
  107. package/template/.editorconfig +18 -0
  108. package/template/.env +120 -0
  109. package/template/biome.json +63 -0
  110. package/template/components.json +21 -0
  111. package/template/db/init.sh +89 -0
  112. package/template/db/sql/00_drop.sql +2 -0
  113. package/template/db/sql/01_users.sql +31 -0
  114. package/template/db/sql/02_todos.sql +20 -0
  115. package/template/docker/Dockerfile +13 -0
  116. package/template/docker/docker-compose.yaml +79 -0
  117. package/template/gitignore +42 -0
  118. package/template/jsconfig.json +18 -0
  119. package/template/package.json +88 -0
  120. package/template/postcss.config.js +9 -0
  121. package/template/src/cli/App.jsx +25 -0
  122. package/template/src/cli/components/JsonTreeViewer.jsx +128 -0
  123. package/template/src/cli/components/shadcn-io/spinner/index.jsx +232 -0
  124. package/template/src/cli/components/stepper.jsx +408 -0
  125. package/template/src/cli/components/ui/avatar.jsx +36 -0
  126. package/template/src/cli/components/ui/badge.jsx +31 -0
  127. package/template/src/cli/components/ui/breadcrumb.jsx +97 -0
  128. package/template/src/cli/components/ui/card.jsx +73 -0
  129. package/template/src/cli/components/ui/collapsible.jsx +16 -0
  130. package/template/src/cli/components/ui/dropdown-menu.jsx +179 -0
  131. package/template/src/cli/components/ui/field.jsx +217 -0
  132. package/template/src/cli/components/ui/input.jsx +19 -0
  133. package/template/src/cli/components/ui/label.jsx +17 -0
  134. package/template/src/cli/components/ui/pagination.jsx +99 -0
  135. package/template/src/cli/components/ui/patched/alert.jsx +56 -0
  136. package/template/src/cli/components/ui/patched/button.jsx +45 -0
  137. package/template/src/cli/components/ui/patched/dialog.jsx +114 -0
  138. package/template/src/cli/components/ui/patched/sidebar.jsx +660 -0
  139. package/template/src/cli/components/ui/select.jsx +141 -0
  140. package/template/src/cli/components/ui/separator.jsx +21 -0
  141. package/template/src/cli/components/ui/sheet.jsx +115 -0
  142. package/template/src/cli/components/ui/skeleton.jsx +13 -0
  143. package/template/src/cli/components/ui/sonner.jsx +22 -0
  144. package/template/src/cli/components/ui/switch.jsx +25 -0
  145. package/template/src/cli/components/ui/table.jsx +88 -0
  146. package/template/src/cli/components/ui/textarea.jsx +16 -0
  147. package/template/src/cli/components/ui/tooltip.jsx +45 -0
  148. package/template/src/cli/config/store_keys.mjs +2 -0
  149. package/template/src/cli/context/data/DataContext.jsx +5 -0
  150. package/template/src/cli/context/data/DataProvider.jsx +44 -0
  151. package/template/src/cli/context/data/useBreads.mjs +15 -0
  152. package/template/src/cli/context/data/useDataContext.mjs +4 -0
  153. package/template/src/cli/context/session/SessionContext.mjs +4 -0
  154. package/template/src/cli/context/session/SessionProvider.jsx +31 -0
  155. package/template/src/cli/context/session/makePermissioner.mjs +34 -0
  156. package/template/src/cli/context/session/useSessionContext.mjs +6 -0
  157. package/template/src/cli/context/theme/ThemeContext.mjs +4 -0
  158. package/template/src/cli/context/theme/ThemeProvider.jsx +49 -0
  159. package/template/src/cli/context/theme/useThemeContext.mjs +6 -0
  160. package/template/src/cli/context/ui/UIContext.jsx +5 -0
  161. package/template/src/cli/context/ui/UIProvider.jsx +16 -0
  162. package/template/src/cli/context/ui/useUIContext.mjs +4 -0
  163. package/template/src/cli/context/util.mjs +17 -0
  164. package/template/src/cli/entry-cli.jsx +33 -0
  165. package/template/src/cli/hooks/useIsMobile.mjs +19 -0
  166. package/template/src/cli/hooks/useStoragedState.mjs +63 -0
  167. package/template/src/cli/index.html +29 -0
  168. package/template/src/cli/layout/app-sidebar.jsx +25 -0
  169. package/template/src/cli/layout/main-layout.jsx +63 -0
  170. package/template/src/cli/layout/nav-last-todos.jsx +72 -0
  171. package/template/src/cli/layout/nav-main.jsx +39 -0
  172. package/template/src/cli/layout/nav-user.jsx +105 -0
  173. package/template/src/cli/layout/prop-switcher.jsx +93 -0
  174. package/template/src/cli/lib/utils.mjs +10 -0
  175. package/template/src/cli/pages/Index.jsx +13 -0
  176. package/template/src/cli/pages/IndexOffline.jsx +13 -0
  177. package/template/src/cli/pages/IndexOnline.jsx +18 -0
  178. package/template/src/cli/pages/dash/Dashboard.jsx +29 -0
  179. package/template/src/cli/pages/offline/Login.jsx +43 -0
  180. package/template/src/cli/pages/offline/LoginForm.jsx +115 -0
  181. package/template/src/cli/pages/security/Security.jsx +39 -0
  182. package/template/src/cli/pages/security/SecurityForm.jsx +106 -0
  183. package/template/src/cli/pages/todos/TodoActions.jsx +99 -0
  184. package/template/src/cli/pages/todos/TodoAdd.jsx +43 -0
  185. package/template/src/cli/pages/todos/TodoList.jsx +60 -0
  186. package/template/src/cli/pages/todos/Todos.jsx +23 -0
  187. package/template/src/cli/pages/todos/context/TodosContext.jsx +5 -0
  188. package/template/src/cli/pages/todos/context/TodosProvider.jsx +191 -0
  189. package/template/src/cli/pages/todos/context/useTodosContext.mjs +4 -0
  190. package/template/src/ns/models/Todo.mjs +29 -0
  191. package/template/src/ns/models/TodoList.mjs +8 -0
  192. package/template/src/ns/models/User.mjs +40 -0
  193. package/template/src/server/bot/check_today.mjs +10 -0
  194. package/template/src/server/io/cache/base.mjs +21 -0
  195. package/template/src/server/io/db/filter.mjs +92 -0
  196. package/template/src/server/io/db/todos/delete.mjs +29 -0
  197. package/template/src/server/io/db/todos/find.mjs +13 -0
  198. package/template/src/server/io/db/todos/read.mjs +83 -0
  199. package/template/src/server/io/db/todos/toggle.mjs +37 -0
  200. package/template/src/server/io/db/todos/upsave.mjs +32 -0
  201. package/template/src/server/io/db/triggers/user.mjs +13 -0
  202. package/template/src/server/io/db/users/auth.mjs +132 -0
  203. package/template/src/server/io/db/users/pwd.mjs +38 -0
  204. package/template/src/server/io/db/users/save.mjs +17 -0
  205. package/template/src/server/miolo/auth/basic.mjs +15 -0
  206. package/template/src/server/miolo/auth/guest.mjs +3 -0
  207. package/template/src/server/miolo/auth/passport.mjs +73 -0
  208. package/template/src/server/miolo/cache.mjs +11 -0
  209. package/template/src/server/miolo/cron/foo.mjs +7 -0
  210. package/template/src/server/miolo/cron/index.mjs +28 -0
  211. package/template/src/server/miolo/cron/invalidate.mjs +21 -0
  212. package/template/src/server/miolo/db.mjs +36 -0
  213. package/template/src/server/miolo/http.mjs +14 -0
  214. package/template/src/server/miolo/index.mjs +43 -0
  215. package/template/src/server/miolo/routes/crud.mjs +16 -0
  216. package/template/src/server/miolo/routes/index.mjs +8 -0
  217. package/template/src/server/miolo/ssr/entry-server.jsx +13 -0
  218. package/template/src/server/miolo/ssr/loader.mjs +18 -0
  219. package/template/src/server/routes/index.mjs +66 -0
  220. package/template/src/server/routes/todos/mod.mjs +52 -0
  221. package/template/src/server/routes/todos/read.mjs +45 -0
  222. package/template/src/server/routes/todos/special.mjs +47 -0
  223. package/template/src/server/routes/users/user.mjs +54 -0
  224. package/template/src/server/server.mjs +10 -0
  225. package/template/src/server/utils/crypt.mjs +38 -0
  226. package/template/src/server/utils/io.mjs +15 -0
  227. package/template/src/server/utils/pwdfor.mjs +25 -0
  228. package/template/src/server/utils/schema.mjs +22 -0
  229. package/template/src/static/img/default/profile.png +0 -0
  230. package/template/src/static/img/favicon.ico +0 -0
  231. package/template/src/static/img/miolo_logo.png +0 -0
  232. package/template/src/static/img/miolo_name.png +0 -0
  233. package/template/src/static/public/manifest.json +21 -0
  234. package/template/src/static/public/sw.js +79 -0
  235. package/template/src/static/style/globals.css +156 -0
  236. package/template/src/static/style/json-tree.css +54 -0
  237. package/template/src/static/style/skeleton.css +49 -0
  238. package/bin/prod-build/build-client.mjs +0 -67
  239. package/bin/prod-build/build-server.mjs +0 -58
  240. package/bin/prod-run/restart.mjs +0 -9
  241. package/bin/prod-run/start.mjs +0 -15
  242. package/bin/prod-run/stop.mjs +0 -20
  243. package/src/engines/logger/verify.mjs +0 -22
  244. package/src/middleware/auth/credentials/index.mjs +0 -151
  245. package/src/middleware/auth/credentials/session/index.mjs +0 -24
  246. package/src/middleware/auth/credentials/session/store_koa_redis.mjs +0 -3
@@ -0,0 +1,232 @@
1
+ import { LoaderCircleIcon, LoaderIcon, LoaderPinwheelIcon } from "lucide-react"
2
+ import { cn } from "#cli/lib/utils.mjs"
3
+
4
+ const Default = ({ className, ...props }) => (
5
+ <LoaderIcon className={cn("animate-spin", className)} {...props} />
6
+ )
7
+
8
+ const Circle = ({ className, ...props }) => (
9
+ <LoaderCircleIcon className={cn("animate-spin", className)} {...props} />
10
+ )
11
+
12
+ const Pinwheel = ({ className, ...props }) => (
13
+ <LoaderPinwheelIcon className={cn("animate-spin", className)} {...props} />
14
+ )
15
+
16
+ const CircleFilled = ({ className, size = 24, ...props }) => (
17
+ <div className="relative" style={{ width: size, height: size }}>
18
+ <div className="absolute inset-0 rotate-180">
19
+ <LoaderCircleIcon
20
+ className={cn("animate-spin", className, "text-foreground opacity-20")}
21
+ size={size}
22
+ {...props}
23
+ />
24
+ </div>
25
+ <LoaderCircleIcon className={cn("relative animate-spin", className)} size={size} {...props} />
26
+ </div>
27
+ )
28
+
29
+ const Ellipsis = ({ size = 24, ...props }) => {
30
+ return (
31
+ <svg
32
+ height={size}
33
+ viewBox="0 0 24 24"
34
+ width={size}
35
+ xmlns="http://www.w3.org/2000/svg"
36
+ {...props}
37
+ >
38
+ <title>Loading...</title>
39
+ <circle cx="4" cy="12" fill="currentColor" r="2">
40
+ <animate
41
+ attributeName="cy"
42
+ begin="0;ellipsis3.end+0.25s"
43
+ calcMode="spline"
44
+ dur="0.6s"
45
+ id="ellipsis1"
46
+ keySplines=".33,.66,.66,1;.33,0,.66,.33"
47
+ values="12;6;12"
48
+ />
49
+ </circle>
50
+ <circle cx="12" cy="12" fill="currentColor" r="2">
51
+ <animate
52
+ attributeName="cy"
53
+ begin="ellipsis1.begin+0.1s"
54
+ calcMode="spline"
55
+ dur="0.6s"
56
+ keySplines=".33,.66,.66,1;.33,0,.66,.33"
57
+ values="12;6;12"
58
+ />
59
+ </circle>
60
+ <circle cx="20" cy="12" fill="currentColor" r="2">
61
+ <animate
62
+ attributeName="cy"
63
+ begin="ellipsis1.begin+0.2s"
64
+ calcMode="spline"
65
+ dur="0.6s"
66
+ id="ellipsis3"
67
+ keySplines=".33,.66,.66,1;.33,0,.66,.33"
68
+ values="12;6;12"
69
+ />
70
+ </circle>
71
+ </svg>
72
+ )
73
+ }
74
+
75
+ const Ring = ({ size = 24, ...props }) => (
76
+ <svg
77
+ height={size}
78
+ stroke="currentColor"
79
+ viewBox="0 0 44 44"
80
+ width={size}
81
+ xmlns="http://www.w3.org/2000/svg"
82
+ {...props}
83
+ >
84
+ <title>Loading...</title>
85
+ <g fill="none" fillRule="evenodd" strokeWidth="2">
86
+ <circle cx="22" cy="22" r="1">
87
+ <animate
88
+ attributeName="r"
89
+ begin="0s"
90
+ calcMode="spline"
91
+ dur="1.8s"
92
+ keySplines="0.165, 0.84, 0.44, 1"
93
+ keyTimes="0; 1"
94
+ repeatCount="indefinite"
95
+ values="1; 20"
96
+ />
97
+ <animate
98
+ attributeName="stroke-opacity"
99
+ begin="0s"
100
+ calcMode="spline"
101
+ dur="1.8s"
102
+ keySplines="0.3, 0.61, 0.355, 1"
103
+ keyTimes="0; 1"
104
+ repeatCount="indefinite"
105
+ values="1; 0"
106
+ />
107
+ </circle>
108
+ <circle cx="22" cy="22" r="1">
109
+ <animate
110
+ attributeName="r"
111
+ begin="-0.9s"
112
+ calcMode="spline"
113
+ dur="1.8s"
114
+ keySplines="0.165, 0.84, 0.44, 1"
115
+ keyTimes="0; 1"
116
+ repeatCount="indefinite"
117
+ values="1; 20"
118
+ />
119
+ <animate
120
+ attributeName="stroke-opacity"
121
+ begin="-0.9s"
122
+ calcMode="spline"
123
+ dur="1.8s"
124
+ keySplines="0.3, 0.61, 0.355, 1"
125
+ keyTimes="0; 1"
126
+ repeatCount="indefinite"
127
+ values="1; 0"
128
+ />
129
+ </circle>
130
+ </g>
131
+ </svg>
132
+ )
133
+
134
+ const Bars = ({ size = 24, ...props }) => (
135
+ <svg height={size} viewBox="0 0 24 24" width={size} xmlns="http://www.w3.org/2000/svg" {...props}>
136
+ <title>Loading...</title>
137
+ <style>{`
138
+ .spinner-bar {
139
+ animation: spinner-bars-animation .8s linear infinite;
140
+ animation-delay: -.8s;
141
+ }
142
+ .spinner-bars-2 {
143
+ animation-delay: -.65s;
144
+ }
145
+ .spinner-bars-3 {
146
+ animation-delay: -0.5s;
147
+ }
148
+ @keyframes spinner-bars-animation {
149
+ 0% {
150
+ y: 1px;
151
+ height: 22px;
152
+ }
153
+ 93.75% {
154
+ y: 5px;
155
+ height: 14px;
156
+ opacity: 0.2;
157
+ }
158
+ }
159
+ `}</style>
160
+ <rect className="spinner-bar" fill="currentColor" height="22" width="6" x="1" y="1" />
161
+ <rect
162
+ className="spinner-bar spinner-bars-2"
163
+ fill="currentColor"
164
+ height="22"
165
+ width="6"
166
+ x="9"
167
+ y="1"
168
+ />
169
+ <rect
170
+ className="spinner-bar spinner-bars-3"
171
+ fill="currentColor"
172
+ height="22"
173
+ width="6"
174
+ x="17"
175
+ y="1"
176
+ />
177
+ </svg>
178
+ )
179
+
180
+ const Infinite = ({ size = 24, ...props }) => (
181
+ <svg
182
+ height={size}
183
+ preserveAspectRatio="xMidYMid"
184
+ viewBox="0 0 100 100"
185
+ width={size}
186
+ xmlns="http://www.w3.org/2000/svg"
187
+ {...props}
188
+ >
189
+ <title>Loading...</title>
190
+ <path
191
+ d="M24.3 30C11.4 30 5 43.3 5 50s6.4 20 19.3 20c19.3 0 32.1-40 51.4-40 C88.6 30 95 43.3 95 50s-6.4 20-19.3 20C56.4 70 43.6 30 24.3 30z"
192
+ fill="none"
193
+ stroke="currentColor"
194
+ strokeDasharray="205.271142578125 51.317785644531256"
195
+ strokeLinecap="round"
196
+ strokeWidth="10"
197
+ style={{
198
+ transform: "scale(0.8)",
199
+ transformOrigin: "50px 50px"
200
+ }}
201
+ >
202
+ <animate
203
+ attributeName="stroke-dashoffset"
204
+ dur="2s"
205
+ keyTimes="0;1"
206
+ repeatCount="indefinite"
207
+ values="0;256.58892822265625"
208
+ />
209
+ </path>
210
+ </svg>
211
+ )
212
+
213
+ export const Spinner = ({ variant, ...props }) => {
214
+ switch (variant) {
215
+ case "circle":
216
+ return <Circle {...props} />
217
+ case "pinwheel":
218
+ return <Pinwheel {...props} />
219
+ case "circle-filled":
220
+ return <CircleFilled {...props} />
221
+ case "ellipsis":
222
+ return <Ellipsis {...props} />
223
+ case "ring":
224
+ return <Ring {...props} />
225
+ case "bars":
226
+ return <Bars {...props} />
227
+ case "infinite":
228
+ return <Infinite {...props} />
229
+ default:
230
+ return <Default {...props} />
231
+ }
232
+ }
@@ -0,0 +1,408 @@
1
+ import { Slot } from "@radix-ui/react-slot"
2
+ import * as Stepperize from "@stepperize/react"
3
+ import { cva } from "class-variance-authority"
4
+ import { Children, createContext, isValidElement, useContext, useMemo } from "react"
5
+ import { Button } from "#cli/components/ui/patched/button.jsx"
6
+ import { cn } from "#cli/lib/utils.mjs"
7
+
8
+ const StepperContext = createContext(null)
9
+
10
+ const useStepperProvider = () => {
11
+ const context = useContext(StepperContext)
12
+ if (!context) {
13
+ throw new Error("useStepper must be used within a StepperProvider.")
14
+ }
15
+ return context
16
+ }
17
+
18
+ const defineStepper = (...steps) => {
19
+ const { Scoped, useStepper, ...rest } = Stepperize.defineStepper(...steps)
20
+
21
+ const StepperContainer = ({ children, className, ...props }) => {
22
+ const methods = useStepper()
23
+
24
+ return (
25
+ <div date-component="stepper" className={cn("w-full", className)} {...props}>
26
+ {typeof children === "function" ? children({ methods }) : children}
27
+ </div>
28
+ )
29
+ }
30
+
31
+ return {
32
+ ...rest,
33
+ useStepper,
34
+ Stepper: {
35
+ Provider: ({
36
+ variant = "horizontal",
37
+ labelOrientation = "horizontal",
38
+ tracking = false,
39
+ children,
40
+ className,
41
+ ...props
42
+ }) => {
43
+ return (
44
+ <StepperContext.Provider value={{ variant, labelOrientation, tracking }}>
45
+ <Scoped initialStep={props.initialStep} initialMetadata={props.initialMetadata}>
46
+ <StepperContainer className={className} {...props}>
47
+ {children}
48
+ </StepperContainer>
49
+ </Scoped>
50
+ </StepperContext.Provider>
51
+ )
52
+ },
53
+ Navigation: ({ children, "aria-label": ariaLabel = "Stepper Navigation", ...props }) => {
54
+ const { variant } = useStepperProvider()
55
+ return (
56
+ /* biome-ignore lint/a11y/noNoninteractiveElementToInteractiveRole: biome-ignore */
57
+ <nav date-component="stepper-navigation" aria-label={ariaLabel} role="tablist" {...props}>
58
+ <ol
59
+ date-component="stepper-navigation-list"
60
+ className={classForNavigationList({ variant: variant })}
61
+ >
62
+ {children}
63
+ </ol>
64
+ </nav>
65
+ )
66
+ },
67
+ Step: ({ children, className, icon, ...props }) => {
68
+ const { variant, labelOrientation } = useStepperProvider()
69
+ const { current } = useStepper()
70
+
71
+ const utils = rest.utils
72
+ const steps = rest.steps
73
+
74
+ const stepIndex = utils.getIndex(props.of)
75
+ const step = steps[stepIndex]
76
+ const currentIndex = utils.getIndex(current.id)
77
+
78
+ const isLast = utils.getLast().id === props.of
79
+ const isActive = current.id === props.of
80
+
81
+ const dataState = getStepState(currentIndex, stepIndex)
82
+ const childMap = useStepChildren(children)
83
+
84
+ const title = childMap.get("title")
85
+ const description = childMap.get("description")
86
+ const panel = childMap.get("panel")
87
+
88
+ if (variant === "circle") {
89
+ return (
90
+ <li
91
+ date-component="stepper-step"
92
+ className={cn(
93
+ "flex shrink-0 items-center gap-4 rounded-md transition-colors",
94
+ className
95
+ )}
96
+ >
97
+ <CircleStepIndicator currentStep={stepIndex + 1} totalSteps={steps.length} />
98
+ <div
99
+ date-component="stepper-step-content"
100
+ className="flex flex-col items-start gap-1"
101
+ >
102
+ {title}
103
+ {description}
104
+ </div>
105
+ </li>
106
+ )
107
+ }
108
+
109
+ return (
110
+ <>
111
+ <li
112
+ date-component="stepper-step"
113
+ className={cn([
114
+ "group peer relative flex items-center gap-2",
115
+ "data-[variant=vertical]:flex-row",
116
+ "data-[label-orientation=vertical]:w-full",
117
+ "data-[label-orientation=vertical]:flex-col",
118
+ "data-[label-orientation=vertical]:justify-center"
119
+ ])}
120
+ data-variant={variant}
121
+ data-label-orientation={labelOrientation}
122
+ data-state={dataState}
123
+ data-disabled={props.disabled}
124
+ >
125
+ <Button
126
+ id={`step-${step.id}`}
127
+ date-component="stepper-step-indicator"
128
+ type="button"
129
+ role="tab"
130
+ tabIndex={dataState !== "inactive" ? 0 : -1}
131
+ className="rounded-full"
132
+ variant={dataState !== "inactive" ? "default" : "secondary"}
133
+ size="icon"
134
+ aria-controls={`step-panel-${props.of}`}
135
+ aria-current={isActive ? "step" : undefined}
136
+ aria-posinset={stepIndex + 1}
137
+ aria-setsize={steps.length}
138
+ aria-selected={isActive}
139
+ onKeyDown={(e) =>
140
+ onStepKeyDown(e, utils.getNext(props.of), utils.getPrev(props.of))
141
+ }
142
+ {...props}
143
+ >
144
+ {icon ?? stepIndex + 1}
145
+ </Button>
146
+ {variant === "horizontal" && labelOrientation === "vertical" && (
147
+ <StepperSeparator
148
+ orientation="horizontal"
149
+ labelOrientation={labelOrientation}
150
+ isLast={isLast}
151
+ state={dataState}
152
+ disabled={props.disabled}
153
+ />
154
+ )}
155
+ <div date-component="stepper-step-content" className="flex flex-col items-start">
156
+ {title}
157
+ {description}
158
+ </div>
159
+ </li>
160
+ {variant === "horizontal" && labelOrientation === "horizontal" && (
161
+ <StepperSeparator
162
+ orientation="horizontal"
163
+ isLast={isLast}
164
+ state={dataState}
165
+ disabled={props.disabled}
166
+ />
167
+ )}
168
+ {variant === "vertical" && (
169
+ <div className="flex gap-4">
170
+ {!isLast && (
171
+ <div className="flex justify-center ps-[calc(var(--spacing)_*_4.5_-_1px)]">
172
+ <StepperSeparator
173
+ orientation="vertical"
174
+ isLast={isLast}
175
+ state={dataState}
176
+ disabled={props.disabled}
177
+ />
178
+ </div>
179
+ )}
180
+ <div className="my-3 flex-1 ps-4">{panel}</div>
181
+ </div>
182
+ )}
183
+ </>
184
+ )
185
+ },
186
+ Title,
187
+ Description,
188
+ Panel: ({ children, asChild, ...props }) => {
189
+ const Comp = asChild ? Slot : "div"
190
+ const { tracking } = useStepperProvider()
191
+
192
+ return (
193
+ <Comp
194
+ date-component="stepper-step-panel"
195
+ ref={(node) => scrollIntoStepperPanel(node, tracking)}
196
+ {...props}
197
+ >
198
+ {children}
199
+ </Comp>
200
+ )
201
+ },
202
+ Controls: ({ children, className, asChild, ...props }) => {
203
+ const Comp = asChild ? Slot : "div"
204
+ return (
205
+ <Comp
206
+ date-component="stepper-controls"
207
+ className={cn("flex justify-end gap-4", className)}
208
+ {...props}
209
+ >
210
+ {children}
211
+ </Comp>
212
+ )
213
+ }
214
+ }
215
+ }
216
+ }
217
+
218
+ const Title = ({ children, className, asChild, ...props }) => {
219
+ const Comp = asChild ? Slot : "h4"
220
+
221
+ return (
222
+ <Comp
223
+ date-component="stepper-step-title"
224
+ className={cn("text-base font-medium", className)}
225
+ {...props}
226
+ >
227
+ {children}
228
+ </Comp>
229
+ )
230
+ }
231
+
232
+ const Description = ({ children, className, asChild, ...props }) => {
233
+ const Comp = asChild ? Slot : "p"
234
+
235
+ return (
236
+ <Comp
237
+ date-component="stepper-step-description"
238
+ className={cn("text-sm text-muted-foreground", className)}
239
+ {...props}
240
+ >
241
+ {children}
242
+ </Comp>
243
+ )
244
+ }
245
+
246
+ const StepperSeparator = ({ orientation, isLast, labelOrientation, state, disabled }) => {
247
+ if (isLast) {
248
+ return null
249
+ }
250
+ return (
251
+ /* biome-ignore lint/a11y/useSemanticElements: biome-ignore */
252
+ <div
253
+ date-component="stepper-separator"
254
+ data-orientation={orientation}
255
+ data-state={state}
256
+ data-disabled={disabled}
257
+ /* biome-ignore lint/a11y/useAriaPropsForRole: biome-ignore */
258
+ role="separator"
259
+ tabIndex={-1}
260
+ className={classForSeparator({ orientation, labelOrientation })}
261
+ />
262
+ )
263
+ }
264
+
265
+ const CircleStepIndicator = ({ currentStep, totalSteps, size = 80, strokeWidth = 6 }) => {
266
+ const radius = (size - strokeWidth) / 2
267
+ const circumference = radius * 2 * Math.PI
268
+ const fillPercentage = (currentStep / totalSteps) * 100
269
+ const dashOffset = circumference - (circumference * fillPercentage) / 100
270
+ return (
271
+ <div
272
+ date-component="stepper-step-indicator"
273
+ role="progressbar"
274
+ aria-valuenow={currentStep}
275
+ aria-valuemin={1}
276
+ aria-valuemax={totalSteps}
277
+ tabIndex={-1}
278
+ className="relative inline-flex items-center justify-center"
279
+ >
280
+ <svg width={size} height={size}>
281
+ <title>Step Indicator</title>
282
+ <circle
283
+ cx={size / 2}
284
+ cy={size / 2}
285
+ r={radius}
286
+ fill="none"
287
+ stroke="currentColor"
288
+ strokeWidth={strokeWidth}
289
+ className="text-muted-foreground"
290
+ />
291
+ <circle
292
+ cx={size / 2}
293
+ cy={size / 2}
294
+ r={radius}
295
+ fill="none"
296
+ stroke="currentColor"
297
+ strokeWidth={strokeWidth}
298
+ strokeDasharray={circumference}
299
+ strokeDashoffset={dashOffset}
300
+ className="text-primary transition-all duration-300 ease-in-out"
301
+ transform={`rotate(-90 ${size / 2} ${size / 2})`}
302
+ />
303
+ </svg>
304
+ <div className="absolute inset-0 flex items-center justify-center">
305
+ <span className="text-sm font-medium" aria-live="polite">
306
+ {currentStep} of {totalSteps}
307
+ </span>
308
+ </div>
309
+ </div>
310
+ )
311
+ }
312
+
313
+ const classForNavigationList = cva("flex gap-2", {
314
+ variants: {
315
+ variant: {
316
+ horizontal: "flex-row items-center justify-between",
317
+ vertical: "flex-col",
318
+ circle: "flex-row items-center justify-between"
319
+ }
320
+ }
321
+ })
322
+
323
+ const classForSeparator = cva(
324
+ [
325
+ "bg-muted",
326
+ "data-[state=completed]:bg-primary data-[disabled]:opacity-50",
327
+ "transition-all duration-300 ease-in-out"
328
+ ],
329
+ {
330
+ variants: {
331
+ orientation: {
332
+ horizontal: "h-0.5 flex-1",
333
+ vertical: "h-full w-0.5"
334
+ },
335
+ labelOrientation: {
336
+ vertical: "absolute left-[calc(50%+30px)] right-[calc(-50%+20px)] top-5 block shrink-0"
337
+ }
338
+ }
339
+ }
340
+ )
341
+
342
+ function scrollIntoStepperPanel(node, tracking) {
343
+ if (tracking) {
344
+ node?.scrollIntoView({ behavior: "smooth", block: "center" })
345
+ }
346
+ }
347
+
348
+ const useStepChildren = (children) => {
349
+ return useMemo(() => extractChildren(children), [children])
350
+ }
351
+
352
+ const extractChildren = (children) => {
353
+ const childrenArray = Children.toArray(children)
354
+ const map = new Map()
355
+
356
+ for (const child of childrenArray) {
357
+ if (isValidElement(child)) {
358
+ if (child.type === Title) {
359
+ map.set("title", child)
360
+ } else if (child.type === Description) {
361
+ map.set("description", child)
362
+ } else {
363
+ map.set("panel", child)
364
+ }
365
+ }
366
+ }
367
+
368
+ return map
369
+ }
370
+
371
+ const onStepKeyDown = (e, nextStep, prevStep) => {
372
+ const { key } = e
373
+ const directions = {
374
+ next: ["ArrowRight", "ArrowDown"],
375
+ prev: ["ArrowLeft", "ArrowUp"]
376
+ }
377
+
378
+ if (directions.next.includes(key) || directions.prev.includes(key)) {
379
+ const direction = directions.next.includes(key) ? "next" : "prev"
380
+ const step = direction === "next" ? nextStep : prevStep
381
+
382
+ if (!step) {
383
+ return
384
+ }
385
+
386
+ const stepElement = document.getElementById(`step-${step.id}`)
387
+ if (!stepElement) {
388
+ return
389
+ }
390
+
391
+ const isActive = stepElement.parentElement?.getAttribute("data-state") !== "inactive"
392
+ if (isActive || direction === "prev") {
393
+ stepElement.focus()
394
+ }
395
+ }
396
+ }
397
+
398
+ const getStepState = (currentIndex, stepIndex) => {
399
+ if (currentIndex === stepIndex) {
400
+ return "active"
401
+ }
402
+ if (currentIndex > stepIndex) {
403
+ return "completed"
404
+ }
405
+ return "inactive"
406
+ }
407
+
408
+ export { defineStepper }
@@ -0,0 +1,36 @@
1
+ "use client"
2
+ import * as AvatarPrimitive from "@radix-ui/react-avatar"
3
+
4
+ import { cn } from "#cli/lib/utils.mjs"
5
+
6
+ function Avatar({ className, ...props }) {
7
+ return (
8
+ <AvatarPrimitive.Root
9
+ data-slot="avatar"
10
+ className={cn("relative flex size-8 shrink-0 overflow-hidden rounded-full", className)}
11
+ {...props}
12
+ />
13
+ )
14
+ }
15
+
16
+ function AvatarImage({ className, ...props }) {
17
+ return (
18
+ <AvatarPrimitive.Image
19
+ data-slot="avatar-image"
20
+ className={cn("aspect-square size-full", className)}
21
+ {...props}
22
+ />
23
+ )
24
+ }
25
+
26
+ function AvatarFallback({ className, ...props }) {
27
+ return (
28
+ <AvatarPrimitive.Fallback
29
+ data-slot="avatar-fallback"
30
+ className={cn("bg-muted flex size-full items-center justify-center rounded-full", className)}
31
+ {...props}
32
+ />
33
+ )
34
+ }
35
+
36
+ export { Avatar, AvatarFallback, AvatarImage }
@@ -0,0 +1,31 @@
1
+ import { Slot } from "@radix-ui/react-slot"
2
+ import { cva } from "class-variance-authority"
3
+
4
+ import { cn } from "#cli/lib/utils.mjs"
5
+
6
+ const badgeVariants = cva(
7
+ "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
12
+ secondary:
13
+ "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
14
+ destructive:
15
+ "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
16
+ outline: "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground"
17
+ }
18
+ },
19
+ defaultVariants: {
20
+ variant: "default"
21
+ }
22
+ }
23
+ )
24
+
25
+ function Badge({ className, variant, asChild = false, ...props }) {
26
+ const Comp = asChild ? Slot : "span"
27
+
28
+ return <Comp data-slot="badge" className={cn(badgeVariants({ variant }), className)} {...props} />
29
+ }
30
+
31
+ export { Badge, badgeVariants }