@maizzle/framework 6.0.0-9 → 6.0.0-rc.0

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 (614) hide show
  1. package/bin/maizzle.mjs +10 -0
  2. package/dist/build.d.mts +19 -0
  3. package/dist/build.d.mts.map +1 -0
  4. package/dist/build.mjs +121 -0
  5. package/dist/build.mjs.map +1 -0
  6. package/dist/components/Button.vue +141 -0
  7. package/dist/components/Divider.vue +105 -0
  8. package/dist/components/NoWidows.vue +123 -0
  9. package/dist/components/NotOutlook.vue +17 -0
  10. package/dist/components/Outlook.vue +74 -0
  11. package/dist/components/Spacer.vue +36 -0
  12. package/dist/components/Vml.vue +89 -0
  13. package/dist/components/WithUrl.vue +190 -0
  14. package/dist/components/utils.d.mts +5 -0
  15. package/dist/components/utils.d.mts.map +1 -0
  16. package/dist/components/utils.mjs +9 -0
  17. package/dist/components/utils.mjs.map +1 -0
  18. package/dist/components/utils.ts +6 -0
  19. package/dist/composables/defineConfig.d.mts +14 -0
  20. package/dist/composables/defineConfig.d.mts.map +1 -0
  21. package/dist/composables/defineConfig.mjs +34 -0
  22. package/dist/composables/defineConfig.mjs.map +1 -0
  23. package/dist/composables/renderContext.d.mts +19 -0
  24. package/dist/composables/renderContext.d.mts.map +1 -0
  25. package/dist/composables/renderContext.mjs +6 -0
  26. package/dist/composables/renderContext.mjs.map +1 -0
  27. package/dist/composables/useConfig.d.mts +9 -0
  28. package/dist/composables/useConfig.d.mts.map +1 -0
  29. package/dist/composables/useConfig.mjs +13 -0
  30. package/dist/composables/useConfig.mjs.map +1 -0
  31. package/dist/composables/useDoctype.d.mts +13 -0
  32. package/dist/composables/useDoctype.d.mts.map +1 -0
  33. package/dist/composables/useDoctype.mjs +20 -0
  34. package/dist/composables/useDoctype.mjs.map +1 -0
  35. package/dist/composables/useEvent.d.mts +17 -0
  36. package/dist/composables/useEvent.d.mts.map +1 -0
  37. package/dist/composables/useEvent.mjs +25 -0
  38. package/dist/composables/useEvent.mjs.map +1 -0
  39. package/dist/composables/usePlaintext.d.mts +19 -0
  40. package/dist/composables/usePlaintext.d.mts.map +1 -0
  41. package/dist/composables/usePlaintext.mjs +22 -0
  42. package/dist/composables/usePlaintext.mjs.map +1 -0
  43. package/dist/config/defaults.d.mts +6 -0
  44. package/dist/config/defaults.d.mts.map +1 -0
  45. package/dist/config/defaults.mjs +28 -0
  46. package/dist/config/defaults.mjs.map +1 -0
  47. package/dist/config/index.d.mts +15 -0
  48. package/dist/config/index.d.mts.map +1 -0
  49. package/dist/config/index.mjs +47 -0
  50. package/dist/config/index.mjs.map +1 -0
  51. package/dist/events/index.d.mts +91 -0
  52. package/dist/events/index.d.mts.map +1 -0
  53. package/dist/events/index.mjs +110 -0
  54. package/dist/events/index.mjs.map +1 -0
  55. package/dist/index.d.mts +29 -0
  56. package/dist/index.mjs +29 -0
  57. package/dist/plaintext.d.mts +5 -0
  58. package/dist/plaintext.d.mts.map +1 -0
  59. package/dist/plaintext.mjs +15 -0
  60. package/dist/plaintext.mjs.map +1 -0
  61. package/dist/plugin.d.mts +17 -0
  62. package/dist/plugin.d.mts.map +1 -0
  63. package/dist/plugin.mjs +41 -0
  64. package/dist/plugin.mjs.map +1 -0
  65. package/dist/plugins/postcss/mergeMediaQueries.d.mts +18 -0
  66. package/dist/plugins/postcss/mergeMediaQueries.d.mts.map +1 -0
  67. package/dist/plugins/postcss/mergeMediaQueries.mjs +22 -0
  68. package/dist/plugins/postcss/mergeMediaQueries.mjs.map +1 -0
  69. package/dist/plugins/postcss/pruneVars.d.mts +8 -0
  70. package/dist/plugins/postcss/pruneVars.d.mts.map +1 -0
  71. package/dist/plugins/postcss/pruneVars.mjs +49 -0
  72. package/dist/plugins/postcss/pruneVars.mjs.map +1 -0
  73. package/dist/plugins/postcss/removeDeclarations.d.mts +12 -0
  74. package/dist/plugins/postcss/removeDeclarations.d.mts.map +1 -0
  75. package/dist/plugins/postcss/removeDeclarations.mjs +45 -0
  76. package/dist/plugins/postcss/removeDeclarations.mjs.map +1 -0
  77. package/dist/plugins/postcss/tailwindCleanup.d.mts +18 -0
  78. package/dist/plugins/postcss/tailwindCleanup.d.mts.map +1 -0
  79. package/dist/plugins/postcss/tailwindCleanup.mjs +35 -0
  80. package/dist/plugins/postcss/tailwindCleanup.mjs.map +1 -0
  81. package/dist/render/createRenderer.d.mts +34 -0
  82. package/dist/render/createRenderer.d.mts.map +1 -0
  83. package/dist/render/createRenderer.mjs +149 -0
  84. package/dist/render/createRenderer.mjs.map +1 -0
  85. package/dist/render/index.d.mts +26 -0
  86. package/dist/render/index.d.mts.map +1 -0
  87. package/dist/render/index.mjs +40 -0
  88. package/dist/render/index.mjs.map +1 -0
  89. package/dist/serve.d.mts +25 -0
  90. package/dist/serve.d.mts.map +1 -0
  91. package/dist/serve.mjs +402 -0
  92. package/dist/serve.mjs.map +1 -0
  93. package/dist/server/compatibility.d.mts +6 -0
  94. package/dist/server/compatibility.d.mts.map +1 -0
  95. package/dist/server/compatibility.mjs +83 -0
  96. package/dist/server/compatibility.mjs.map +1 -0
  97. package/dist/server/linter.d.mts +6 -0
  98. package/dist/server/linter.d.mts.map +1 -0
  99. package/dist/server/linter.mjs +200 -0
  100. package/dist/server/linter.mjs.map +1 -0
  101. package/dist/server/ui/App.vue +360 -0
  102. package/dist/server/ui/components/ui/button/Button.vue +31 -0
  103. package/dist/server/ui/components/ui/button/index.ts +38 -0
  104. package/dist/server/ui/components/ui/command/Command.vue +87 -0
  105. package/dist/server/ui/components/ui/command/CommandDialog.vue +31 -0
  106. package/dist/server/ui/components/ui/command/CommandEmpty.vue +27 -0
  107. package/dist/server/ui/components/ui/command/CommandGroup.vue +45 -0
  108. package/dist/server/ui/components/ui/command/CommandInput.vue +39 -0
  109. package/dist/server/ui/components/ui/command/CommandItem.vue +76 -0
  110. package/dist/server/ui/components/ui/command/CommandList.vue +25 -0
  111. package/dist/server/ui/components/ui/command/CommandSeparator.vue +21 -0
  112. package/dist/server/ui/components/ui/command/CommandShortcut.vue +17 -0
  113. package/dist/server/ui/components/ui/command/index.ts +25 -0
  114. package/dist/server/ui/components/ui/dialog/Dialog.vue +19 -0
  115. package/dist/server/ui/components/ui/dialog/DialogClose.vue +15 -0
  116. package/dist/server/ui/components/ui/dialog/DialogContent.vue +53 -0
  117. package/dist/server/ui/components/ui/dialog/DialogDescription.vue +23 -0
  118. package/dist/server/ui/components/ui/dialog/DialogFooter.vue +27 -0
  119. package/dist/server/ui/components/ui/dialog/DialogHeader.vue +17 -0
  120. package/dist/server/ui/components/ui/dialog/DialogOverlay.vue +21 -0
  121. package/dist/server/ui/components/ui/dialog/DialogScrollContent.vue +59 -0
  122. package/dist/server/ui/components/ui/dialog/DialogTitle.vue +23 -0
  123. package/dist/server/ui/components/ui/dialog/DialogTrigger.vue +15 -0
  124. package/dist/server/ui/components/ui/dialog/index.ts +10 -0
  125. package/dist/server/ui/components/ui/dropdown-menu/DropdownMenu.vue +19 -0
  126. package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +39 -0
  127. package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuContent.vue +39 -0
  128. package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuGroup.vue +15 -0
  129. package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuItem.vue +31 -0
  130. package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuLabel.vue +23 -0
  131. package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +21 -0
  132. package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +40 -0
  133. package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuSeparator.vue +23 -0
  134. package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuShortcut.vue +17 -0
  135. package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuSub.vue +18 -0
  136. package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuSubContent.vue +27 -0
  137. package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +31 -0
  138. package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuTrigger.vue +17 -0
  139. package/dist/server/ui/components/ui/dropdown-menu/index.ts +16 -0
  140. package/dist/server/ui/components/ui/empty/Empty.vue +20 -0
  141. package/dist/server/ui/components/ui/empty/EmptyContent.vue +20 -0
  142. package/dist/server/ui/components/ui/empty/EmptyDescription.vue +20 -0
  143. package/dist/server/ui/components/ui/empty/EmptyHeader.vue +20 -0
  144. package/dist/server/ui/components/ui/empty/EmptyMedia.vue +21 -0
  145. package/dist/server/ui/components/ui/empty/EmptyTitle.vue +17 -0
  146. package/dist/server/ui/components/ui/empty/index.ts +26 -0
  147. package/dist/server/ui/components/ui/input/Input.vue +33 -0
  148. package/dist/server/ui/components/ui/input/index.ts +1 -0
  149. package/dist/server/ui/components/ui/kbd/Kbd.vue +20 -0
  150. package/dist/server/ui/components/ui/kbd/KbdGroup.vue +17 -0
  151. package/dist/server/ui/components/ui/kbd/index.ts +2 -0
  152. package/dist/server/ui/components/ui/resizable/ResizableHandle.vue +30 -0
  153. package/dist/server/ui/components/ui/resizable/ResizablePanel.vue +21 -0
  154. package/dist/server/ui/components/ui/resizable/ResizablePanelGroup.vue +25 -0
  155. package/dist/server/ui/components/ui/resizable/index.ts +3 -0
  156. package/dist/server/ui/components/ui/scroll-area/ScrollArea.vue +33 -0
  157. package/dist/server/ui/components/ui/scroll-area/ScrollBar.vue +32 -0
  158. package/dist/server/ui/components/ui/scroll-area/index.ts +2 -0
  159. package/dist/server/ui/components/ui/separator/Separator.vue +29 -0
  160. package/dist/server/ui/components/ui/separator/index.ts +1 -0
  161. package/dist/server/ui/components/ui/sheet/Sheet.vue +19 -0
  162. package/dist/server/ui/components/ui/sheet/SheetClose.vue +15 -0
  163. package/dist/server/ui/components/ui/sheet/SheetContent.vue +62 -0
  164. package/dist/server/ui/components/ui/sheet/SheetDescription.vue +21 -0
  165. package/dist/server/ui/components/ui/sheet/SheetFooter.vue +16 -0
  166. package/dist/server/ui/components/ui/sheet/SheetHeader.vue +15 -0
  167. package/dist/server/ui/components/ui/sheet/SheetOverlay.vue +21 -0
  168. package/dist/server/ui/components/ui/sheet/SheetTitle.vue +21 -0
  169. package/dist/server/ui/components/ui/sheet/SheetTrigger.vue +15 -0
  170. package/dist/server/ui/components/ui/sheet/index.ts +8 -0
  171. package/dist/server/ui/components/ui/sidebar/Sidebar.vue +96 -0
  172. package/dist/server/ui/components/ui/sidebar/SidebarContent.vue +18 -0
  173. package/dist/server/ui/components/ui/sidebar/SidebarFooter.vue +18 -0
  174. package/dist/server/ui/components/ui/sidebar/SidebarGroup.vue +18 -0
  175. package/dist/server/ui/components/ui/sidebar/SidebarGroupAction.vue +27 -0
  176. package/dist/server/ui/components/ui/sidebar/SidebarGroupContent.vue +18 -0
  177. package/dist/server/ui/components/ui/sidebar/SidebarGroupLabel.vue +25 -0
  178. package/dist/server/ui/components/ui/sidebar/SidebarHeader.vue +18 -0
  179. package/dist/server/ui/components/ui/sidebar/SidebarInput.vue +31 -0
  180. package/dist/server/ui/components/ui/sidebar/SidebarInset.vue +21 -0
  181. package/dist/server/ui/components/ui/sidebar/SidebarMenu.vue +18 -0
  182. package/dist/server/ui/components/ui/sidebar/SidebarMenuAction.vue +35 -0
  183. package/dist/server/ui/components/ui/sidebar/SidebarMenuBadge.vue +26 -0
  184. package/dist/server/ui/components/ui/sidebar/SidebarMenuButton.vue +48 -0
  185. package/dist/server/ui/components/ui/sidebar/SidebarMenuButtonChild.vue +36 -0
  186. package/dist/server/ui/components/ui/sidebar/SidebarMenuItem.vue +18 -0
  187. package/dist/server/ui/components/ui/sidebar/SidebarMenuSkeleton.vue +35 -0
  188. package/dist/server/ui/components/ui/sidebar/SidebarMenuSub.vue +22 -0
  189. package/dist/server/ui/components/ui/sidebar/SidebarMenuSubButton.vue +36 -0
  190. package/dist/server/ui/components/ui/sidebar/SidebarMenuSubItem.vue +18 -0
  191. package/dist/server/ui/components/ui/sidebar/SidebarProvider.vue +82 -0
  192. package/dist/server/ui/components/ui/sidebar/SidebarRail.vue +33 -0
  193. package/dist/server/ui/components/ui/sidebar/SidebarSeparator.vue +19 -0
  194. package/dist/server/ui/components/ui/sidebar/SidebarTrigger.vue +28 -0
  195. package/dist/server/ui/components/ui/sidebar/index.ts +60 -0
  196. package/dist/server/ui/components/ui/sidebar/utils.ts +19 -0
  197. package/dist/server/ui/components/ui/skeleton/Skeleton.vue +17 -0
  198. package/dist/server/ui/components/ui/skeleton/index.ts +1 -0
  199. package/dist/server/ui/components/ui/tabs/Tabs.vue +24 -0
  200. package/dist/server/ui/components/ui/tabs/TabsContent.vue +21 -0
  201. package/dist/server/ui/components/ui/tabs/TabsList.vue +24 -0
  202. package/dist/server/ui/components/ui/tabs/TabsTrigger.vue +26 -0
  203. package/dist/server/ui/components/ui/tabs/index.ts +4 -0
  204. package/dist/server/ui/components/ui/toggle/Toggle.vue +35 -0
  205. package/dist/server/ui/components/ui/toggle/index.ts +28 -0
  206. package/dist/server/ui/components/ui/toggle-group/ToggleGroup.vue +49 -0
  207. package/dist/server/ui/components/ui/toggle-group/ToggleGroupItem.vue +46 -0
  208. package/dist/server/ui/components/ui/toggle-group/index.ts +2 -0
  209. package/dist/server/ui/components/ui/tooltip/Tooltip.vue +19 -0
  210. package/dist/server/ui/components/ui/tooltip/TooltipContent.vue +34 -0
  211. package/dist/server/ui/components/ui/tooltip/TooltipProvider.vue +14 -0
  212. package/dist/server/ui/components/ui/tooltip/TooltipTrigger.vue +15 -0
  213. package/dist/server/ui/components/ui/tooltip/index.ts +4 -0
  214. package/dist/server/ui/favicon.svg +1 -0
  215. package/dist/server/ui/index.html +13 -0
  216. package/dist/server/ui/lib/utils.ts +7 -0
  217. package/dist/server/ui/logo-gradient.svg +1 -0
  218. package/dist/server/ui/logo.svg +1 -0
  219. package/dist/server/ui/main.css +129 -0
  220. package/dist/server/ui/main.ts +16 -0
  221. package/dist/server/ui/mark-gradient.svg +1 -0
  222. package/dist/server/ui/mark.svg +1 -0
  223. package/dist/server/ui/pages/Home.vue +39 -0
  224. package/dist/server/ui/pages/Preview.vue +609 -0
  225. package/dist/server/ui/stripes.svg +174 -0
  226. package/dist/transformers/addAttributes.d.mts +32 -0
  227. package/dist/transformers/addAttributes.d.mts.map +1 -0
  228. package/dist/transformers/addAttributes.mjs +101 -0
  229. package/dist/transformers/addAttributes.mjs.map +1 -0
  230. package/dist/transformers/attributeToStyle.d.mts +25 -0
  231. package/dist/transformers/attributeToStyle.d.mts.map +1 -0
  232. package/dist/transformers/attributeToStyle.mjs +80 -0
  233. package/dist/transformers/attributeToStyle.mjs.map +1 -0
  234. package/dist/transformers/base.d.mts +8 -0
  235. package/dist/transformers/base.d.mts.map +1 -0
  236. package/dist/transformers/base.mjs +160 -0
  237. package/dist/transformers/base.mjs.map +1 -0
  238. package/dist/transformers/entities.d.mts +8 -0
  239. package/dist/transformers/entities.d.mts.map +1 -0
  240. package/dist/transformers/entities.mjs +38 -0
  241. package/dist/transformers/entities.mjs.map +1 -0
  242. package/dist/transformers/format.d.mts +15 -0
  243. package/dist/transformers/format.d.mts.map +1 -0
  244. package/dist/transformers/format.mjs +26 -0
  245. package/dist/transformers/format.mjs.map +1 -0
  246. package/dist/transformers/index.d.mts +35 -0
  247. package/dist/transformers/index.d.mts.map +1 -0
  248. package/dist/transformers/index.mjs +73 -0
  249. package/dist/transformers/index.mjs.map +1 -0
  250. package/dist/transformers/inlineCSS.d.mts +30 -0
  251. package/dist/transformers/inlineCSS.d.mts.map +1 -0
  252. package/dist/transformers/inlineCSS.mjs +79 -0
  253. package/dist/transformers/inlineCSS.mjs.map +1 -0
  254. package/dist/transformers/inlineLink.d.mts +14 -0
  255. package/dist/transformers/inlineLink.d.mts.map +1 -0
  256. package/dist/transformers/inlineLink.mjs +76 -0
  257. package/dist/transformers/inlineLink.mjs.map +1 -0
  258. package/dist/transformers/minify.d.mts +17 -0
  259. package/dist/transformers/minify.d.mts.map +1 -0
  260. package/dist/transformers/minify.mjs +24 -0
  261. package/dist/transformers/minify.mjs.map +1 -0
  262. package/dist/transformers/purgeCSS.d.mts +23 -0
  263. package/dist/transformers/purgeCSS.d.mts.map +1 -0
  264. package/dist/transformers/purgeCSS.mjs +66 -0
  265. package/dist/transformers/purgeCSS.mjs.map +1 -0
  266. package/dist/transformers/removeAttributes.d.mts +31 -0
  267. package/dist/transformers/removeAttributes.d.mts.map +1 -0
  268. package/dist/transformers/removeAttributes.mjs +63 -0
  269. package/dist/transformers/removeAttributes.mjs.map +1 -0
  270. package/dist/transformers/replaceStrings.d.mts +16 -0
  271. package/dist/transformers/replaceStrings.d.mts.map +1 -0
  272. package/dist/transformers/replaceStrings.mjs +19 -0
  273. package/dist/transformers/replaceStrings.mjs.map +1 -0
  274. package/dist/transformers/safeClassNames.d.mts +22 -0
  275. package/dist/transformers/safeClassNames.d.mts.map +1 -0
  276. package/dist/transformers/safeClassNames.mjs +103 -0
  277. package/dist/transformers/safeClassNames.mjs.map +1 -0
  278. package/dist/transformers/shorthandCSS.d.mts +24 -0
  279. package/dist/transformers/shorthandCSS.d.mts.map +1 -0
  280. package/dist/transformers/shorthandCSS.mjs +48 -0
  281. package/dist/transformers/shorthandCSS.mjs.map +1 -0
  282. package/dist/transformers/tailwindcss.d.mts +20 -0
  283. package/dist/transformers/tailwindcss.d.mts.map +1 -0
  284. package/dist/transformers/tailwindcss.mjs +136 -0
  285. package/dist/transformers/tailwindcss.mjs.map +1 -0
  286. package/dist/transformers/urlQuery.d.mts +24 -0
  287. package/dist/transformers/urlQuery.d.mts.map +1 -0
  288. package/dist/transformers/urlQuery.mjs +65 -0
  289. package/dist/transformers/urlQuery.mjs.map +1 -0
  290. package/dist/types/config.d.mts +129 -0
  291. package/dist/types/config.d.mts.map +1 -0
  292. package/dist/types/config.mjs +1 -0
  293. package/dist/types/index.d.mts +2 -0
  294. package/dist/types/index.mjs +1 -0
  295. package/dist/utils/ast/index.d.mts +4 -0
  296. package/dist/utils/ast/index.mjs +5 -0
  297. package/dist/utils/ast/parser.d.mts +7 -0
  298. package/dist/utils/ast/parser.d.mts.map +1 -0
  299. package/dist/utils/ast/parser.mjs +15 -0
  300. package/dist/utils/ast/parser.mjs.map +1 -0
  301. package/dist/utils/ast/serializer.d.mts +7 -0
  302. package/dist/utils/ast/serializer.d.mts.map +1 -0
  303. package/dist/utils/ast/serializer.mjs +13 -0
  304. package/dist/utils/ast/serializer.mjs.map +1 -0
  305. package/dist/utils/ast/walker.d.mts +7 -0
  306. package/dist/utils/ast/walker.d.mts.map +1 -0
  307. package/dist/utils/ast/walker.mjs +12 -0
  308. package/dist/utils/ast/walker.mjs.map +1 -0
  309. package/dist/utils/url.d.mts +8 -0
  310. package/dist/utils/url.d.mts.map +1 -0
  311. package/dist/utils/url.mjs +32 -0
  312. package/dist/utils/url.mjs.map +1 -0
  313. package/node_modules/@clack/core/CHANGELOG.md +257 -0
  314. package/node_modules/@clack/core/LICENSE +9 -0
  315. package/node_modules/@clack/core/README.md +22 -0
  316. package/node_modules/@clack/core/dist/index.cjs +15 -0
  317. package/node_modules/@clack/core/dist/index.cjs.map +1 -0
  318. package/node_modules/@clack/core/dist/index.d.cts +211 -0
  319. package/node_modules/@clack/core/dist/index.d.mts +211 -0
  320. package/node_modules/@clack/core/dist/index.d.ts +211 -0
  321. package/node_modules/@clack/core/dist/index.mjs +15 -0
  322. package/node_modules/@clack/core/dist/index.mjs.map +1 -0
  323. package/node_modules/@clack/core/package.json +62 -0
  324. package/node_modules/@clack/prompts/CHANGELOG.md +412 -0
  325. package/node_modules/@clack/prompts/LICENSE +9 -0
  326. package/node_modules/@clack/prompts/README.md +207 -0
  327. package/node_modules/@clack/prompts/dist/index.cjs +87 -0
  328. package/node_modules/@clack/prompts/dist/index.cjs.map +1 -0
  329. package/node_modules/@clack/prompts/dist/index.d.cts +165 -0
  330. package/node_modules/@clack/prompts/dist/index.d.mts +165 -0
  331. package/node_modules/@clack/prompts/dist/index.d.ts +165 -0
  332. package/node_modules/@clack/prompts/dist/index.mjs +87 -0
  333. package/node_modules/@clack/prompts/dist/index.mjs.map +1 -0
  334. package/node_modules/@clack/prompts/package.json +61 -0
  335. package/node_modules/@maizzle/cli/LICENSE +21 -0
  336. package/node_modules/@maizzle/cli/README.md +58 -0
  337. package/node_modules/@maizzle/cli/dist/commands/make/component.d.mts +4 -0
  338. package/node_modules/@maizzle/cli/dist/commands/make/component.mjs +31 -0
  339. package/node_modules/@maizzle/cli/dist/commands/make/config.d.mts +4 -0
  340. package/node_modules/@maizzle/cli/dist/commands/make/config.mjs +21 -0
  341. package/node_modules/@maizzle/cli/dist/commands/make/layout.d.mts +4 -0
  342. package/node_modules/@maizzle/cli/dist/commands/make/layout.mjs +31 -0
  343. package/node_modules/@maizzle/cli/dist/commands/make/scaffold.d.mts +5 -0
  344. package/node_modules/@maizzle/cli/dist/commands/make/scaffold.mjs +28 -0
  345. package/node_modules/@maizzle/cli/dist/commands/make/stubs/component.vue +9 -0
  346. package/node_modules/@maizzle/cli/dist/commands/make/stubs/config.ts +9 -0
  347. package/node_modules/@maizzle/cli/dist/commands/make/stubs/layout.vue +39 -0
  348. package/node_modules/@maizzle/cli/dist/commands/make/stubs/template.vue +8 -0
  349. package/node_modules/@maizzle/cli/dist/commands/make/template.d.mts +4 -0
  350. package/node_modules/@maizzle/cli/dist/commands/make/template.mjs +31 -0
  351. package/node_modules/@maizzle/cli/dist/index.d.mts +8 -0
  352. package/node_modules/@maizzle/cli/dist/index.mjs +42 -0
  353. package/node_modules/@maizzle/cli/package.json +59 -0
  354. package/node_modules/citty/LICENSE +36 -0
  355. package/node_modules/citty/README.md +134 -0
  356. package/node_modules/citty/dist/index.cjs +475 -0
  357. package/node_modules/citty/dist/index.d.cts +80 -0
  358. package/node_modules/citty/dist/index.d.mts +80 -0
  359. package/node_modules/citty/dist/index.d.ts +80 -0
  360. package/node_modules/citty/dist/index.mjs +463 -0
  361. package/node_modules/citty/package.json +49 -0
  362. package/node_modules/commander/LICENSE +22 -0
  363. package/node_modules/commander/Readme.md +1149 -0
  364. package/node_modules/commander/esm.mjs +16 -0
  365. package/node_modules/commander/index.js +24 -0
  366. package/node_modules/commander/lib/argument.js +149 -0
  367. package/node_modules/commander/lib/command.js +2662 -0
  368. package/node_modules/commander/lib/error.js +39 -0
  369. package/node_modules/commander/lib/help.js +709 -0
  370. package/node_modules/commander/lib/option.js +367 -0
  371. package/node_modules/commander/lib/suggestSimilar.js +101 -0
  372. package/node_modules/commander/package-support.json +16 -0
  373. package/node_modules/commander/package.json +82 -0
  374. package/node_modules/commander/typings/esm.d.mts +3 -0
  375. package/node_modules/commander/typings/index.d.ts +1045 -0
  376. package/node_modules/consola/LICENSE +47 -0
  377. package/node_modules/consola/README.md +352 -0
  378. package/node_modules/consola/basic.d.ts +1 -0
  379. package/node_modules/consola/browser.d.ts +1 -0
  380. package/node_modules/consola/core.d.ts +1 -0
  381. package/node_modules/consola/dist/basic.cjs +32 -0
  382. package/node_modules/consola/dist/basic.d.cts +23 -0
  383. package/node_modules/consola/dist/basic.d.mts +21 -0
  384. package/node_modules/consola/dist/basic.d.ts +23 -0
  385. package/node_modules/consola/dist/basic.mjs +24 -0
  386. package/node_modules/consola/dist/browser.cjs +84 -0
  387. package/node_modules/consola/dist/browser.d.cts +23 -0
  388. package/node_modules/consola/dist/browser.d.mts +21 -0
  389. package/node_modules/consola/dist/browser.d.ts +23 -0
  390. package/node_modules/consola/dist/browser.mjs +76 -0
  391. package/node_modules/consola/dist/chunks/prompt.cjs +288 -0
  392. package/node_modules/consola/dist/chunks/prompt.mjs +280 -0
  393. package/node_modules/consola/dist/core.cjs +517 -0
  394. package/node_modules/consola/dist/core.d.cts +459 -0
  395. package/node_modules/consola/dist/core.d.mts +459 -0
  396. package/node_modules/consola/dist/core.d.ts +459 -0
  397. package/node_modules/consola/dist/core.mjs +512 -0
  398. package/node_modules/consola/dist/index.cjs +663 -0
  399. package/node_modules/consola/dist/index.d.cts +24 -0
  400. package/node_modules/consola/dist/index.d.mts +22 -0
  401. package/node_modules/consola/dist/index.d.ts +24 -0
  402. package/node_modules/consola/dist/index.mjs +651 -0
  403. package/node_modules/consola/dist/shared/consola.DCGIlDNP.cjs +75 -0
  404. package/node_modules/consola/dist/shared/consola.DRwqZj3T.mjs +72 -0
  405. package/node_modules/consola/dist/shared/consola.DXBYu-KD.mjs +288 -0
  406. package/node_modules/consola/dist/shared/consola.DwRq1yyg.cjs +312 -0
  407. package/node_modules/consola/dist/utils.cjs +64 -0
  408. package/node_modules/consola/dist/utils.d.cts +286 -0
  409. package/node_modules/consola/dist/utils.d.mts +286 -0
  410. package/node_modules/consola/dist/utils.d.ts +286 -0
  411. package/node_modules/consola/dist/utils.mjs +54 -0
  412. package/node_modules/consola/lib/index.cjs +10 -0
  413. package/node_modules/consola/package.json +136 -0
  414. package/node_modules/consola/utils.d.ts +1 -0
  415. package/node_modules/create-maizzle/README.md +86 -0
  416. package/node_modules/create-maizzle/bin/create-maizzle.mjs +4 -0
  417. package/node_modules/create-maizzle/node_modules/@clack/core/CHANGELOG.md +340 -0
  418. package/node_modules/create-maizzle/node_modules/@clack/core/LICENSE +9 -0
  419. package/node_modules/create-maizzle/node_modules/@clack/core/README.md +22 -0
  420. package/node_modules/create-maizzle/node_modules/@clack/core/dist/index.d.mts +349 -0
  421. package/node_modules/create-maizzle/node_modules/@clack/core/dist/index.mjs +11 -0
  422. package/node_modules/create-maizzle/node_modules/@clack/core/dist/index.mjs.map +1 -0
  423. package/node_modules/create-maizzle/node_modules/@clack/core/package.json +60 -0
  424. package/node_modules/create-maizzle/node_modules/@clack/prompts/CHANGELOG.md +576 -0
  425. package/node_modules/create-maizzle/node_modules/@clack/prompts/LICENSE +9 -0
  426. package/node_modules/create-maizzle/node_modules/@clack/prompts/README.md +270 -0
  427. package/node_modules/create-maizzle/node_modules/@clack/prompts/dist/index.d.mts +391 -0
  428. package/node_modules/create-maizzle/node_modules/@clack/prompts/dist/index.mjs +137 -0
  429. package/node_modules/create-maizzle/node_modules/@clack/prompts/dist/index.mjs.map +1 -0
  430. package/node_modules/create-maizzle/node_modules/@clack/prompts/package.json +65 -0
  431. package/node_modules/create-maizzle/package.json +47 -0
  432. package/node_modules/create-maizzle/src/index.js +242 -0
  433. package/node_modules/defu/LICENSE +21 -0
  434. package/node_modules/defu/README.md +171 -0
  435. package/node_modules/defu/dist/defu.cjs +77 -0
  436. package/node_modules/defu/dist/defu.d.cts +31 -0
  437. package/node_modules/defu/dist/defu.d.mts +29 -0
  438. package/node_modules/defu/dist/defu.d.ts +31 -0
  439. package/node_modules/defu/dist/defu.mjs +69 -0
  440. package/node_modules/defu/lib/defu.cjs +10 -0
  441. package/node_modules/defu/lib/defu.d.cts +12 -0
  442. package/node_modules/defu/package.json +48 -0
  443. package/node_modules/fast-string-truncated-width/dist/index.d.ts +4 -0
  444. package/node_modules/fast-string-truncated-width/dist/index.js +171 -0
  445. package/node_modules/fast-string-truncated-width/dist/types.d.ts +22 -0
  446. package/node_modules/fast-string-truncated-width/dist/types.js +2 -0
  447. package/node_modules/fast-string-truncated-width/dist/utils.d.ts +4 -0
  448. package/node_modules/fast-string-truncated-width/dist/utils.js +15 -0
  449. package/node_modules/fast-string-truncated-width/license +21 -0
  450. package/node_modules/fast-string-truncated-width/package.json +35 -0
  451. package/node_modules/fast-string-truncated-width/readme.md +60 -0
  452. package/node_modules/fast-string-width/dist/index.d.ts +4 -0
  453. package/node_modules/fast-string-width/dist/index.js +14 -0
  454. package/node_modules/fast-string-width/license +21 -0
  455. package/node_modules/fast-string-width/package.json +34 -0
  456. package/node_modules/fast-string-width/readme.md +45 -0
  457. package/node_modules/fast-wrap-ansi/LICENSE +23 -0
  458. package/node_modules/fast-wrap-ansi/README.md +26 -0
  459. package/node_modules/fast-wrap-ansi/lib/main.d.ts +6 -0
  460. package/node_modules/fast-wrap-ansi/lib/main.js +216 -0
  461. package/node_modules/fast-wrap-ansi/lib/main.js.map +1 -0
  462. package/node_modules/fast-wrap-ansi/package.json +51 -0
  463. package/node_modules/giget/LICENSE +184 -0
  464. package/node_modules/giget/README.md +248 -0
  465. package/node_modules/giget/dist/cli.mjs +112 -0
  466. package/node_modules/giget/dist/index.d.mts +49 -0
  467. package/node_modules/giget/dist/index.mjs +22 -0
  468. package/node_modules/giget/dist/shared/giget.OCaTp9b-.mjs +468 -0
  469. package/node_modules/giget/package.json +62 -0
  470. package/node_modules/node-fetch-native/LICENSE +114 -0
  471. package/node_modules/node-fetch-native/README.md +225 -0
  472. package/node_modules/node-fetch-native/dist/chunks/multipart-parser.cjs +2 -0
  473. package/node_modules/node-fetch-native/dist/chunks/multipart-parser.mjs +2 -0
  474. package/node_modules/node-fetch-native/dist/index.cjs +1 -0
  475. package/node_modules/node-fetch-native/dist/index.mjs +1 -0
  476. package/node_modules/node-fetch-native/dist/native.cjs +1 -0
  477. package/node_modules/node-fetch-native/dist/native.mjs +1 -0
  478. package/node_modules/node-fetch-native/dist/node.cjs +19 -0
  479. package/node_modules/node-fetch-native/dist/node.mjs +19 -0
  480. package/node_modules/node-fetch-native/dist/polyfill.cjs +1 -0
  481. package/node_modules/node-fetch-native/dist/polyfill.mjs +1 -0
  482. package/node_modules/node-fetch-native/dist/proxy-stub.cjs +1 -0
  483. package/node_modules/node-fetch-native/dist/proxy-stub.mjs +1 -0
  484. package/node_modules/node-fetch-native/dist/proxy.cjs +58 -0
  485. package/node_modules/node-fetch-native/dist/shared/node-fetch-native.DfbY2q-x.mjs +1 -0
  486. package/node_modules/node-fetch-native/dist/shared/node-fetch-native.DhEqb06g.cjs +1 -0
  487. package/node_modules/node-fetch-native/index.d.ts +1 -0
  488. package/node_modules/node-fetch-native/lib/empty.cjs +0 -0
  489. package/node_modules/node-fetch-native/lib/empty.mjs +0 -0
  490. package/node_modules/node-fetch-native/lib/index.cjs +11 -0
  491. package/node_modules/node-fetch-native/lib/index.d.cts +10 -0
  492. package/node_modules/node-fetch-native/lib/index.d.mts +10 -0
  493. package/node_modules/node-fetch-native/lib/index.d.ts +10 -0
  494. package/node_modules/node-fetch-native/lib/native.cjs +11 -0
  495. package/node_modules/node-fetch-native/lib/polyfill.d.cts +1 -0
  496. package/node_modules/node-fetch-native/lib/polyfill.d.mts +1 -0
  497. package/node_modules/node-fetch-native/lib/polyfill.d.ts +1 -0
  498. package/node_modules/node-fetch-native/lib/proxy.d.ts +32 -0
  499. package/node_modules/node-fetch-native/node.d.ts +1 -0
  500. package/node_modules/node-fetch-native/package.json +138 -0
  501. package/node_modules/node-fetch-native/polyfill.d.ts +1 -0
  502. package/node_modules/node-fetch-native/proxy.d.ts +1 -0
  503. package/node_modules/nypm/LICENSE +21 -0
  504. package/node_modules/nypm/README.md +152 -0
  505. package/node_modules/nypm/dist/cli.d.mts +1 -0
  506. package/node_modules/nypm/dist/cli.mjs +460 -0
  507. package/node_modules/nypm/dist/index.d.mts +209 -0
  508. package/node_modules/nypm/dist/index.mjs +423 -0
  509. package/node_modules/nypm/node_modules/citty/LICENSE +21 -0
  510. package/node_modules/nypm/node_modules/citty/README.md +231 -0
  511. package/node_modules/nypm/node_modules/citty/dist/THIRD-PARTY-LICENSES.md +33 -0
  512. package/node_modules/nypm/node_modules/citty/dist/_chunks/libs/scule.mjs +70 -0
  513. package/node_modules/nypm/node_modules/citty/dist/index.d.mts +112 -0
  514. package/node_modules/nypm/node_modules/citty/dist/index.mjs +425 -0
  515. package/node_modules/nypm/node_modules/citty/package.json +42 -0
  516. package/node_modules/nypm/package.json +54 -0
  517. package/node_modules/pathe/LICENSE +70 -0
  518. package/node_modules/pathe/README.md +73 -0
  519. package/node_modules/pathe/dist/index.cjs +39 -0
  520. package/node_modules/pathe/dist/index.d.cts +47 -0
  521. package/node_modules/pathe/dist/index.d.mts +47 -0
  522. package/node_modules/pathe/dist/index.d.ts +47 -0
  523. package/node_modules/pathe/dist/index.mjs +19 -0
  524. package/node_modules/pathe/dist/shared/pathe.BSlhyZSM.cjs +266 -0
  525. package/node_modules/pathe/dist/shared/pathe.M-eThtNZ.mjs +249 -0
  526. package/node_modules/pathe/dist/utils.cjs +82 -0
  527. package/node_modules/pathe/dist/utils.d.cts +32 -0
  528. package/node_modules/pathe/dist/utils.d.mts +32 -0
  529. package/node_modules/pathe/dist/utils.d.ts +32 -0
  530. package/node_modules/pathe/dist/utils.mjs +77 -0
  531. package/node_modules/pathe/package.json +61 -0
  532. package/node_modules/pathe/utils.d.ts +1 -0
  533. package/node_modules/picocolors/LICENSE +15 -0
  534. package/node_modules/picocolors/README.md +21 -0
  535. package/node_modules/picocolors/package.json +25 -0
  536. package/node_modules/picocolors/picocolors.browser.js +4 -0
  537. package/node_modules/picocolors/picocolors.d.ts +5 -0
  538. package/node_modules/picocolors/picocolors.js +75 -0
  539. package/node_modules/picocolors/types.d.ts +51 -0
  540. package/node_modules/sisteransi/license +21 -0
  541. package/node_modules/sisteransi/package.json +34 -0
  542. package/node_modules/sisteransi/readme.md +113 -0
  543. package/node_modules/sisteransi/src/index.js +58 -0
  544. package/node_modules/sisteransi/src/sisteransi.d.ts +35 -0
  545. package/node_modules/tinyexec/LICENSE +21 -0
  546. package/node_modules/tinyexec/README.md +269 -0
  547. package/node_modules/tinyexec/dist/LICENSES.txt +83 -0
  548. package/node_modules/tinyexec/dist/main.d.mts +71 -0
  549. package/node_modules/tinyexec/dist/main.mjs +642 -0
  550. package/node_modules/tinyexec/package.json +62 -0
  551. package/package.json +74 -73
  552. package/CHANGELOG.md +0 -770
  553. package/bin/maizzle +0 -5
  554. package/src/commands/build.js +0 -348
  555. package/src/commands/serve.js +0 -3
  556. package/src/generators/plaintext.js +0 -222
  557. package/src/generators/render.js +0 -129
  558. package/src/index.js +0 -49
  559. package/src/posthtml/defaultComponentsConfig.js +0 -19
  560. package/src/posthtml/defaultConfig.js +0 -14
  561. package/src/posthtml/index.js +0 -102
  562. package/src/posthtml/plugins/combineMediaQueries.js +0 -42
  563. package/src/posthtml/plugins/envAttributes.js +0 -32
  564. package/src/posthtml/plugins/envTags.js +0 -33
  565. package/src/posthtml/plugins/expandLinkTag.js +0 -59
  566. package/src/posthtml/plugins/postcss/compileCss.js +0 -125
  567. package/src/posthtml/plugins/removeRawStyleAttributes.js +0 -30
  568. package/src/server/client.js +0 -182
  569. package/src/server/index.js +0 -464
  570. package/src/server/routes/hmr.js +0 -26
  571. package/src/server/routes/index.js +0 -77
  572. package/src/server/views/404.html +0 -59
  573. package/src/server/views/error.html +0 -83
  574. package/src/server/views/index.html +0 -172
  575. package/src/server/websockets.js +0 -27
  576. package/src/transformers/addAttributes.js +0 -29
  577. package/src/transformers/attributeToStyle.js +0 -90
  578. package/src/transformers/baseUrl.js +0 -154
  579. package/src/transformers/core.js +0 -32
  580. package/src/transformers/filters/defaultFilters.js +0 -146
  581. package/src/transformers/filters/index.js +0 -18
  582. package/src/transformers/index.js +0 -259
  583. package/src/transformers/inline.js +0 -306
  584. package/src/transformers/markdown.js +0 -26
  585. package/src/transformers/minify.js +0 -27
  586. package/src/transformers/posthtmlMso.js +0 -14
  587. package/src/transformers/prettify.js +0 -29
  588. package/src/transformers/preventWidows.js +0 -37
  589. package/src/transformers/purge.js +0 -56
  590. package/src/transformers/removeAttributes.js +0 -55
  591. package/src/transformers/replaceStrings.js +0 -37
  592. package/src/transformers/safeClassNames.js +0 -29
  593. package/src/transformers/shorthandCss.js +0 -20
  594. package/src/transformers/sixHex.js +0 -30
  595. package/src/transformers/template.js +0 -26
  596. package/src/transformers/urlParameters.js +0 -20
  597. package/src/transformers/useAttributeSizes.js +0 -63
  598. package/src/utils/getConfigByFilePath.js +0 -143
  599. package/src/utils/node.js +0 -68
  600. package/src/utils/string.js +0 -186
  601. package/types/build.d.ts +0 -166
  602. package/types/config.d.ts +0 -638
  603. package/types/css/combineMediaQueries.d.ts +0 -90
  604. package/types/css/inline.d.ts +0 -218
  605. package/types/css/purge.d.ts +0 -125
  606. package/types/events.d.ts +0 -153
  607. package/types/index.d.ts +0 -232
  608. package/types/markdown.d.ts +0 -33
  609. package/types/minify.d.ts +0 -138
  610. package/types/plaintext.d.ts +0 -56
  611. package/types/posthtml.d.ts +0 -162
  612. package/types/render.d.ts +0 -13
  613. package/types/urlParameters.d.ts +0 -40
  614. package/types/widowWords.d.ts +0 -38
@@ -0,0 +1,200 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { glob } from "tinyglobby";
4
+
5
+ //#region src/server/linter.ts
6
+ async function serveLint(url, config, res) {
7
+ const templateSlug = url.replace("/__maizzle/lint/", "").replace(/\?.*$/, "");
8
+ const match = (await glob(config.content ?? ["emails/**/*.vue"])).find((t) => t.replace(/\.(vue|md)$/, "") === templateSlug);
9
+ if (!match) {
10
+ res.statusCode = 404;
11
+ res.end(JSON.stringify({ error: "Template not found" }));
12
+ return;
13
+ }
14
+ try {
15
+ const source = readFileSync(resolve(match), "utf-8");
16
+ const templateMatch = source.match(/<template\b[^>]*>([\s\S]*)<\/template>/);
17
+ const issues = lintHtml(templateMatch ? templateMatch[1] : source, templateMatch ? source.slice(0, source.indexOf(templateMatch[0]) + templateMatch[0].indexOf(templateMatch[1])).split("\n").length - 1 : 0);
18
+ res.setHeader("Content-Type", "application/json");
19
+ res.end(JSON.stringify(issues));
20
+ } catch (error) {
21
+ res.statusCode = 500;
22
+ res.end(JSON.stringify({ error: error.message }));
23
+ }
24
+ }
25
+ function lintHtml(html, lineOffset = 0) {
26
+ const issues = [];
27
+ const lines = html.split("\n");
28
+ for (let i = 0; i < lines.length; i++) {
29
+ const line = lines[i];
30
+ const lineNum = i + 1 + lineOffset;
31
+ const imgMatches = [...line.matchAll(/<img\b[^>]*?>/gi)];
32
+ for (const match of imgMatches) {
33
+ const tag = match[0];
34
+ if (!/\balt\s*=/i.test(tag)) issues.push({
35
+ type: "warning",
36
+ title: "Missing alt text",
37
+ message: "Image is missing the alt attribute",
38
+ line: lineNum
39
+ });
40
+ }
41
+ for (const match of imgMatches) {
42
+ const srcMatch = match[0].match(/\bsrc\s*=\s*["']([^"']*)["']/i);
43
+ if (!srcMatch) issues.push({
44
+ type: "error",
45
+ title: "Missing image src",
46
+ message: "Image tag has no src attribute",
47
+ line: lineNum
48
+ });
49
+ else if (!srcMatch[1].trim()) issues.push({
50
+ type: "error",
51
+ title: "Empty image src",
52
+ message: "Image src attribute is empty",
53
+ line: lineNum
54
+ });
55
+ else if (srcMatch[1].trim().startsWith("http:")) issues.push({
56
+ type: "warning",
57
+ title: "Insecure image src",
58
+ message: "Image loads over HTTP instead of HTTPS",
59
+ line: lineNum
60
+ });
61
+ }
62
+ const linkMatches = [...line.matchAll(/<a\b[^>]*?>/gi)];
63
+ for (const match of linkMatches) {
64
+ const hrefMatch = match[0].match(/\bhref\s*=\s*["']([^"']*)["']/i);
65
+ if (!hrefMatch) issues.push({
66
+ type: "error",
67
+ title: "Missing link href",
68
+ message: "Anchor tag has no href attribute",
69
+ line: lineNum
70
+ });
71
+ else {
72
+ const href = hrefMatch[1].trim();
73
+ if (!href) issues.push({
74
+ type: "warning",
75
+ title: "Empty link href",
76
+ message: "Link href attribute is empty",
77
+ line: lineNum
78
+ });
79
+ else if (href === "#" || href === "/") issues.push({
80
+ type: "warning",
81
+ title: "Placeholder link",
82
+ message: `Link href is "${href}"`,
83
+ line: lineNum
84
+ });
85
+ else if (href.startsWith("http:")) issues.push({
86
+ type: "warning",
87
+ title: "Insecure link",
88
+ message: "Link uses HTTP instead of HTTPS",
89
+ line: lineNum
90
+ });
91
+ else if (href.startsWith("http") && !/^https?:\/\/.+\..+/i.test(href)) issues.push({
92
+ type: "warning",
93
+ title: "Invalid link",
94
+ message: `Link href "${href}" looks malformed`,
95
+ line: lineNum
96
+ });
97
+ }
98
+ }
99
+ const resourceMatches = [...line.matchAll(/<(?:link|script|source)\b[^>]*?>/gi)];
100
+ for (const match of resourceMatches) {
101
+ const attrMatch = match[0].match(/\b(?:href|src)\s*=\s*["']([^"']*)["']/i);
102
+ if (attrMatch && attrMatch[1].trim().startsWith("http:")) issues.push({
103
+ type: "warning",
104
+ title: "Insecure resource",
105
+ message: "Resource loads over HTTP instead of HTTPS",
106
+ line: lineNum
107
+ });
108
+ }
109
+ const urlMatches = [...line.matchAll(/url\s*\(\s*["']?(http:[^"')]+)["']?\s*\)/gi)];
110
+ for (const _match of urlMatches) issues.push({
111
+ type: "warning",
112
+ title: "Insecure CSS url()",
113
+ message: "CSS url() loads over HTTP instead of HTTPS",
114
+ line: lineNum
115
+ });
116
+ }
117
+ const voidElements = new Set([
118
+ "area",
119
+ "base",
120
+ "br",
121
+ "col",
122
+ "embed",
123
+ "hr",
124
+ "img",
125
+ "input",
126
+ "link",
127
+ "meta",
128
+ "param",
129
+ "source",
130
+ "track",
131
+ "wbr"
132
+ ]);
133
+ const trackedTags = new Set([
134
+ "a",
135
+ "b",
136
+ "body",
137
+ "div",
138
+ "em",
139
+ "h1",
140
+ "h2",
141
+ "h3",
142
+ "h4",
143
+ "h5",
144
+ "h6",
145
+ "head",
146
+ "html",
147
+ "i",
148
+ "li",
149
+ "ol",
150
+ "p",
151
+ "span",
152
+ "strong",
153
+ "style",
154
+ "table",
155
+ "tbody",
156
+ "td",
157
+ "tfoot",
158
+ "th",
159
+ "thead",
160
+ "title",
161
+ "tr",
162
+ "u",
163
+ "ul"
164
+ ]);
165
+ const stack = [];
166
+ const strippedLines = html.replace(/<!--[\s\S]*?-->/g, (m) => "\n".repeat((m.match(/\n/g) || []).length)).replace(/<(style|script)\b[^>]*>[\s\S]*?<\/\1>/gi, (m) => "\n".repeat((m.match(/\n/g) || []).length)).split("\n");
167
+ for (let i = 0; i < strippedLines.length; i++) {
168
+ const line = strippedLines[i];
169
+ const tagRegex = /<\/?([a-zA-Z][a-zA-Z0-9]*)\b[^>]*\/?>/g;
170
+ let m;
171
+ while ((m = tagRegex.exec(line)) !== null) {
172
+ const fullMatch = m[0];
173
+ const tagName = m[1].toLowerCase();
174
+ if (!trackedTags.has(tagName) || voidElements.has(tagName)) continue;
175
+ if (fullMatch.endsWith("/>")) continue;
176
+ if (fullMatch.startsWith("</")) {
177
+ const lastOpen = stack.findLastIndex((s) => s.tag === tagName);
178
+ if (lastOpen !== -1) stack.splice(lastOpen, 1);
179
+ } else stack.push({
180
+ tag: tagName,
181
+ line: i + 1 + lineOffset
182
+ });
183
+ }
184
+ }
185
+ for (const unclosed of stack) issues.push({
186
+ type: "error",
187
+ title: "Unclosed tag",
188
+ message: `<${unclosed.tag}> tag is not closed`,
189
+ line: unclosed.line
190
+ });
191
+ issues.sort((a, b) => {
192
+ if (a.type !== b.type) return a.type === "error" ? -1 : 1;
193
+ return (a.line ?? 0) - (b.line ?? 0);
194
+ });
195
+ return issues;
196
+ }
197
+
198
+ //#endregion
199
+ export { serveLint };
200
+ //# sourceMappingURL=linter.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linter.mjs","names":[],"sources":["../../src/server/linter.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { glob } from 'tinyglobby'\nimport type { MaizzleConfig } from '../types/index.ts'\n\ninterface LintIssue {\n type: 'error' | 'warning'\n title: string\n message: string\n line?: number\n}\n\nexport async function serveLint(url: string, config: MaizzleConfig, res: any) {\n const templateSlug = url.replace('/__maizzle/lint/', '').replace(/\\?.*$/, '')\n\n const contentPatterns = config.content ?? ['emails/**/*.vue']\n const templates = await glob(contentPatterns)\n const match = templates.find(t => t.replace(/\\.(vue|md)$/, '') === templateSlug)\n\n if (!match) {\n res.statusCode = 404\n res.end(JSON.stringify({ error: 'Template not found' }))\n return\n }\n\n try {\n const source = readFileSync(resolve(match), 'utf-8')\n\n // Extract only the <template> block for linting\n const templateMatch = source.match(/<template\\b[^>]*>([\\s\\S]*)<\\/template>/)\n const html = templateMatch ? templateMatch[1] : source\n\n // Calculate the offset of the <template> content within the source file\n const templateOffset = templateMatch\n ? source.slice(0, source.indexOf(templateMatch[0]) + templateMatch[0].indexOf(templateMatch[1])).split('\\n').length - 1\n : 0\n\n const issues = lintHtml(html, templateOffset)\n\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify(issues))\n } catch (error: any) {\n res.statusCode = 500\n res.end(JSON.stringify({ error: error.message }))\n }\n}\n\nfunction lintHtml(html: string, lineOffset = 0): LintIssue[] {\n const issues: LintIssue[] = []\n const lines = html.split('\\n')\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]\n const lineNum = i + 1 + lineOffset\n\n // Images missing alt text\n const imgMatches = [...line.matchAll(/<img\\b[^>]*?>/gi)]\n for (const match of imgMatches) {\n const tag = match[0]\n if (!/\\balt\\s*=/i.test(tag)) {\n issues.push({\n type: 'warning',\n title: 'Missing alt text',\n message: 'Image is missing the alt attribute',\n line: lineNum,\n })\n }\n }\n\n // Images with empty or missing src\n for (const match of imgMatches) {\n const tag = match[0]\n const srcMatch = tag.match(/\\bsrc\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (!srcMatch) {\n issues.push({\n type: 'error',\n title: 'Missing image src',\n message: 'Image tag has no src attribute',\n line: lineNum,\n })\n } else if (!srcMatch[1].trim()) {\n issues.push({\n type: 'error',\n title: 'Empty image src',\n message: 'Image src attribute is empty',\n line: lineNum,\n })\n } else if (srcMatch[1].trim().startsWith('http:')) {\n issues.push({\n type: 'warning',\n title: 'Insecure image src',\n message: 'Image loads over HTTP instead of HTTPS',\n line: lineNum,\n })\n }\n }\n\n // Links: missing href, empty href, placeholder href\n const linkMatches = [...line.matchAll(/<a\\b[^>]*?>/gi)]\n for (const match of linkMatches) {\n const tag = match[0]\n const hrefMatch = tag.match(/\\bhref\\s*=\\s*[\"']([^\"']*)[\"']/i)\n\n if (!hrefMatch) {\n issues.push({\n type: 'error',\n title: 'Missing link href',\n message: 'Anchor tag has no href attribute',\n line: lineNum,\n })\n } else {\n const href = hrefMatch[1].trim()\n if (!href) {\n issues.push({\n type: 'warning',\n title: 'Empty link href',\n message: 'Link href attribute is empty',\n line: lineNum,\n })\n } else if (href === '#' || href === '/') {\n issues.push({\n type: 'warning',\n title: 'Placeholder link',\n message: `Link href is \"${href}\"`,\n line: lineNum,\n })\n } else if (href.startsWith('http:')) {\n issues.push({\n type: 'warning',\n title: 'Insecure link',\n message: 'Link uses HTTP instead of HTTPS',\n line: lineNum,\n })\n } else if (href.startsWith('http') && !/^https?:\\/\\/.+\\..+/i.test(href)) {\n issues.push({\n type: 'warning',\n title: 'Invalid link',\n message: `Link href \"${href}\" looks malformed`,\n line: lineNum,\n })\n }\n }\n }\n\n // Insecure resources (<link href>, <script src>, <source src>)\n const resourceMatches = [...line.matchAll(/<(?:link|script|source)\\b[^>]*?>/gi)]\n for (const match of resourceMatches) {\n const tag = match[0]\n const attrMatch = tag.match(/\\b(?:href|src)\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (attrMatch && attrMatch[1].trim().startsWith('http:')) {\n issues.push({\n type: 'warning',\n title: 'Insecure resource',\n message: 'Resource loads over HTTP instead of HTTPS',\n line: lineNum,\n })\n }\n }\n\n // Insecure CSS url() references\n const urlMatches = [...line.matchAll(/url\\s*\\(\\s*[\"']?(http:[^\"')]+)[\"']?\\s*\\)/gi)]\n for (const _match of urlMatches) {\n issues.push({\n type: 'warning',\n title: 'Insecure CSS url()',\n message: 'CSS url() loads over HTTP instead of HTTPS',\n line: lineNum,\n })\n }\n }\n\n // Check for unclosed tags (block-level and common inline elements)\n const voidElements = new Set([\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\n 'link', 'meta', 'param', 'source', 'track', 'wbr',\n ])\n\n const trackedTags = new Set([\n 'a', 'b', 'body', 'div', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n 'head', 'html', 'i', 'li', 'ol', 'p', 'span', 'strong', 'style',\n 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'title', 'tr', 'u', 'ul',\n ])\n\n const stack: Array<{ tag: string, line: number }> = []\n\n // Strip comments and content inside <style>/<script> to avoid false matches\n const stripped = html\n .replace(/<!--[\\s\\S]*?-->/g, (m) => '\\n'.repeat((m.match(/\\n/g) || []).length))\n .replace(/<(style|script)\\b[^>]*>[\\s\\S]*?<\\/\\1>/gi, (m) => '\\n'.repeat((m.match(/\\n/g) || []).length))\n\n const strippedLines = stripped.split('\\n')\n\n for (let i = 0; i < strippedLines.length; i++) {\n const line = strippedLines[i]\n const tagRegex = /<\\/?([a-zA-Z][a-zA-Z0-9]*)\\b[^>]*\\/?>/g\n let m\n\n while ((m = tagRegex.exec(line)) !== null) {\n const fullMatch = m[0]\n const tagName = m[1].toLowerCase()\n\n if (!trackedTags.has(tagName) || voidElements.has(tagName)) continue\n if (fullMatch.endsWith('/>')) continue\n\n if (fullMatch.startsWith('</')) {\n // Closing tag\n const lastOpen = stack.findLastIndex(s => s.tag === tagName)\n if (lastOpen !== -1) {\n stack.splice(lastOpen, 1)\n }\n } else {\n // Opening tag\n stack.push({ tag: tagName, line: i + 1 + lineOffset })\n }\n }\n }\n\n for (const unclosed of stack) {\n issues.push({\n type: 'error',\n title: 'Unclosed tag',\n message: `<${unclosed.tag}> tag is not closed`,\n line: unclosed.line,\n })\n }\n\n // Sort: errors first, then warnings, then by line\n issues.sort((a, b) => {\n if (a.type !== b.type) return a.type === 'error' ? -1 : 1\n return (a.line ?? 0) - (b.line ?? 0)\n })\n\n return issues\n}\n"],"mappings":"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"}
@@ -0,0 +1,360 @@
1
+ <script setup lang="ts">
2
+ import { ref, computed, onMounted, onUnmounted, watch, watchEffect } from 'vue'
3
+ import { RouterLink, RouterView, useRoute, useRouter } from 'vue-router'
4
+ import { Monitor, CodeXml, Smartphone, ChevronDown, ArrowUp, ArrowDown, CornerDownLeft, Check, X } from 'lucide-vue-next'
5
+ import logoUrl from '@/logo.svg'
6
+ import logoGradientUrl from '@/logo-gradient.svg'
7
+ import { Kbd } from '@/components/ui/kbd'
8
+ import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
9
+ import { Button } from '@/components/ui/button'
10
+ import {
11
+ DropdownMenu,
12
+ DropdownMenuContent,
13
+ DropdownMenuItem,
14
+ DropdownMenuTrigger,
15
+ } from '@/components/ui/dropdown-menu'
16
+ import { ScrollArea } from '@/components/ui/scroll-area'
17
+ import {
18
+ CommandDialog,
19
+ CommandEmpty,
20
+ CommandGroup,
21
+ CommandInput,
22
+ CommandItem,
23
+ CommandList,
24
+ } from '@/components/ui/command'
25
+ import {
26
+ Sidebar,
27
+ SidebarContent,
28
+ SidebarFooter,
29
+ SidebarGroup,
30
+ SidebarGroupContent,
31
+ SidebarGroupLabel,
32
+ SidebarHeader,
33
+ SidebarInset,
34
+ SidebarMenu,
35
+ SidebarMenuItem,
36
+ SidebarMenuButton,
37
+ SidebarProvider,
38
+ SidebarTrigger,
39
+ SidebarInput,
40
+ } from '@/components/ui/sidebar'
41
+
42
+
43
+ interface Template {
44
+ name: string
45
+ path: string
46
+ href: string
47
+ }
48
+
49
+ const route = useRoute()
50
+
51
+ watchEffect(() => {
52
+ const slug = route.path === '/' ? '' : route.path.split('/').pop()
53
+ document.title = slug ? `Maizzle Dev - ${slug}.vue` : 'Maizzle Dev'
54
+ })
55
+
56
+ const templates = ref<Template[]>([])
57
+ const search = ref('')
58
+ const loading = ref(true)
59
+ const viewMode = ref<'preview' | 'source'>('preview')
60
+ const sidebarOpen = ref(localStorage.getItem('maizzle:sidebar') !== 'closed')
61
+
62
+ interface DevicePreset {
63
+ name: string
64
+ width: number
65
+ height: number
66
+ }
67
+
68
+ const devicePresets: DevicePreset[] = [
69
+ { name: 'iPhone 17 Pro', width: 390, height: 844 },
70
+ { name: 'iPhone 17 Pro Max', width: 430, height: 932 },
71
+ { name: 'iPad Pro 11"', width: 834, height: 1194 },
72
+ { name: 'iPad Pro 12.9"', width: 1024, height: 1366 },
73
+ { name: 'Galaxy S26 Ultra', width: 412, height: 915 },
74
+ { name: 'Pixel 9 Pro', width: 393, height: 873 },
75
+ { name: 'Redmi Note 13 Lite', width: 360, height: 800 },
76
+ ]
77
+
78
+ const selectedDevice = ref<DevicePreset | null>(null)
79
+ const panelWidth = ref(0)
80
+ const panelHeight = ref(0)
81
+ const isDragging = ref(false)
82
+ const isFullSize = ref(true)
83
+ const resetKey = ref(0)
84
+
85
+ function selectDevice(device: DevicePreset) {
86
+ selectedDevice.value = device
87
+ viewMode.value = 'preview'
88
+ }
89
+
90
+ watch(sidebarOpen, (open) => {
91
+ localStorage.setItem('maizzle:sidebar', open ? 'open' : 'closed')
92
+ })
93
+
94
+ async function fetchTemplates() {
95
+ const res = await fetch('/__maizzle/templates')
96
+ templates.value = await res.json()
97
+ loading.value = false
98
+ }
99
+
100
+ onMounted(fetchTemplates)
101
+
102
+ if ((import.meta as any).hot) {
103
+ (import.meta as any).hot.on('maizzle:templates-changed', fetchTemplates)
104
+ }
105
+
106
+ const grouped = computed(() => {
107
+ const filtered = templates.value.filter(t =>
108
+ t.name.toLowerCase().includes(search.value.toLowerCase())
109
+ || t.path.toLowerCase().includes(search.value.toLowerCase())
110
+ )
111
+
112
+ const groups: Record<string, Template[]> = {}
113
+
114
+ for (const t of filtered) {
115
+ const parts = t.path.split('/')
116
+ const dir = parts.length > 1 ? parts.slice(0, -1).join('/') : '.'
117
+ if (!groups[dir]) groups[dir] = []
118
+ groups[dir].push(t)
119
+ }
120
+
121
+ return groups
122
+ })
123
+
124
+ const filteredCount = computed(() => {
125
+ return Object.values(grouped.value).reduce((sum, items) => sum + items.length, 0)
126
+ })
127
+
128
+ const isActive = (href: string) => route.path === href
129
+
130
+ const isPreviewRoute = computed(() => route.path !== '/')
131
+
132
+ // Command palette
133
+ const router = useRouter()
134
+ const commandOpen = ref(false)
135
+
136
+ const commandGrouped = computed(() => {
137
+ const groups: Record<string, Template[]> = {}
138
+
139
+ for (const t of templates.value) {
140
+ const parts = t.path.split('/')
141
+ const dir = parts.length > 1 ? parts.slice(0, -1).join('/') : '.'
142
+ if (!groups[dir]) groups[dir] = []
143
+ groups[dir].push(t)
144
+ }
145
+
146
+ return groups
147
+ })
148
+
149
+ function getFileName(path: string) {
150
+ return path.split('/').pop() || path
151
+ }
152
+
153
+ function onCommandSelect(href: string) {
154
+ commandOpen.value = false
155
+ router.push(href)
156
+ }
157
+
158
+ function onKeydown(e: KeyboardEvent) {
159
+ if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
160
+ e.preventDefault()
161
+ commandOpen.value = !commandOpen.value
162
+ return
163
+ }
164
+
165
+ if ((e.metaKey || e.ctrlKey) && e.key === 'b') {
166
+ e.preventDefault()
167
+ sidebarOpen.value = !sidebarOpen.value
168
+ return
169
+ }
170
+
171
+ if (e.key === '/' && !isInputFocused()) {
172
+ e.preventDefault()
173
+ commandOpen.value = true
174
+ }
175
+ }
176
+
177
+ function isInputFocused() {
178
+ const el = document.activeElement
179
+ if (!el) return false
180
+ const tag = el.tagName.toLowerCase()
181
+ return tag === 'input' || tag === 'textarea' || (el as HTMLElement).isContentEditable
182
+ }
183
+
184
+ onMounted(() => document.addEventListener('keydown', onKeydown))
185
+ onUnmounted(() => document.removeEventListener('keydown', onKeydown))
186
+ </script>
187
+
188
+ <template>
189
+ <SidebarProvider v-model:open="sidebarOpen">
190
+ <Sidebar collapsible="offcanvas" class="border-r border-gray-200 dark:border-gray-800">
191
+ <SidebarHeader class="h-12 flex-row items-center justify-between border-b border-gray-200 dark:border-gray-800 px-4">
192
+ <RouterLink to="/" class="flex items-center gap-2">
193
+ <img :src="logoUrl" alt="Maizzle" class="h-4 dark:hidden">
194
+ <img :src="logoGradientUrl" alt="Maizzle" class="hidden h-4 dark:block">
195
+ </RouterLink>
196
+ <SidebarTrigger class="-mr-1" />
197
+ </SidebarHeader>
198
+
199
+ <div class="px-3 pt-3 pb-1">
200
+ <div class="relative flex items-center">
201
+ <SidebarInput
202
+ v-model="search"
203
+ placeholder="Search emails..."
204
+ class="text-xs! pr-7"
205
+ @keydown.esc="search && (search = '')"
206
+ />
207
+ <button
208
+ v-if="search"
209
+ class="absolute right-2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
210
+ @click="search = ''"
211
+ >
212
+ <X class="size-3.5" />
213
+ </button>
214
+ </div>
215
+ </div>
216
+
217
+ <SidebarContent>
218
+ <ScrollArea class="flex-1">
219
+ <SidebarGroup v-if="loading">
220
+ <p class="px-2 py-4 text-xs text-gray-500 dark:text-gray-400">Loading emails...</p>
221
+ </SidebarGroup>
222
+
223
+ <SidebarGroup v-else-if="filteredCount === 0">
224
+ <p class="px-2 py-4 text-xs text-gray-500 dark:text-gray-400">No emails found.</p>
225
+ </SidebarGroup>
226
+
227
+ <SidebarGroup v-for="(items, dir) in grouped" :key="dir" v-else>
228
+ <SidebarGroupLabel>{{ dir }}</SidebarGroupLabel>
229
+ <SidebarGroupContent>
230
+ <SidebarMenu>
231
+ <SidebarMenuItem v-for="t in items" :key="t.path">
232
+ <SidebarMenuButton
233
+ as-child
234
+ size="sm"
235
+ :is-active="isActive(t.href)"
236
+ >
237
+ <RouterLink :to="t.href" class="truncate">
238
+ <svg class="size-3 shrink-0 opacity-50" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
239
+ <path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" />
240
+ <path d="M14 2v4a2 2 0 0 0 2 2h4" />
241
+ </svg>
242
+ <span class="truncate">{{ t.name }}</span>
243
+ </RouterLink>
244
+ </SidebarMenuButton>
245
+ </SidebarMenuItem>
246
+ </SidebarMenu>
247
+ </SidebarGroupContent>
248
+ </SidebarGroup>
249
+ </ScrollArea>
250
+ </SidebarContent>
251
+
252
+ <SidebarFooter class="h-10 justify-center border-t border-gray-200 dark:border-gray-800">
253
+ <p class="text-[10px] text-gray-500 dark:text-gray-400">{{ templates.length }} email{{ templates.length !== 1 ? 's' : '' }}</p>
254
+ </SidebarFooter>
255
+ </Sidebar>
256
+
257
+ <SidebarInset>
258
+ <!-- Header toolbar -->
259
+ <header class="grid h-12 grid-cols-[1fr_auto_1fr] items-center border-b px-4">
260
+ <div>
261
+ <Transition
262
+ enter-from-class="opacity-0"
263
+ enter-active-class="transition-opacity duration-150 delay-200"
264
+ leave-active-class="transition-opacity duration-0"
265
+ leave-to-class="opacity-0"
266
+ >
267
+ <SidebarTrigger v-show="!sidebarOpen" />
268
+ </Transition>
269
+ </div>
270
+
271
+ <!-- View mode toggles (centered) -->
272
+ <ToggleGroup v-if="isPreviewRoute" v-model="viewMode" type="single" variant="outline" size="sm">
273
+ <ToggleGroupItem value="preview">
274
+ <Monitor class="size-4" />
275
+ </ToggleGroupItem>
276
+ <ToggleGroupItem value="source">
277
+ <CodeXml class="size-4" />
278
+ </ToggleGroupItem>
279
+ </ToggleGroup>
280
+ <div v-else />
281
+
282
+ <div class="flex items-center justify-end gap-3">
283
+ <span
284
+ v-if="isPreviewRoute && (!isFullSize || selectedDevice) && panelWidth"
285
+ class="text-xs font-medium tabular-nums text-gray-500 dark:text-gray-400 select-none"
286
+ >
287
+ {{ panelWidth }} &times; {{ panelHeight }}
288
+ </span>
289
+ <DropdownMenu v-if="isPreviewRoute">
290
+ <DropdownMenuTrigger as-child>
291
+ <Button variant="outline" size="sm" class="gap-1.5">
292
+ <Smartphone class="size-4" />
293
+ <span v-if="selectedDevice" class="text-xs">{{ selectedDevice.name }}</span>
294
+ <ChevronDown class="size-3 opacity-50" />
295
+ </Button>
296
+ </DropdownMenuTrigger>
297
+ <DropdownMenuContent align="end">
298
+ <DropdownMenuItem @click="selectedDevice = null; viewMode = 'preview'; resetKey++">
299
+ <Check v-if="!selectedDevice" class="size-3.5" />
300
+ <span :class="!selectedDevice ? '' : 'pl-5.5'">Responsive</span>
301
+ </DropdownMenuItem>
302
+ <DropdownMenuItem
303
+ v-for="device in devicePresets"
304
+ :key="device.name"
305
+ @click="selectDevice(device)"
306
+ >
307
+ <Check v-if="selectedDevice?.name === device.name" class="size-3.5" />
308
+ <span :class="selectedDevice?.name === device.name ? '' : 'pl-5.5'">{{ device.name }}</span>
309
+ <span class="ml-auto text-xs text-gray-500 dark:text-gray-400 tabular-nums">{{ device.width }}&times;{{ device.height }}</span>
310
+ </DropdownMenuItem>
311
+ </DropdownMenuContent>
312
+ </DropdownMenu>
313
+ </div>
314
+ </header>
315
+
316
+ <!-- Main content -->
317
+ <div class="flex-1 overflow-hidden">
318
+ <RouterView v-slot="{ Component }">
319
+ <component :is="Component" v-model:view-mode="viewMode" :device="selectedDevice" :reset-key="resetKey" v-model:panel-width="panelWidth" v-model:panel-height="panelHeight" v-model:is-dragging="isDragging" v-model:is-full-size="isFullSize" @clear-device="selectedDevice = null" />
320
+ </RouterView>
321
+ </div>
322
+ </SidebarInset>
323
+
324
+ <CommandDialog v-model:open="commandOpen" title="Search emails" description="Search and navigate to an email">
325
+ <CommandInput placeholder="Search emails..." />
326
+ <CommandList>
327
+ <CommandEmpty>No emails found.</CommandEmpty>
328
+ <CommandGroup v-for="(items, dir) in commandGrouped" :key="dir" :heading="String(dir)">
329
+ <CommandItem
330
+ v-for="t in items"
331
+ :key="t.path"
332
+ :value="t.path"
333
+ @select="onCommandSelect(t.href)"
334
+ >
335
+ <svg class="size-3 shrink-0 opacity-50" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
336
+ <path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" />
337
+ <path d="M14 2v4a2 2 0 0 0 2 2h4" />
338
+ </svg>
339
+ <span>{{ getFileName(t.path) }}</span>
340
+ </CommandItem>
341
+ </CommandGroup>
342
+ </CommandList>
343
+ <div class="flex items-center gap-4 border-t px-3 py-2 text-xs text-gray-500 dark:text-gray-400">
344
+ <span class="inline-flex items-center gap-1">
345
+ <Kbd><ArrowUp class="size-3" /></Kbd>
346
+ <Kbd><ArrowDown class="size-3" /></Kbd>
347
+ Navigate
348
+ </span>
349
+ <span class="inline-flex items-center gap-1">
350
+ <Kbd><CornerDownLeft class="size-3" /></Kbd>
351
+ Open
352
+ </span>
353
+ <span class="inline-flex items-center gap-1">
354
+ <Kbd>Esc</Kbd>
355
+ Close
356
+ </span>
357
+ </div>
358
+ </CommandDialog>
359
+ </SidebarProvider>
360
+ </template>
@@ -0,0 +1,31 @@
1
+ <script setup lang="ts">
2
+ import type { PrimitiveProps } from "reka-ui"
3
+ import type { HTMLAttributes } from "vue"
4
+ import type { ButtonVariants } from "."
5
+ import { Primitive } from "reka-ui"
6
+ import { cn } from "@/lib/utils"
7
+ import { buttonVariants } from "."
8
+
9
+ interface Props extends PrimitiveProps {
10
+ variant?: ButtonVariants["variant"]
11
+ size?: ButtonVariants["size"]
12
+ class?: HTMLAttributes["class"]
13
+ }
14
+
15
+ const props = withDefaults(defineProps<Props>(), {
16
+ as: "button",
17
+ })
18
+ </script>
19
+
20
+ <template>
21
+ <Primitive
22
+ data-slot="button"
23
+ :data-variant="variant"
24
+ :data-size="size"
25
+ :as="as"
26
+ :as-child="asChild"
27
+ :class="cn(buttonVariants({ variant, size }), props.class)"
28
+ >
29
+ <slot />
30
+ </Primitive>
31
+ </template>
@@ -0,0 +1,38 @@
1
+ import type { VariantProps } from "class-variance-authority"
2
+ import { cva } from "class-variance-authority"
3
+
4
+ export { default as Button } from "./Button.vue"
5
+
6
+ export const buttonVariants = cva(
7
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-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",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default:
12
+ "bg-primary text-primary-foreground hover:bg-primary/90",
13
+ destructive:
14
+ "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
15
+ outline:
16
+ "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
17
+ secondary:
18
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80",
19
+ ghost:
20
+ "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
21
+ link: "text-primary underline-offset-4 hover:underline",
22
+ },
23
+ size: {
24
+ "default": "h-9 px-4 py-2 has-[>svg]:px-3",
25
+ "sm": "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
26
+ "lg": "h-10 rounded-md px-6 has-[>svg]:px-4",
27
+ "icon": "size-9",
28
+ "icon-sm": "size-8",
29
+ "icon-lg": "size-10",
30
+ },
31
+ },
32
+ defaultVariants: {
33
+ variant: "default",
34
+ size: "default",
35
+ },
36
+ },
37
+ )
38
+ export type ButtonVariants = VariantProps<typeof buttonVariants>