@maizzle/framework 6.0.0-9 → 6.0.0-rc.1
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.
- package/bin/maizzle.mjs +10 -0
- package/dist/build.d.mts +19 -0
- package/dist/build.d.mts.map +1 -0
- package/dist/build.mjs +138 -0
- package/dist/build.mjs.map +1 -0
- package/dist/components/Button.vue +141 -0
- package/dist/components/Divider.vue +105 -0
- package/dist/components/NoWidows.vue +123 -0
- package/dist/components/NotOutlook.vue +17 -0
- package/dist/components/Outlook.vue +74 -0
- package/dist/components/Spacer.vue +36 -0
- package/dist/components/Vml.vue +89 -0
- package/dist/components/WithUrl.vue +190 -0
- package/dist/components/utils.d.mts +5 -0
- package/dist/components/utils.d.mts.map +1 -0
- package/dist/components/utils.mjs +9 -0
- package/dist/components/utils.mjs.map +1 -0
- package/dist/components/utils.ts +6 -0
- package/dist/composables/defineConfig.d.mts +14 -0
- package/dist/composables/defineConfig.d.mts.map +1 -0
- package/dist/composables/defineConfig.mjs +34 -0
- package/dist/composables/defineConfig.mjs.map +1 -0
- package/dist/composables/renderContext.d.mts +19 -0
- package/dist/composables/renderContext.d.mts.map +1 -0
- package/dist/composables/renderContext.mjs +6 -0
- package/dist/composables/renderContext.mjs.map +1 -0
- package/dist/composables/useConfig.d.mts +9 -0
- package/dist/composables/useConfig.d.mts.map +1 -0
- package/dist/composables/useConfig.mjs +13 -0
- package/dist/composables/useConfig.mjs.map +1 -0
- package/dist/composables/useDoctype.d.mts +13 -0
- package/dist/composables/useDoctype.d.mts.map +1 -0
- package/dist/composables/useDoctype.mjs +20 -0
- package/dist/composables/useDoctype.mjs.map +1 -0
- package/dist/composables/useEvent.d.mts +17 -0
- package/dist/composables/useEvent.d.mts.map +1 -0
- package/dist/composables/useEvent.mjs +25 -0
- package/dist/composables/useEvent.mjs.map +1 -0
- package/dist/composables/usePlaintext.d.mts +19 -0
- package/dist/composables/usePlaintext.d.mts.map +1 -0
- package/dist/composables/usePlaintext.mjs +22 -0
- package/dist/composables/usePlaintext.mjs.map +1 -0
- package/dist/config/defaults.d.mts +6 -0
- package/dist/config/defaults.d.mts.map +1 -0
- package/dist/config/defaults.mjs +28 -0
- package/dist/config/defaults.mjs.map +1 -0
- package/dist/config/index.d.mts +15 -0
- package/dist/config/index.d.mts.map +1 -0
- package/dist/config/index.mjs +64 -0
- package/dist/config/index.mjs.map +1 -0
- package/dist/events/index.d.mts +91 -0
- package/dist/events/index.d.mts.map +1 -0
- package/dist/events/index.mjs +110 -0
- package/dist/events/index.mjs.map +1 -0
- package/dist/index.d.mts +29 -0
- package/dist/index.mjs +29 -0
- package/dist/plaintext.d.mts +5 -0
- package/dist/plaintext.d.mts.map +1 -0
- package/dist/plaintext.mjs +15 -0
- package/dist/plaintext.mjs.map +1 -0
- package/dist/plugin.d.mts +17 -0
- package/dist/plugin.d.mts.map +1 -0
- package/dist/plugin.mjs +41 -0
- package/dist/plugin.mjs.map +1 -0
- package/dist/plugins/postcss/mergeMediaQueries.d.mts +18 -0
- package/dist/plugins/postcss/mergeMediaQueries.d.mts.map +1 -0
- package/dist/plugins/postcss/mergeMediaQueries.mjs +22 -0
- package/dist/plugins/postcss/mergeMediaQueries.mjs.map +1 -0
- package/dist/plugins/postcss/pruneVars.d.mts +8 -0
- package/dist/plugins/postcss/pruneVars.d.mts.map +1 -0
- package/dist/plugins/postcss/pruneVars.mjs +49 -0
- package/dist/plugins/postcss/pruneVars.mjs.map +1 -0
- package/dist/plugins/postcss/removeDeclarations.d.mts +12 -0
- package/dist/plugins/postcss/removeDeclarations.d.mts.map +1 -0
- package/dist/plugins/postcss/removeDeclarations.mjs +45 -0
- package/dist/plugins/postcss/removeDeclarations.mjs.map +1 -0
- package/dist/plugins/postcss/tailwindCleanup.d.mts +18 -0
- package/dist/plugins/postcss/tailwindCleanup.d.mts.map +1 -0
- package/dist/plugins/postcss/tailwindCleanup.mjs +35 -0
- package/dist/plugins/postcss/tailwindCleanup.mjs.map +1 -0
- package/dist/render/createRenderer.d.mts +36 -0
- package/dist/render/createRenderer.d.mts.map +1 -0
- package/dist/render/createRenderer.mjs +150 -0
- package/dist/render/createRenderer.mjs.map +1 -0
- package/dist/render/index.d.mts +26 -0
- package/dist/render/index.d.mts.map +1 -0
- package/dist/render/index.mjs +43 -0
- package/dist/render/index.mjs.map +1 -0
- package/dist/serve.d.mts +25 -0
- package/dist/serve.d.mts.map +1 -0
- package/dist/serve.mjs +404 -0
- package/dist/serve.mjs.map +1 -0
- package/dist/server/compatibility.d.mts +6 -0
- package/dist/server/compatibility.d.mts.map +1 -0
- package/dist/server/compatibility.mjs +83 -0
- package/dist/server/compatibility.mjs.map +1 -0
- package/dist/server/linter.d.mts +6 -0
- package/dist/server/linter.d.mts.map +1 -0
- package/dist/server/linter.mjs +200 -0
- package/dist/server/linter.mjs.map +1 -0
- package/dist/server/ui/App.vue +360 -0
- package/dist/server/ui/components/ui/button/Button.vue +31 -0
- package/dist/server/ui/components/ui/button/index.ts +38 -0
- package/dist/server/ui/components/ui/command/Command.vue +87 -0
- package/dist/server/ui/components/ui/command/CommandDialog.vue +31 -0
- package/dist/server/ui/components/ui/command/CommandEmpty.vue +27 -0
- package/dist/server/ui/components/ui/command/CommandGroup.vue +45 -0
- package/dist/server/ui/components/ui/command/CommandInput.vue +39 -0
- package/dist/server/ui/components/ui/command/CommandItem.vue +76 -0
- package/dist/server/ui/components/ui/command/CommandList.vue +25 -0
- package/dist/server/ui/components/ui/command/CommandSeparator.vue +21 -0
- package/dist/server/ui/components/ui/command/CommandShortcut.vue +17 -0
- package/dist/server/ui/components/ui/command/index.ts +25 -0
- package/dist/server/ui/components/ui/dialog/Dialog.vue +19 -0
- package/dist/server/ui/components/ui/dialog/DialogClose.vue +15 -0
- package/dist/server/ui/components/ui/dialog/DialogContent.vue +53 -0
- package/dist/server/ui/components/ui/dialog/DialogDescription.vue +23 -0
- package/dist/server/ui/components/ui/dialog/DialogFooter.vue +27 -0
- package/dist/server/ui/components/ui/dialog/DialogHeader.vue +17 -0
- package/dist/server/ui/components/ui/dialog/DialogOverlay.vue +21 -0
- package/dist/server/ui/components/ui/dialog/DialogScrollContent.vue +59 -0
- package/dist/server/ui/components/ui/dialog/DialogTitle.vue +23 -0
- package/dist/server/ui/components/ui/dialog/DialogTrigger.vue +15 -0
- package/dist/server/ui/components/ui/dialog/index.ts +10 -0
- package/dist/server/ui/components/ui/dropdown-menu/DropdownMenu.vue +19 -0
- package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +39 -0
- package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuContent.vue +39 -0
- package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuGroup.vue +15 -0
- package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuItem.vue +31 -0
- package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuLabel.vue +23 -0
- package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +21 -0
- package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +40 -0
- package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuSeparator.vue +23 -0
- package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuShortcut.vue +17 -0
- package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuSub.vue +18 -0
- package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuSubContent.vue +27 -0
- package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +31 -0
- package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuTrigger.vue +17 -0
- package/dist/server/ui/components/ui/dropdown-menu/index.ts +16 -0
- package/dist/server/ui/components/ui/empty/Empty.vue +20 -0
- package/dist/server/ui/components/ui/empty/EmptyContent.vue +20 -0
- package/dist/server/ui/components/ui/empty/EmptyDescription.vue +20 -0
- package/dist/server/ui/components/ui/empty/EmptyHeader.vue +20 -0
- package/dist/server/ui/components/ui/empty/EmptyMedia.vue +21 -0
- package/dist/server/ui/components/ui/empty/EmptyTitle.vue +17 -0
- package/dist/server/ui/components/ui/empty/index.ts +26 -0
- package/dist/server/ui/components/ui/input/Input.vue +33 -0
- package/dist/server/ui/components/ui/input/index.ts +1 -0
- package/dist/server/ui/components/ui/kbd/Kbd.vue +20 -0
- package/dist/server/ui/components/ui/kbd/KbdGroup.vue +17 -0
- package/dist/server/ui/components/ui/kbd/index.ts +2 -0
- package/dist/server/ui/components/ui/resizable/ResizableHandle.vue +30 -0
- package/dist/server/ui/components/ui/resizable/ResizablePanel.vue +21 -0
- package/dist/server/ui/components/ui/resizable/ResizablePanelGroup.vue +25 -0
- package/dist/server/ui/components/ui/resizable/index.ts +3 -0
- package/dist/server/ui/components/ui/scroll-area/ScrollArea.vue +33 -0
- package/dist/server/ui/components/ui/scroll-area/ScrollBar.vue +32 -0
- package/dist/server/ui/components/ui/scroll-area/index.ts +2 -0
- package/dist/server/ui/components/ui/separator/Separator.vue +29 -0
- package/dist/server/ui/components/ui/separator/index.ts +1 -0
- package/dist/server/ui/components/ui/sheet/Sheet.vue +19 -0
- package/dist/server/ui/components/ui/sheet/SheetClose.vue +15 -0
- package/dist/server/ui/components/ui/sheet/SheetContent.vue +62 -0
- package/dist/server/ui/components/ui/sheet/SheetDescription.vue +21 -0
- package/dist/server/ui/components/ui/sheet/SheetFooter.vue +16 -0
- package/dist/server/ui/components/ui/sheet/SheetHeader.vue +15 -0
- package/dist/server/ui/components/ui/sheet/SheetOverlay.vue +21 -0
- package/dist/server/ui/components/ui/sheet/SheetTitle.vue +21 -0
- package/dist/server/ui/components/ui/sheet/SheetTrigger.vue +15 -0
- package/dist/server/ui/components/ui/sheet/index.ts +8 -0
- package/dist/server/ui/components/ui/sidebar/Sidebar.vue +96 -0
- package/dist/server/ui/components/ui/sidebar/SidebarContent.vue +18 -0
- package/dist/server/ui/components/ui/sidebar/SidebarFooter.vue +18 -0
- package/dist/server/ui/components/ui/sidebar/SidebarGroup.vue +18 -0
- package/dist/server/ui/components/ui/sidebar/SidebarGroupAction.vue +27 -0
- package/dist/server/ui/components/ui/sidebar/SidebarGroupContent.vue +18 -0
- package/dist/server/ui/components/ui/sidebar/SidebarGroupLabel.vue +25 -0
- package/dist/server/ui/components/ui/sidebar/SidebarHeader.vue +18 -0
- package/dist/server/ui/components/ui/sidebar/SidebarInput.vue +31 -0
- package/dist/server/ui/components/ui/sidebar/SidebarInset.vue +21 -0
- package/dist/server/ui/components/ui/sidebar/SidebarMenu.vue +18 -0
- package/dist/server/ui/components/ui/sidebar/SidebarMenuAction.vue +35 -0
- package/dist/server/ui/components/ui/sidebar/SidebarMenuBadge.vue +26 -0
- package/dist/server/ui/components/ui/sidebar/SidebarMenuButton.vue +48 -0
- package/dist/server/ui/components/ui/sidebar/SidebarMenuButtonChild.vue +36 -0
- package/dist/server/ui/components/ui/sidebar/SidebarMenuItem.vue +18 -0
- package/dist/server/ui/components/ui/sidebar/SidebarMenuSkeleton.vue +35 -0
- package/dist/server/ui/components/ui/sidebar/SidebarMenuSub.vue +22 -0
- package/dist/server/ui/components/ui/sidebar/SidebarMenuSubButton.vue +36 -0
- package/dist/server/ui/components/ui/sidebar/SidebarMenuSubItem.vue +18 -0
- package/dist/server/ui/components/ui/sidebar/SidebarProvider.vue +82 -0
- package/dist/server/ui/components/ui/sidebar/SidebarRail.vue +33 -0
- package/dist/server/ui/components/ui/sidebar/SidebarSeparator.vue +19 -0
- package/dist/server/ui/components/ui/sidebar/SidebarTrigger.vue +28 -0
- package/dist/server/ui/components/ui/sidebar/index.ts +60 -0
- package/dist/server/ui/components/ui/sidebar/utils.ts +19 -0
- package/dist/server/ui/components/ui/skeleton/Skeleton.vue +17 -0
- package/dist/server/ui/components/ui/skeleton/index.ts +1 -0
- package/dist/server/ui/components/ui/tabs/Tabs.vue +24 -0
- package/dist/server/ui/components/ui/tabs/TabsContent.vue +21 -0
- package/dist/server/ui/components/ui/tabs/TabsList.vue +24 -0
- package/dist/server/ui/components/ui/tabs/TabsTrigger.vue +26 -0
- package/dist/server/ui/components/ui/tabs/index.ts +4 -0
- package/dist/server/ui/components/ui/toggle/Toggle.vue +35 -0
- package/dist/server/ui/components/ui/toggle/index.ts +28 -0
- package/dist/server/ui/components/ui/toggle-group/ToggleGroup.vue +49 -0
- package/dist/server/ui/components/ui/toggle-group/ToggleGroupItem.vue +46 -0
- package/dist/server/ui/components/ui/toggle-group/index.ts +2 -0
- package/dist/server/ui/components/ui/tooltip/Tooltip.vue +19 -0
- package/dist/server/ui/components/ui/tooltip/TooltipContent.vue +34 -0
- package/dist/server/ui/components/ui/tooltip/TooltipProvider.vue +14 -0
- package/dist/server/ui/components/ui/tooltip/TooltipTrigger.vue +15 -0
- package/dist/server/ui/components/ui/tooltip/index.ts +4 -0
- package/dist/server/ui/favicon.svg +1 -0
- package/dist/server/ui/index.html +13 -0
- package/dist/server/ui/lib/utils.ts +7 -0
- package/dist/server/ui/logo-gradient.svg +1 -0
- package/dist/server/ui/logo.svg +1 -0
- package/dist/server/ui/main.css +129 -0
- package/dist/server/ui/main.ts +16 -0
- package/dist/server/ui/mark-gradient.svg +1 -0
- package/dist/server/ui/mark.svg +1 -0
- package/dist/server/ui/pages/Home.vue +39 -0
- package/dist/server/ui/pages/Preview.vue +609 -0
- package/dist/server/ui/stripes.svg +174 -0
- package/dist/transformers/addAttributes.d.mts +32 -0
- package/dist/transformers/addAttributes.d.mts.map +1 -0
- package/dist/transformers/addAttributes.mjs +101 -0
- package/dist/transformers/addAttributes.mjs.map +1 -0
- package/dist/transformers/attributeToStyle.d.mts +25 -0
- package/dist/transformers/attributeToStyle.d.mts.map +1 -0
- package/dist/transformers/attributeToStyle.mjs +80 -0
- package/dist/transformers/attributeToStyle.mjs.map +1 -0
- package/dist/transformers/base.d.mts +8 -0
- package/dist/transformers/base.d.mts.map +1 -0
- package/dist/transformers/base.mjs +160 -0
- package/dist/transformers/base.mjs.map +1 -0
- package/dist/transformers/entities.d.mts +8 -0
- package/dist/transformers/entities.d.mts.map +1 -0
- package/dist/transformers/entities.mjs +38 -0
- package/dist/transformers/entities.mjs.map +1 -0
- package/dist/transformers/format.d.mts +15 -0
- package/dist/transformers/format.d.mts.map +1 -0
- package/dist/transformers/format.mjs +26 -0
- package/dist/transformers/format.mjs.map +1 -0
- package/dist/transformers/index.d.mts +35 -0
- package/dist/transformers/index.d.mts.map +1 -0
- package/dist/transformers/index.mjs +73 -0
- package/dist/transformers/index.mjs.map +1 -0
- package/dist/transformers/inlineCSS.d.mts +30 -0
- package/dist/transformers/inlineCSS.d.mts.map +1 -0
- package/dist/transformers/inlineCSS.mjs +79 -0
- package/dist/transformers/inlineCSS.mjs.map +1 -0
- package/dist/transformers/inlineLink.d.mts +14 -0
- package/dist/transformers/inlineLink.d.mts.map +1 -0
- package/dist/transformers/inlineLink.mjs +76 -0
- package/dist/transformers/inlineLink.mjs.map +1 -0
- package/dist/transformers/minify.d.mts +17 -0
- package/dist/transformers/minify.d.mts.map +1 -0
- package/dist/transformers/minify.mjs +24 -0
- package/dist/transformers/minify.mjs.map +1 -0
- package/dist/transformers/purgeCSS.d.mts +23 -0
- package/dist/transformers/purgeCSS.d.mts.map +1 -0
- package/dist/transformers/purgeCSS.mjs +66 -0
- package/dist/transformers/purgeCSS.mjs.map +1 -0
- package/dist/transformers/removeAttributes.d.mts +31 -0
- package/dist/transformers/removeAttributes.d.mts.map +1 -0
- package/dist/transformers/removeAttributes.mjs +63 -0
- package/dist/transformers/removeAttributes.mjs.map +1 -0
- package/dist/transformers/replaceStrings.d.mts +16 -0
- package/dist/transformers/replaceStrings.d.mts.map +1 -0
- package/dist/transformers/replaceStrings.mjs +19 -0
- package/dist/transformers/replaceStrings.mjs.map +1 -0
- package/dist/transformers/safeClassNames.d.mts +22 -0
- package/dist/transformers/safeClassNames.d.mts.map +1 -0
- package/dist/transformers/safeClassNames.mjs +103 -0
- package/dist/transformers/safeClassNames.mjs.map +1 -0
- package/dist/transformers/shorthandCSS.d.mts +24 -0
- package/dist/transformers/shorthandCSS.d.mts.map +1 -0
- package/dist/transformers/shorthandCSS.mjs +48 -0
- package/dist/transformers/shorthandCSS.mjs.map +1 -0
- package/dist/transformers/tailwindcss.d.mts +20 -0
- package/dist/transformers/tailwindcss.d.mts.map +1 -0
- package/dist/transformers/tailwindcss.mjs +136 -0
- package/dist/transformers/tailwindcss.mjs.map +1 -0
- package/dist/transformers/urlQuery.d.mts +24 -0
- package/dist/transformers/urlQuery.d.mts.map +1 -0
- package/dist/transformers/urlQuery.mjs +65 -0
- package/dist/transformers/urlQuery.mjs.map +1 -0
- package/dist/types/config.d.mts +146 -0
- package/dist/types/config.d.mts.map +1 -0
- package/dist/types/config.mjs +1 -0
- package/dist/types/index.d.mts +2 -0
- package/dist/types/index.mjs +1 -0
- package/dist/utils/ast/index.d.mts +4 -0
- package/dist/utils/ast/index.mjs +5 -0
- package/dist/utils/ast/parser.d.mts +7 -0
- package/dist/utils/ast/parser.d.mts.map +1 -0
- package/dist/utils/ast/parser.mjs +15 -0
- package/dist/utils/ast/parser.mjs.map +1 -0
- package/dist/utils/ast/serializer.d.mts +7 -0
- package/dist/utils/ast/serializer.d.mts.map +1 -0
- package/dist/utils/ast/serializer.mjs +13 -0
- package/dist/utils/ast/serializer.mjs.map +1 -0
- package/dist/utils/ast/walker.d.mts +7 -0
- package/dist/utils/ast/walker.d.mts.map +1 -0
- package/dist/utils/ast/walker.mjs +12 -0
- package/dist/utils/ast/walker.mjs.map +1 -0
- package/dist/utils/url.d.mts +8 -0
- package/dist/utils/url.d.mts.map +1 -0
- package/dist/utils/url.mjs +32 -0
- package/dist/utils/url.mjs.map +1 -0
- package/node_modules/@clack/core/CHANGELOG.md +257 -0
- package/node_modules/@clack/core/LICENSE +9 -0
- package/node_modules/@clack/core/README.md +22 -0
- package/node_modules/@clack/core/dist/index.cjs +15 -0
- package/node_modules/@clack/core/dist/index.cjs.map +1 -0
- package/node_modules/@clack/core/dist/index.d.cts +211 -0
- package/node_modules/@clack/core/dist/index.d.mts +211 -0
- package/node_modules/@clack/core/dist/index.d.ts +211 -0
- package/node_modules/@clack/core/dist/index.mjs +15 -0
- package/node_modules/@clack/core/dist/index.mjs.map +1 -0
- package/node_modules/@clack/core/package.json +62 -0
- package/node_modules/@clack/prompts/CHANGELOG.md +412 -0
- package/node_modules/@clack/prompts/LICENSE +9 -0
- package/node_modules/@clack/prompts/README.md +207 -0
- package/node_modules/@clack/prompts/dist/index.cjs +87 -0
- package/node_modules/@clack/prompts/dist/index.cjs.map +1 -0
- package/node_modules/@clack/prompts/dist/index.d.cts +165 -0
- package/node_modules/@clack/prompts/dist/index.d.mts +165 -0
- package/node_modules/@clack/prompts/dist/index.d.ts +165 -0
- package/node_modules/@clack/prompts/dist/index.mjs +87 -0
- package/node_modules/@clack/prompts/dist/index.mjs.map +1 -0
- package/node_modules/@clack/prompts/package.json +61 -0
- package/node_modules/@maizzle/cli/LICENSE +21 -0
- package/node_modules/@maizzle/cli/README.md +58 -0
- package/node_modules/@maizzle/cli/dist/commands/make/component.d.mts +4 -0
- package/node_modules/@maizzle/cli/dist/commands/make/component.mjs +31 -0
- package/node_modules/@maizzle/cli/dist/commands/make/config.d.mts +4 -0
- package/node_modules/@maizzle/cli/dist/commands/make/config.mjs +21 -0
- package/node_modules/@maizzle/cli/dist/commands/make/layout.d.mts +4 -0
- package/node_modules/@maizzle/cli/dist/commands/make/layout.mjs +31 -0
- package/node_modules/@maizzle/cli/dist/commands/make/scaffold.d.mts +5 -0
- package/node_modules/@maizzle/cli/dist/commands/make/scaffold.mjs +28 -0
- package/node_modules/@maizzle/cli/dist/commands/make/stubs/component.vue +9 -0
- package/node_modules/@maizzle/cli/dist/commands/make/stubs/config.ts +9 -0
- package/node_modules/@maizzle/cli/dist/commands/make/stubs/layout.vue +39 -0
- package/node_modules/@maizzle/cli/dist/commands/make/stubs/template.vue +8 -0
- package/node_modules/@maizzle/cli/dist/commands/make/template.d.mts +4 -0
- package/node_modules/@maizzle/cli/dist/commands/make/template.mjs +31 -0
- package/node_modules/@maizzle/cli/dist/index.d.mts +8 -0
- package/node_modules/@maizzle/cli/dist/index.mjs +42 -0
- package/node_modules/@maizzle/cli/package.json +59 -0
- package/node_modules/citty/LICENSE +36 -0
- package/node_modules/citty/README.md +134 -0
- package/node_modules/citty/dist/index.cjs +475 -0
- package/node_modules/citty/dist/index.d.cts +80 -0
- package/node_modules/citty/dist/index.d.mts +80 -0
- package/node_modules/citty/dist/index.d.ts +80 -0
- package/node_modules/citty/dist/index.mjs +463 -0
- package/node_modules/citty/package.json +49 -0
- package/node_modules/commander/LICENSE +22 -0
- package/node_modules/commander/Readme.md +1149 -0
- package/node_modules/commander/esm.mjs +16 -0
- package/node_modules/commander/index.js +24 -0
- package/node_modules/commander/lib/argument.js +149 -0
- package/node_modules/commander/lib/command.js +2662 -0
- package/node_modules/commander/lib/error.js +39 -0
- package/node_modules/commander/lib/help.js +709 -0
- package/node_modules/commander/lib/option.js +367 -0
- package/node_modules/commander/lib/suggestSimilar.js +101 -0
- package/node_modules/commander/package-support.json +16 -0
- package/node_modules/commander/package.json +82 -0
- package/node_modules/commander/typings/esm.d.mts +3 -0
- package/node_modules/commander/typings/index.d.ts +1045 -0
- package/node_modules/consola/LICENSE +47 -0
- package/node_modules/consola/README.md +352 -0
- package/node_modules/consola/basic.d.ts +1 -0
- package/node_modules/consola/browser.d.ts +1 -0
- package/node_modules/consola/core.d.ts +1 -0
- package/node_modules/consola/dist/basic.cjs +32 -0
- package/node_modules/consola/dist/basic.d.cts +23 -0
- package/node_modules/consola/dist/basic.d.mts +21 -0
- package/node_modules/consola/dist/basic.d.ts +23 -0
- package/node_modules/consola/dist/basic.mjs +24 -0
- package/node_modules/consola/dist/browser.cjs +84 -0
- package/node_modules/consola/dist/browser.d.cts +23 -0
- package/node_modules/consola/dist/browser.d.mts +21 -0
- package/node_modules/consola/dist/browser.d.ts +23 -0
- package/node_modules/consola/dist/browser.mjs +76 -0
- package/node_modules/consola/dist/chunks/prompt.cjs +288 -0
- package/node_modules/consola/dist/chunks/prompt.mjs +280 -0
- package/node_modules/consola/dist/core.cjs +517 -0
- package/node_modules/consola/dist/core.d.cts +459 -0
- package/node_modules/consola/dist/core.d.mts +459 -0
- package/node_modules/consola/dist/core.d.ts +459 -0
- package/node_modules/consola/dist/core.mjs +512 -0
- package/node_modules/consola/dist/index.cjs +663 -0
- package/node_modules/consola/dist/index.d.cts +24 -0
- package/node_modules/consola/dist/index.d.mts +22 -0
- package/node_modules/consola/dist/index.d.ts +24 -0
- package/node_modules/consola/dist/index.mjs +651 -0
- package/node_modules/consola/dist/shared/consola.DCGIlDNP.cjs +75 -0
- package/node_modules/consola/dist/shared/consola.DRwqZj3T.mjs +72 -0
- package/node_modules/consola/dist/shared/consola.DXBYu-KD.mjs +288 -0
- package/node_modules/consola/dist/shared/consola.DwRq1yyg.cjs +312 -0
- package/node_modules/consola/dist/utils.cjs +64 -0
- package/node_modules/consola/dist/utils.d.cts +286 -0
- package/node_modules/consola/dist/utils.d.mts +286 -0
- package/node_modules/consola/dist/utils.d.ts +286 -0
- package/node_modules/consola/dist/utils.mjs +54 -0
- package/node_modules/consola/lib/index.cjs +10 -0
- package/node_modules/consola/package.json +136 -0
- package/node_modules/consola/utils.d.ts +1 -0
- package/node_modules/create-maizzle/README.md +86 -0
- package/node_modules/create-maizzle/bin/create-maizzle.mjs +4 -0
- package/node_modules/create-maizzle/node_modules/@clack/core/CHANGELOG.md +340 -0
- package/node_modules/create-maizzle/node_modules/@clack/core/LICENSE +9 -0
- package/node_modules/create-maizzle/node_modules/@clack/core/README.md +22 -0
- package/node_modules/create-maizzle/node_modules/@clack/core/dist/index.d.mts +349 -0
- package/node_modules/create-maizzle/node_modules/@clack/core/dist/index.mjs +11 -0
- package/node_modules/create-maizzle/node_modules/@clack/core/dist/index.mjs.map +1 -0
- package/node_modules/create-maizzle/node_modules/@clack/core/package.json +60 -0
- package/node_modules/create-maizzle/node_modules/@clack/prompts/CHANGELOG.md +576 -0
- package/node_modules/create-maizzle/node_modules/@clack/prompts/LICENSE +9 -0
- package/node_modules/create-maizzle/node_modules/@clack/prompts/README.md +270 -0
- package/node_modules/create-maizzle/node_modules/@clack/prompts/dist/index.d.mts +391 -0
- package/node_modules/create-maizzle/node_modules/@clack/prompts/dist/index.mjs +137 -0
- package/node_modules/create-maizzle/node_modules/@clack/prompts/dist/index.mjs.map +1 -0
- package/node_modules/create-maizzle/node_modules/@clack/prompts/package.json +65 -0
- package/node_modules/create-maizzle/package.json +47 -0
- package/node_modules/create-maizzle/src/index.js +242 -0
- package/node_modules/defu/LICENSE +21 -0
- package/node_modules/defu/README.md +171 -0
- package/node_modules/defu/dist/defu.cjs +77 -0
- package/node_modules/defu/dist/defu.d.cts +31 -0
- package/node_modules/defu/dist/defu.d.mts +29 -0
- package/node_modules/defu/dist/defu.d.ts +31 -0
- package/node_modules/defu/dist/defu.mjs +69 -0
- package/node_modules/defu/lib/defu.cjs +10 -0
- package/node_modules/defu/lib/defu.d.cts +12 -0
- package/node_modules/defu/package.json +48 -0
- package/node_modules/fast-string-truncated-width/dist/index.d.ts +4 -0
- package/node_modules/fast-string-truncated-width/dist/index.js +171 -0
- package/node_modules/fast-string-truncated-width/dist/types.d.ts +22 -0
- package/node_modules/fast-string-truncated-width/dist/types.js +2 -0
- package/node_modules/fast-string-truncated-width/dist/utils.d.ts +4 -0
- package/node_modules/fast-string-truncated-width/dist/utils.js +15 -0
- package/node_modules/fast-string-truncated-width/license +21 -0
- package/node_modules/fast-string-truncated-width/package.json +35 -0
- package/node_modules/fast-string-truncated-width/readme.md +60 -0
- package/node_modules/fast-string-width/dist/index.d.ts +4 -0
- package/node_modules/fast-string-width/dist/index.js +14 -0
- package/node_modules/fast-string-width/license +21 -0
- package/node_modules/fast-string-width/package.json +34 -0
- package/node_modules/fast-string-width/readme.md +45 -0
- package/node_modules/fast-wrap-ansi/LICENSE +23 -0
- package/node_modules/fast-wrap-ansi/README.md +26 -0
- package/node_modules/fast-wrap-ansi/lib/main.d.ts +6 -0
- package/node_modules/fast-wrap-ansi/lib/main.js +216 -0
- package/node_modules/fast-wrap-ansi/lib/main.js.map +1 -0
- package/node_modules/fast-wrap-ansi/package.json +51 -0
- package/node_modules/giget/LICENSE +184 -0
- package/node_modules/giget/README.md +248 -0
- package/node_modules/giget/dist/cli.mjs +112 -0
- package/node_modules/giget/dist/index.d.mts +49 -0
- package/node_modules/giget/dist/index.mjs +22 -0
- package/node_modules/giget/dist/shared/giget.OCaTp9b-.mjs +468 -0
- package/node_modules/giget/package.json +62 -0
- package/node_modules/node-fetch-native/LICENSE +114 -0
- package/node_modules/node-fetch-native/README.md +225 -0
- package/node_modules/node-fetch-native/dist/chunks/multipart-parser.cjs +2 -0
- package/node_modules/node-fetch-native/dist/chunks/multipart-parser.mjs +2 -0
- package/node_modules/node-fetch-native/dist/index.cjs +1 -0
- package/node_modules/node-fetch-native/dist/index.mjs +1 -0
- package/node_modules/node-fetch-native/dist/native.cjs +1 -0
- package/node_modules/node-fetch-native/dist/native.mjs +1 -0
- package/node_modules/node-fetch-native/dist/node.cjs +19 -0
- package/node_modules/node-fetch-native/dist/node.mjs +19 -0
- package/node_modules/node-fetch-native/dist/polyfill.cjs +1 -0
- package/node_modules/node-fetch-native/dist/polyfill.mjs +1 -0
- package/node_modules/node-fetch-native/dist/proxy-stub.cjs +1 -0
- package/node_modules/node-fetch-native/dist/proxy-stub.mjs +1 -0
- package/node_modules/node-fetch-native/dist/proxy.cjs +58 -0
- package/node_modules/node-fetch-native/dist/shared/node-fetch-native.DfbY2q-x.mjs +1 -0
- package/node_modules/node-fetch-native/dist/shared/node-fetch-native.DhEqb06g.cjs +1 -0
- package/node_modules/node-fetch-native/index.d.ts +1 -0
- package/node_modules/node-fetch-native/lib/empty.cjs +0 -0
- package/node_modules/node-fetch-native/lib/empty.mjs +0 -0
- package/node_modules/node-fetch-native/lib/index.cjs +11 -0
- package/node_modules/node-fetch-native/lib/index.d.cts +10 -0
- package/node_modules/node-fetch-native/lib/index.d.mts +10 -0
- package/node_modules/node-fetch-native/lib/index.d.ts +10 -0
- package/node_modules/node-fetch-native/lib/native.cjs +11 -0
- package/node_modules/node-fetch-native/lib/polyfill.d.cts +1 -0
- package/node_modules/node-fetch-native/lib/polyfill.d.mts +1 -0
- package/node_modules/node-fetch-native/lib/polyfill.d.ts +1 -0
- package/node_modules/node-fetch-native/lib/proxy.d.ts +32 -0
- package/node_modules/node-fetch-native/node.d.ts +1 -0
- package/node_modules/node-fetch-native/package.json +138 -0
- package/node_modules/node-fetch-native/polyfill.d.ts +1 -0
- package/node_modules/node-fetch-native/proxy.d.ts +1 -0
- package/node_modules/nypm/LICENSE +21 -0
- package/node_modules/nypm/README.md +152 -0
- package/node_modules/nypm/dist/cli.d.mts +1 -0
- package/node_modules/nypm/dist/cli.mjs +460 -0
- package/node_modules/nypm/dist/index.d.mts +209 -0
- package/node_modules/nypm/dist/index.mjs +423 -0
- package/node_modules/nypm/node_modules/citty/LICENSE +21 -0
- package/node_modules/nypm/node_modules/citty/README.md +231 -0
- package/node_modules/nypm/node_modules/citty/dist/THIRD-PARTY-LICENSES.md +33 -0
- package/node_modules/nypm/node_modules/citty/dist/_chunks/libs/scule.mjs +70 -0
- package/node_modules/nypm/node_modules/citty/dist/index.d.mts +112 -0
- package/node_modules/nypm/node_modules/citty/dist/index.mjs +425 -0
- package/node_modules/nypm/node_modules/citty/package.json +42 -0
- package/node_modules/nypm/package.json +54 -0
- package/node_modules/pathe/LICENSE +70 -0
- package/node_modules/pathe/README.md +73 -0
- package/node_modules/pathe/dist/index.cjs +39 -0
- package/node_modules/pathe/dist/index.d.cts +47 -0
- package/node_modules/pathe/dist/index.d.mts +47 -0
- package/node_modules/pathe/dist/index.d.ts +47 -0
- package/node_modules/pathe/dist/index.mjs +19 -0
- package/node_modules/pathe/dist/shared/pathe.BSlhyZSM.cjs +266 -0
- package/node_modules/pathe/dist/shared/pathe.M-eThtNZ.mjs +249 -0
- package/node_modules/pathe/dist/utils.cjs +82 -0
- package/node_modules/pathe/dist/utils.d.cts +32 -0
- package/node_modules/pathe/dist/utils.d.mts +32 -0
- package/node_modules/pathe/dist/utils.d.ts +32 -0
- package/node_modules/pathe/dist/utils.mjs +77 -0
- package/node_modules/pathe/package.json +61 -0
- package/node_modules/pathe/utils.d.ts +1 -0
- package/node_modules/picocolors/LICENSE +15 -0
- package/node_modules/picocolors/README.md +21 -0
- package/node_modules/picocolors/package.json +25 -0
- package/node_modules/picocolors/picocolors.browser.js +4 -0
- package/node_modules/picocolors/picocolors.d.ts +5 -0
- package/node_modules/picocolors/picocolors.js +75 -0
- package/node_modules/picocolors/types.d.ts +51 -0
- package/node_modules/sisteransi/license +21 -0
- package/node_modules/sisteransi/package.json +34 -0
- package/node_modules/sisteransi/readme.md +113 -0
- package/node_modules/sisteransi/src/index.js +58 -0
- package/node_modules/sisteransi/src/sisteransi.d.ts +35 -0
- package/node_modules/tinyexec/LICENSE +21 -0
- package/node_modules/tinyexec/README.md +269 -0
- package/node_modules/tinyexec/dist/LICENSES.txt +83 -0
- package/node_modules/tinyexec/dist/main.d.mts +71 -0
- package/node_modules/tinyexec/dist/main.mjs +642 -0
- package/node_modules/tinyexec/package.json +62 -0
- package/package.json +74 -73
- package/CHANGELOG.md +0 -770
- package/bin/maizzle +0 -5
- package/src/commands/build.js +0 -348
- package/src/commands/serve.js +0 -3
- package/src/generators/plaintext.js +0 -222
- package/src/generators/render.js +0 -129
- package/src/index.js +0 -49
- package/src/posthtml/defaultComponentsConfig.js +0 -19
- package/src/posthtml/defaultConfig.js +0 -14
- package/src/posthtml/index.js +0 -102
- package/src/posthtml/plugins/combineMediaQueries.js +0 -42
- package/src/posthtml/plugins/envAttributes.js +0 -32
- package/src/posthtml/plugins/envTags.js +0 -33
- package/src/posthtml/plugins/expandLinkTag.js +0 -59
- package/src/posthtml/plugins/postcss/compileCss.js +0 -125
- package/src/posthtml/plugins/removeRawStyleAttributes.js +0 -30
- package/src/server/client.js +0 -182
- package/src/server/index.js +0 -464
- package/src/server/routes/hmr.js +0 -26
- package/src/server/routes/index.js +0 -77
- package/src/server/views/404.html +0 -59
- package/src/server/views/error.html +0 -83
- package/src/server/views/index.html +0 -172
- package/src/server/websockets.js +0 -27
- package/src/transformers/addAttributes.js +0 -29
- package/src/transformers/attributeToStyle.js +0 -90
- package/src/transformers/baseUrl.js +0 -154
- package/src/transformers/core.js +0 -32
- package/src/transformers/filters/defaultFilters.js +0 -146
- package/src/transformers/filters/index.js +0 -18
- package/src/transformers/index.js +0 -259
- package/src/transformers/inline.js +0 -306
- package/src/transformers/markdown.js +0 -26
- package/src/transformers/minify.js +0 -27
- package/src/transformers/posthtmlMso.js +0 -14
- package/src/transformers/prettify.js +0 -29
- package/src/transformers/preventWidows.js +0 -37
- package/src/transformers/purge.js +0 -56
- package/src/transformers/removeAttributes.js +0 -55
- package/src/transformers/replaceStrings.js +0 -37
- package/src/transformers/safeClassNames.js +0 -29
- package/src/transformers/shorthandCss.js +0 -20
- package/src/transformers/sixHex.js +0 -30
- package/src/transformers/template.js +0 -26
- package/src/transformers/urlParameters.js +0 -20
- package/src/transformers/useAttributeSizes.js +0 -63
- package/src/utils/getConfigByFilePath.js +0 -143
- package/src/utils/node.js +0 -68
- package/src/utils/string.js +0 -186
- package/types/build.d.ts +0 -166
- package/types/config.d.ts +0 -638
- package/types/css/combineMediaQueries.d.ts +0 -90
- package/types/css/inline.d.ts +0 -218
- package/types/css/purge.d.ts +0 -125
- package/types/events.d.ts +0 -153
- package/types/index.d.ts +0 -232
- package/types/markdown.d.ts +0 -33
- package/types/minify.d.ts +0 -138
- package/types/plaintext.d.ts +0 -56
- package/types/posthtml.d.ts +0 -162
- package/types/render.d.ts +0 -13
- package/types/urlParameters.d.ts +0 -40
- 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 }} × {{ 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 }}×{{ 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>
|