@maizzle/framework 6.0.0-rc.22 → 6.0.0-rc.23
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/dist/build.d.ts.map +1 -1
- package/dist/build.js +11 -0
- package/dist/build.js.map +1 -1
- package/dist/components/Heading.vue +1 -1
- package/dist/components/Img.vue +60 -10
- package/dist/components/Link.vue +1 -1
- package/dist/components/Preheader.vue +4 -2
- package/dist/components/Tailwind.vue +4 -2
- package/dist/components/Text.vue +1 -1
- package/dist/components/Vml.vue +354 -0
- package/dist/components/utils.d.ts.map +1 -1
- package/dist/components/utils.js.map +1 -1
- package/dist/composables/defineConfig.js.map +1 -1
- package/dist/composables/renderContext.d.ts.map +1 -1
- package/dist/composables/renderContext.js.map +1 -1
- package/dist/composables/useBaseUrl.d.ts.map +1 -1
- package/dist/composables/useBaseUrl.js.map +1 -1
- package/dist/composables/useConfig.d.ts.map +1 -1
- package/dist/composables/useConfig.js.map +1 -1
- package/dist/composables/useCurrentTemplate.d.ts.map +1 -1
- package/dist/composables/useCurrentTemplate.js +10 -3
- package/dist/composables/useCurrentTemplate.js.map +1 -1
- package/dist/composables/useDoctype.d.ts.map +1 -1
- package/dist/composables/useDoctype.js.map +1 -1
- package/dist/composables/useEvent.js.map +1 -1
- package/dist/composables/useFont.d.ts.map +1 -1
- package/dist/composables/useFont.js.map +1 -1
- package/dist/composables/useOutlookFallback.d.ts.map +1 -1
- package/dist/composables/useOutlookFallback.js.map +1 -1
- package/dist/composables/usePlaintext.d.ts.map +1 -1
- package/dist/composables/usePlaintext.js.map +1 -1
- package/dist/composables/usePreheader.d.ts.map +1 -1
- package/dist/composables/usePreheader.js.map +1 -1
- package/dist/composables/useTransformers.d.ts.map +1 -1
- package/dist/composables/useTransformers.js.map +1 -1
- package/dist/composables/useUrlQuery.d.ts.map +1 -1
- package/dist/composables/useUrlQuery.js.map +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/index.js +12 -0
- package/dist/config/index.js.map +1 -1
- package/dist/events/index.d.ts +5 -0
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +5 -0
- package/dist/events/index.js.map +1 -1
- package/dist/plaintext.d.ts.map +1 -1
- package/dist/plaintext.js.map +1 -1
- package/dist/plugin.js.map +1 -1
- package/dist/plugins/postcss/mergeMediaQueries.d.ts.map +1 -1
- package/dist/plugins/postcss/mergeMediaQueries.js.map +1 -1
- package/dist/plugins/postcss/pruneVars.d.ts.map +1 -1
- package/dist/plugins/postcss/pruneVars.js.map +1 -1
- package/dist/plugins/postcss/quoteFontFamilies.d.ts.map +1 -1
- package/dist/plugins/postcss/quoteFontFamilies.js.map +1 -1
- package/dist/plugins/postcss/removeDeclarations.d.ts.map +1 -1
- package/dist/plugins/postcss/removeDeclarations.js.map +1 -1
- package/dist/plugins/postcss/resolveMaizzleImports.d.ts.map +1 -1
- package/dist/plugins/postcss/resolveMaizzleImports.js.map +1 -1
- package/dist/plugins/postcss/resolveProps.d.ts.map +1 -1
- package/dist/plugins/postcss/resolveProps.js +14 -0
- package/dist/plugins/postcss/resolveProps.js.map +1 -1
- package/dist/plugins/postcss/tailwindCleanup.d.ts.map +1 -1
- package/dist/plugins/postcss/tailwindCleanup.js.map +1 -1
- package/dist/prepare.d.ts.map +1 -1
- package/dist/prepare.js.map +1 -1
- package/dist/render/active.d.ts.map +1 -1
- package/dist/render/active.js.map +1 -1
- package/dist/render/createRenderer.d.ts.map +1 -1
- package/dist/render/createRenderer.js +89 -1
- package/dist/render/createRenderer.js.map +1 -1
- package/dist/render/index.d.ts.map +1 -1
- package/dist/render/index.js +6 -0
- package/dist/render/index.js.map +1 -1
- package/dist/render/injectFonts.js.map +1 -1
- package/dist/render/plugins/codeBlockExtract.d.ts.map +1 -1
- package/dist/render/plugins/codeBlockExtract.js +4 -0
- package/dist/render/plugins/codeBlockExtract.js.map +1 -1
- package/dist/render/plugins/markdownExtract.d.ts.map +1 -1
- package/dist/render/plugins/markdownExtract.js.map +1 -1
- package/dist/render/plugins/rawExtract.d.ts.map +1 -1
- package/dist/render/plugins/rawExtract.js.map +1 -1
- package/dist/render/plugins/rowSourceLocation.d.ts.map +1 -1
- package/dist/render/plugins/rowSourceLocation.js.map +1 -1
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +48 -15
- package/dist/serve.js.map +1 -1
- package/dist/server/compatibility.d.ts.map +1 -1
- package/dist/server/compatibility.js +48 -0
- package/dist/server/compatibility.js.map +1 -1
- package/dist/server/email.js.map +1 -1
- package/dist/server/linter.js +6 -0
- package/dist/server/linter.js.map +1 -1
- package/dist/server/sfc-utils.d.ts.map +1 -1
- package/dist/server/sfc-utils.js.map +1 -1
- package/dist/server/ui/App.vue +17 -16
- package/dist/server/ui/components/Markdown.vue +17 -0
- package/dist/server/ui/components/SidebarClose.vue +1 -1
- package/dist/server/ui/components/ui/checkbox/Checkbox.vue +1 -1
- package/dist/server/ui/components/ui/command/CommandInput.vue +2 -2
- package/dist/server/ui/components/ui/dialog/DialogContent.vue +1 -1
- package/dist/server/ui/components/ui/dialog/DialogScrollContent.vue +1 -1
- package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +1 -1
- package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +1 -1
- package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +1 -1
- package/dist/server/ui/components/ui/sheet/SheetContent.vue +1 -1
- package/dist/server/ui/components/ui/sidebar/SidebarTrigger.vue +1 -1
- package/dist/server/ui/components/ui/tags-input/TagsInputItemDelete.vue +1 -1
- package/dist/server/ui/lib/emulated-dark-mode.ts +25 -10
- package/dist/server/ui/pages/Home.vue +1 -1
- package/dist/server/ui/pages/Preview.vue +32 -18
- package/dist/tests/render/_helpers.js.map +1 -1
- package/dist/transformers/addAttributes.d.ts.map +1 -1
- package/dist/transformers/addAttributes.js.map +1 -1
- package/dist/transformers/attributeToStyle.d.ts.map +1 -1
- package/dist/transformers/attributeToStyle.js.map +1 -1
- package/dist/transformers/base.d.ts.map +1 -1
- package/dist/transformers/base.js +4 -0
- package/dist/transformers/base.js.map +1 -1
- package/dist/transformers/columnWidth.d.ts.map +1 -1
- package/dist/transformers/columnWidth.js.map +1 -1
- package/dist/transformers/entities.d.ts.map +1 -1
- package/dist/transformers/entities.js.map +1 -1
- package/dist/transformers/filters/defaults.d.ts.map +1 -1
- package/dist/transformers/filters/defaults.js.map +1 -1
- package/dist/transformers/filters/index.d.ts.map +1 -1
- package/dist/transformers/filters/index.js.map +1 -1
- package/dist/transformers/format.d.ts.map +1 -1
- package/dist/transformers/format.js.map +1 -1
- package/dist/transformers/index.d.ts.map +1 -1
- package/dist/transformers/index.js +26 -0
- package/dist/transformers/index.js.map +1 -1
- package/dist/transformers/inlineCss.d.ts.map +1 -1
- package/dist/transformers/inlineCss.js +25 -2
- package/dist/transformers/inlineCss.js.map +1 -1
- package/dist/transformers/inlineLink.d.ts.map +1 -1
- package/dist/transformers/inlineLink.js.map +1 -1
- package/dist/transformers/minify.d.ts.map +1 -1
- package/dist/transformers/minify.js.map +1 -1
- package/dist/transformers/minifyCodeInline.d.ts.map +1 -1
- package/dist/transformers/minifyCodeInline.js.map +1 -1
- package/dist/transformers/msoPlaceholders.d.ts.map +1 -1
- package/dist/transformers/msoPlaceholders.js.map +1 -1
- package/dist/transformers/purgeCss.d.ts.map +1 -1
- package/dist/transformers/purgeCss.js +29 -3
- package/dist/transformers/purgeCss.js.map +1 -1
- package/dist/transformers/removeAttributes.d.ts.map +1 -1
- package/dist/transformers/removeAttributes.js.map +1 -1
- package/dist/transformers/replaceStrings.d.ts.map +1 -1
- package/dist/transformers/replaceStrings.js.map +1 -1
- package/dist/transformers/safeSelectors.d.ts.map +1 -1
- package/dist/transformers/safeSelectors.js +13 -1
- package/dist/transformers/safeSelectors.js.map +1 -1
- package/dist/transformers/shorthandCss.d.ts.map +1 -1
- package/dist/transformers/shorthandCss.js.map +1 -1
- package/dist/transformers/sixHex.d.ts.map +1 -1
- package/dist/transformers/sixHex.js.map +1 -1
- package/dist/transformers/tailwindComponent.js +9 -0
- package/dist/transformers/tailwindComponent.js.map +1 -1
- package/dist/transformers/tailwindcss.d.ts.map +1 -1
- package/dist/transformers/tailwindcss.js +22 -0
- package/dist/transformers/tailwindcss.js.map +1 -1
- package/dist/transformers/urlQuery.d.ts.map +1 -1
- package/dist/transformers/urlQuery.js.map +1 -1
- package/dist/types/config.d.ts +4 -8
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/utils/ast/parser.d.ts.map +1 -1
- package/dist/utils/ast/parser.js.map +1 -1
- package/dist/utils/ast/serializer.d.ts.map +1 -1
- package/dist/utils/ast/serializer.js.map +1 -1
- package/dist/utils/ast/walker.d.ts.map +1 -1
- package/dist/utils/ast/walker.js.map +1 -1
- package/dist/utils/compileTailwindCss.d.ts.map +1 -1
- package/dist/utils/compileTailwindCss.js.map +1 -1
- package/dist/utils/componentSources.d.ts.map +1 -1
- package/dist/utils/componentSources.js.map +1 -1
- package/dist/utils/cssBox.d.ts.map +1 -1
- package/dist/utils/cssBox.js.map +1 -1
- package/dist/utils/decodeStyleEntities.d.ts.map +1 -1
- package/dist/utils/decodeStyleEntities.js.map +1 -1
- package/dist/utils/detect.d.ts.map +1 -1
- package/dist/utils/detect.js.map +1 -1
- package/dist/utils/output-markers.d.ts.map +1 -1
- package/dist/utils/output-markers.js.map +1 -1
- package/dist/utils/url.d.ts.map +1 -1
- package/dist/utils/url.js.map +1 -1
- package/dist/utils/watchPaths.js.map +1 -1
- package/node_modules/@clack/core/CHANGELOG.md +6 -0
- package/node_modules/@clack/core/dist/index.d.mts +1 -1
- package/node_modules/@clack/core/dist/index.mjs +8 -8
- package/node_modules/@clack/core/dist/index.mjs.map +1 -1
- package/node_modules/@clack/core/package.json +1 -1
- package/node_modules/@clack/prompts/CHANGELOG.md +13 -0
- package/node_modules/@clack/prompts/README.md +2 -2
- package/node_modules/@clack/prompts/dist/index.d.mts +98 -0
- package/node_modules/@clack/prompts/dist/index.mjs +122 -121
- package/node_modules/@clack/prompts/dist/index.mjs.map +1 -1
- package/node_modules/@clack/prompts/package.json +2 -2
- package/node_modules/fast-wrap-ansi/lib/main.js +0 -1
- package/node_modules/fast-wrap-ansi/package.json +10 -10
- package/node_modules/maizzle/dist/commands/make/config.mjs +7 -6
- package/node_modules/maizzle/dist/commands/new.mjs +15 -84
- package/node_modules/maizzle/package.json +2 -2
- package/node_modules/tinyexec/README.md +8 -0
- package/node_modules/tinyexec/dist/main.d.mts +16 -1
- package/node_modules/tinyexec/dist/main.mjs +163 -457
- package/node_modules/tinyexec/package.json +12 -14
- package/package.json +3 -4
- package/node_modules/fast-wrap-ansi/lib/main.js.map +0 -1
- package/node_modules/tinyexec/dist/LICENSES.txt +0 -83
package/dist/build.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","names":[],"sources":["../src/build.ts"],"mappings":";;UAeiB,WAAA;EACf,KAAA;EACA,MAAA,EAAQ,
|
|
1
|
+
{"version":3,"file":"build.d.ts","names":[],"sources":["../src/build.ts"],"mappings":";;UAeiB,WAAA;EACf,KAAA;EACA,MAAA,EAAQ,aAAa;AAAA;;;;;;;;AAAA;AAavB;;iBAAsB,KAAA,CAAM,WAAA,GAAc,OAAA,CAAQ,aAAA,aAA0B,OAAA,CAAQ,WAAA"}
|
package/dist/build.js
CHANGED
|
@@ -70,12 +70,23 @@ async function build(configInput) {
|
|
|
70
70
|
template
|
|
71
71
|
});
|
|
72
72
|
const rendered = await renderer.render(absolutePath, config);
|
|
73
|
+
/**
|
|
74
|
+
* Register SFC event handlers collected during render so they take
|
|
75
|
+
* part in the post-render events (afterRender / afterTransform).
|
|
76
|
+
* They're cleared at the end of the iteration so they don't
|
|
77
|
+
* leak into the next template.
|
|
78
|
+
*/
|
|
73
79
|
for (const { name, handler } of rendered.sfcEventHandlers) events.on(name, handler);
|
|
74
80
|
let html = await events.fireAfterRender({
|
|
75
81
|
config,
|
|
76
82
|
template,
|
|
77
83
|
html: rendered.html
|
|
78
84
|
});
|
|
85
|
+
/**
|
|
86
|
+
* Use the per-template merged config (from defineConfig() in the SFC) so
|
|
87
|
+
* that template-level overrides like css.safe: false are respected
|
|
88
|
+
* by transformers.
|
|
89
|
+
*/
|
|
79
90
|
const templateConfig = rendered.templateConfig;
|
|
80
91
|
const doctype = rendered.doctype ?? templateConfig.doctype ?? "<!DOCTYPE html>";
|
|
81
92
|
if (templateConfig.useTransformers !== false) html = await runTransformers(html, templateConfig, absolutePath, doctype, rendered.tailwindBlocks);
|
package/dist/build.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.js","names":["parsePath"],"sources":["../src/build.ts"],"sourcesContent":["import { readFileSync, writeFileSync, mkdirSync, cpSync, existsSync, rmSync } from 'node:fs'\nimport { resolve, dirname, basename, relative, join, parse as parsePath } from 'node:path'\nimport { glob } from 'tinyglobby'\nimport ora from 'ora'\nimport { resolveConfig } from './config/index.ts'\nimport { EventManager } from './events/index.ts'\nimport { runTransformers } from './transformers/index.ts'\nimport { createRenderer } from './render/createRenderer.ts'\nimport { createPlaintext } from './plaintext.ts'\nimport { stripForHtml, stripForPlaintext } from './utils/output-markers.ts'\nimport { normalizeComponentSources } from './utils/componentSources.ts'\nimport { _setCurrentTemplate } from './composables/useCurrentTemplate.ts'\nimport defu from 'defu'\nimport type { MaizzleConfig } from './types/index.ts'\n\nexport interface BuildResult {\n files: string[]\n config: MaizzleConfig\n}\n\n/**\n * Build all SFC email templates to HTML files.\n *\n * Creates a single Renderer instance, then loops through each template\n * calling render → transformers → write to disk.\n *\n * Pass a `Partial<MaizzleConfig>` to override config inline, or a string\n * to load config from a specific file path. Omit to load `maizzle.config`\n * from the working directory.\n */\nexport async function build(configInput?: Partial<MaizzleConfig> | string): Promise<BuildResult> {\n const start = Date.now()\n const spinner = ora({ text: 'Building templates...', spinner: 'circleHalves' }).start()\n\n const config = await resolveConfig(configInput)\n\n const events = new EventManager()\n events.registerConfig(config)\n await events.fireBeforeCreate({ config })\n\n const outputPath = resolve(config.output?.path ?? 'dist')\n const outputExtension = config.output?.extension ?? 'html'\n\n const contentPatterns = config.content ?? ['emails/**/*.vue']\n const contentBase = computeContentBase(contentPatterns)\n const templateFiles = await glob(contentPatterns)\n\n if (templateFiles.length === 0) {\n spinner.succeed('No templates found')\n return { files: [], config }\n }\n\n // Clear the output directory before writing fresh output\n if (existsSync(outputPath)) {\n rmSync(outputPath, { recursive: true, force: true })\n }\n\n const renderer = await createRenderer({ markdown: config.markdown, root: config.root, componentDirs: normalizeComponentSources(config.components?.source, process.cwd()), vite: config.vite })\n const outputFiles: string[] = []\n\n try {\n for (const templatePath of templateFiles) {\n const absolutePath = resolve(templatePath)\n const parsedPath = parsePath(absolutePath)\n const template = { source: readFileSync(absolutePath, 'utf-8'), path: parsedPath }\n\n _setCurrentTemplate(parsedPath)\n\n try {\n await events.fireBeforeRender({ config, template })\n\n const rendered = await renderer.render(absolutePath, config)\n\n // Register SFC event handlers collected during render so they participate\n // in the post-render events (afterRender / afterTransform). They're cleared\n // at the end of the iteration so they don't leak into the next template.\n for (const { name, handler } of rendered.sfcEventHandlers) {\n events.on(name, handler)\n }\n\n let html = await events.fireAfterRender({ config, template, html: rendered.html })\n\n // Use the per-template merged config (from defineConfig() in the SFC) so that\n // template-level overrides like css.safe: false are respected by transformers.\n const templateConfig = rendered.templateConfig\n\n const doctype = rendered.doctype ?? templateConfig.doctype ?? '<!DOCTYPE html>'\n\n if (templateConfig.useTransformers !== false) {\n html = await runTransformers(html, templateConfig, absolutePath, doctype, rendered.tailwindBlocks)\n }\n\n html = await events.fireAfterTransform({ config, template, html })\n html = `${doctype}\\n${html}`\n\n const htmlOut = stripForHtml(html)\n const outputFilePath = resolveOutputPath(templatePath, outputPath, outputExtension, contentBase)\n mkdirSync(dirname(outputFilePath), { recursive: true })\n writeFileSync(outputFilePath, htmlOut)\n outputFiles.push(outputFilePath)\n\n // Generate plaintext version if configured\n const globalPlaintext = templateConfig.plaintext\n const sfcPlaintext = rendered.plaintext\n\n if (globalPlaintext || sfcPlaintext) {\n const globalCfg = typeof globalPlaintext === 'object' ? globalPlaintext : {}\n const stripOptions = defu(sfcPlaintext?.options, globalCfg.options)\n const plaintext = createPlaintext(stripForPlaintext(html), stripOptions)\n const ptExtension = sfcPlaintext?.extension ?? globalCfg.extension ?? 'txt'\n\n let ptOutputPath: string\n\n if (sfcPlaintext?.destination) {\n const name = basename(templatePath).replace(/\\.(vue|md)$/, '')\n ptOutputPath = join(resolve(sfcPlaintext.destination), `${name}.${ptExtension}`)\n } else if (globalCfg.destination) {\n ptOutputPath = resolveOutputPath(templatePath, resolve(globalCfg.destination), ptExtension, contentBase)\n } else {\n ptOutputPath = resolveOutputPath(templatePath, outputPath, ptExtension, contentBase)\n }\n\n mkdirSync(dirname(ptOutputPath), { recursive: true })\n writeFileSync(ptOutputPath, plaintext)\n }\n } finally {\n _setCurrentTemplate(undefined)\n events.clearSfcHandlers()\n }\n }\n\n await copyStatic(config, outputPath)\n await events.fireAfterBuild({ files: outputFiles, config })\n } finally {\n await renderer.close()\n }\n\n const duration = ((Date.now() - start) / 1000).toFixed(2)\n const count = outputFiles.length\n spinner.stopAndPersist({\n symbol: '✅',\n text: `Built ${count} template${count !== 1 ? 's' : ''} in ${duration}s`,\n })\n\n return { files: outputFiles, config }\n}\n\n/**\n * Extract the static (non-glob) prefix from content patterns.\n *\n * For example, `['/abs/path/emails/**\\/*.vue']` → `'/abs/path/emails'`\n *\n * This is used to strip the content base from template paths\n * so the output preserves only the subdirectory structure.\n */\nfunction computeContentBase(patterns: string[]): string {\n // Use the first non-negated pattern\n const pattern = patterns.find(p => !p.startsWith('!')) ?? patterns[0]\n\n // Split on first glob character (* { ? [) and take the directory part\n const staticPart = pattern.split(/[*{?[]/)[0]\n\n // Ensure we have a clean directory path (not a partial segment)\n return resolve(staticPart.endsWith('/') ? staticPart : dirname(staticPart))\n}\n\nfunction resolveOutputPath(templatePath: string, outputDir: string, extension: string, contentBase: string): string {\n const name = basename(templatePath).replace(/\\.(vue|md)$/, '')\n const absTemplate = resolve(templatePath)\n const rel = relative(contentBase, dirname(absTemplate))\n\n return join(outputDir, rel, `${name}.${extension}`)\n}\n\nasync function copyStatic(config: MaizzleConfig, outputPath: string): Promise<void> {\n const sources = config.static?.source ?? ['public/**/*.*']\n const destination = config.static?.destination ?? 'public'\n\n const files = await glob(sources)\n\n for (const file of files) {\n const destPath = join(outputPath, destination, relative(dirname(sources[0]).replace(/\\*.*$/, ''), file))\n const destDir = dirname(destPath)\n\n if (!existsSync(destDir)) {\n mkdirSync(destDir, { recursive: true })\n }\n\n cpSync(file, destPath)\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"build.js","names":["parsePath"],"sources":["../src/build.ts"],"sourcesContent":["import { readFileSync, writeFileSync, mkdirSync, cpSync, existsSync, rmSync } from 'node:fs'\nimport { resolve, dirname, basename, relative, join, parse as parsePath } from 'node:path'\nimport { glob } from 'tinyglobby'\nimport ora from 'ora'\nimport { resolveConfig } from './config/index.ts'\nimport { EventManager } from './events/index.ts'\nimport { runTransformers } from './transformers/index.ts'\nimport { createRenderer } from './render/createRenderer.ts'\nimport { createPlaintext } from './plaintext.ts'\nimport { stripForHtml, stripForPlaintext } from './utils/output-markers.ts'\nimport { normalizeComponentSources } from './utils/componentSources.ts'\nimport { _setCurrentTemplate } from './composables/useCurrentTemplate.ts'\nimport defu from 'defu'\nimport type { MaizzleConfig } from './types/index.ts'\n\nexport interface BuildResult {\n files: string[]\n config: MaizzleConfig\n}\n\n/**\n * Build all SFC email templates to HTML files.\n *\n * Creates a single Renderer instance, then loops through each template\n * calling render → transformers → write to disk.\n *\n * Pass a `Partial<MaizzleConfig>` to override config inline, or a string\n * to load config from a specific file path. Omit to load `maizzle.config`\n * from the working directory.\n */\nexport async function build(configInput?: Partial<MaizzleConfig> | string): Promise<BuildResult> {\n const start = Date.now()\n const spinner = ora({ text: 'Building templates...', spinner: 'circleHalves' }).start()\n\n const config = await resolveConfig(configInput)\n\n const events = new EventManager()\n events.registerConfig(config)\n await events.fireBeforeCreate({ config })\n\n const outputPath = resolve(config.output?.path ?? 'dist')\n const outputExtension = config.output?.extension ?? 'html'\n\n const contentPatterns = config.content ?? ['emails/**/*.vue']\n const contentBase = computeContentBase(contentPatterns)\n const templateFiles = await glob(contentPatterns)\n\n if (templateFiles.length === 0) {\n spinner.succeed('No templates found')\n return { files: [], config }\n }\n\n // Clear the output directory before writing fresh output\n if (existsSync(outputPath)) {\n rmSync(outputPath, { recursive: true, force: true })\n }\n\n const renderer = await createRenderer({ markdown: config.markdown, root: config.root, componentDirs: normalizeComponentSources(config.components?.source, process.cwd()), vite: config.vite })\n const outputFiles: string[] = []\n\n try {\n for (const templatePath of templateFiles) {\n const absolutePath = resolve(templatePath)\n const parsedPath = parsePath(absolutePath)\n const template = { source: readFileSync(absolutePath, 'utf-8'), path: parsedPath }\n\n _setCurrentTemplate(parsedPath)\n\n try {\n await events.fireBeforeRender({ config, template })\n\n const rendered = await renderer.render(absolutePath, config)\n\n /**\n * Register SFC event handlers collected during render so they take\n * part in the post-render events (afterRender / afterTransform).\n * They're cleared at the end of the iteration so they don't\n * leak into the next template.\n */\n for (const { name, handler } of rendered.sfcEventHandlers) {\n events.on(name, handler)\n }\n\n let html = await events.fireAfterRender({ config, template, html: rendered.html })\n\n /**\n * Use the per-template merged config (from defineConfig() in the SFC) so\n * that template-level overrides like css.safe: false are respected\n * by transformers.\n */\n const templateConfig = rendered.templateConfig\n\n const doctype = rendered.doctype ?? templateConfig.doctype ?? '<!DOCTYPE html>'\n\n if (templateConfig.useTransformers !== false) {\n html = await runTransformers(html, templateConfig, absolutePath, doctype, rendered.tailwindBlocks)\n }\n\n html = await events.fireAfterTransform({ config, template, html })\n html = `${doctype}\\n${html}`\n\n const htmlOut = stripForHtml(html)\n const outputFilePath = resolveOutputPath(templatePath, outputPath, outputExtension, contentBase)\n mkdirSync(dirname(outputFilePath), { recursive: true })\n writeFileSync(outputFilePath, htmlOut)\n outputFiles.push(outputFilePath)\n\n // Generate plaintext version if configured\n const globalPlaintext = templateConfig.plaintext\n const sfcPlaintext = rendered.plaintext\n\n if (globalPlaintext || sfcPlaintext) {\n const globalCfg = typeof globalPlaintext === 'object' ? globalPlaintext : {}\n const stripOptions = defu(sfcPlaintext?.options, globalCfg.options)\n const plaintext = createPlaintext(stripForPlaintext(html), stripOptions)\n const ptExtension = sfcPlaintext?.extension ?? globalCfg.extension ?? 'txt'\n\n let ptOutputPath: string\n\n if (sfcPlaintext?.destination) {\n const name = basename(templatePath).replace(/\\.(vue|md)$/, '')\n ptOutputPath = join(resolve(sfcPlaintext.destination), `${name}.${ptExtension}`)\n } else if (globalCfg.destination) {\n ptOutputPath = resolveOutputPath(templatePath, resolve(globalCfg.destination), ptExtension, contentBase)\n } else {\n ptOutputPath = resolveOutputPath(templatePath, outputPath, ptExtension, contentBase)\n }\n\n mkdirSync(dirname(ptOutputPath), { recursive: true })\n writeFileSync(ptOutputPath, plaintext)\n }\n } finally {\n _setCurrentTemplate(undefined)\n events.clearSfcHandlers()\n }\n }\n\n await copyStatic(config, outputPath)\n await events.fireAfterBuild({ files: outputFiles, config })\n } finally {\n await renderer.close()\n }\n\n const duration = ((Date.now() - start) / 1000).toFixed(2)\n const count = outputFiles.length\n spinner.stopAndPersist({\n symbol: '✅',\n text: `Built ${count} template${count !== 1 ? 's' : ''} in ${duration}s`,\n })\n\n return { files: outputFiles, config }\n}\n\n/**\n * Extract the static (non-glob) prefix from content patterns.\n *\n * For example, `['/abs/path/emails/**\\/*.vue']` → `'/abs/path/emails'`\n *\n * This is used to strip the content base from template paths\n * so the output preserves only the subdirectory structure.\n */\nfunction computeContentBase(patterns: string[]): string {\n // Use the first non-negated pattern\n const pattern = patterns.find(p => !p.startsWith('!')) ?? patterns[0]\n\n // Split on first glob character (* { ? [) and take the directory part\n const staticPart = pattern.split(/[*{?[]/)[0]\n\n // Ensure we have a clean directory path (not a partial segment)\n return resolve(staticPart.endsWith('/') ? staticPart : dirname(staticPart))\n}\n\nfunction resolveOutputPath(templatePath: string, outputDir: string, extension: string, contentBase: string): string {\n const name = basename(templatePath).replace(/\\.(vue|md)$/, '')\n const absTemplate = resolve(templatePath)\n const rel = relative(contentBase, dirname(absTemplate))\n\n return join(outputDir, rel, `${name}.${extension}`)\n}\n\nasync function copyStatic(config: MaizzleConfig, outputPath: string): Promise<void> {\n const sources = config.static?.source ?? ['public/**/*.*']\n const destination = config.static?.destination ?? 'public'\n\n const files = await glob(sources)\n\n for (const file of files) {\n const destPath = join(outputPath, destination, relative(dirname(sources[0]).replace(/\\*.*$/, ''), file))\n const destDir = dirname(destPath)\n\n if (!existsSync(destDir)) {\n mkdirSync(destDir, { recursive: true })\n }\n\n cpSync(file, destPath)\n }\n}\n"],"mappings":"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"}
|
|
@@ -22,7 +22,7 @@ const mergedClass = computed(() => twMerge('m-0', attrs.class as string))
|
|
|
22
22
|
</script>
|
|
23
23
|
|
|
24
24
|
<template>
|
|
25
|
-
<component :is="tag" v-bind="
|
|
25
|
+
<component :is="tag" v-bind="{ ...$attrs, class: mergedClass }">
|
|
26
26
|
<slot />
|
|
27
27
|
</component>
|
|
28
28
|
</template>
|
package/dist/components/Img.vue
CHANGED
|
@@ -85,13 +85,27 @@ const props = defineProps({
|
|
|
85
85
|
/**
|
|
86
86
|
* Toggle Outlook (MSO) and VML fallback markup for this image.
|
|
87
87
|
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
88
|
+
* Inherits from an ancestor (e.g. a Layout calling
|
|
89
|
+
* `useOutlookFallback(false)`); an explicit value overrides. When
|
|
90
|
+
* `false`, the VML `<v:rect>` emitted in cropped mode (`aspect`)
|
|
91
|
+
* is skipped and the modern padding-hack div renders to all
|
|
92
|
+
* clients including Outlook (which will show an empty area).
|
|
91
93
|
*
|
|
92
|
-
* @default true
|
|
94
|
+
* @default inherits — root default `true`
|
|
93
95
|
*/
|
|
94
96
|
outlookFallback: outlookFallbackProp,
|
|
97
|
+
/**
|
|
98
|
+
* URL to navigate to when the image is clicked.
|
|
99
|
+
*
|
|
100
|
+
* Modern clients: output is wrapped in `<a href>`. In cropped mode the
|
|
101
|
+
* anchor is `display:block` so the whole padding-hack area is clickable.
|
|
102
|
+
* Outlook: emitted as the `href` attribute on the `<v:rect>` (a
|
|
103
|
+
* documented VML Shape attribute).
|
|
104
|
+
*/
|
|
105
|
+
href: {
|
|
106
|
+
type: String,
|
|
107
|
+
default: ''
|
|
108
|
+
},
|
|
95
109
|
})
|
|
96
110
|
|
|
97
111
|
const outlookFallback = useOutlookFallback(props.outlookFallback)
|
|
@@ -217,8 +231,10 @@ const vmlAspect = computed(() => {
|
|
|
217
231
|
const VmlRect = () => {
|
|
218
232
|
if (!isCropped.value || !heightPx.value || !Number.isFinite(imgWidth.value)) return null
|
|
219
233
|
const aspectAttr = vmlAspect.value ? ` aspect="${vmlAspect.value}"` : ''
|
|
234
|
+
const altAttr = props.alt ? ` alt="${escapeAttr(props.alt)}"` : ''
|
|
235
|
+
const hrefAttr = props.href ? ` href="${escapeAttr(props.href)}"` : ''
|
|
220
236
|
return createStaticVNode(
|
|
221
|
-
`<!--[if mso]><v:rect xmlns:v="urn:schemas-microsoft-com:vml" fill="true" stroke="false" style="width:${imgWidth.value}px;height:${heightPx.value}px;"><v:fill type="frame" src="${escapeAttr(props.src)}"${aspectAttr} /></v:rect><![endif]-->`,
|
|
237
|
+
`<!--[if mso]><v:rect xmlns:v="urn:schemas-microsoft-com:vml" fill="true" stroke="false"${hrefAttr}${altAttr} style="width:${imgWidth.value}px;height:${heightPx.value}px;"><v:fill type="frame" src="${escapeAttr(props.src)}"${aspectAttr} /></v:rect><![endif]-->`,
|
|
222
238
|
1
|
|
223
239
|
)
|
|
224
240
|
}
|
|
@@ -226,19 +242,43 @@ const VmlRect = () => {
|
|
|
226
242
|
const NotMsoBefore = () => createStaticVNode('<!--[if !mso]><!-->', 1)
|
|
227
243
|
const NotMsoAfter = () => createStaticVNode('<!--<![endif]-->', 1)
|
|
228
244
|
|
|
229
|
-
const
|
|
245
|
+
const imgClass = 'max-w-full align-middle'
|
|
230
246
|
</script>
|
|
231
247
|
|
|
232
248
|
<template>
|
|
233
249
|
<template v-if="isCropped">
|
|
234
250
|
<VmlRect v-if="outlookFallback" />
|
|
235
251
|
<NotMsoBefore v-if="outlookFallback" />
|
|
252
|
+
<a v-if="href" :href="href" class="block no-underline">
|
|
253
|
+
<div
|
|
254
|
+
v-bind="{ ...attrs, class: undefined }"
|
|
255
|
+
role="img"
|
|
256
|
+
:aria-label="alt || undefined"
|
|
257
|
+
:class="['overflow-hidden table max-w-full', parsedClass.className]"
|
|
258
|
+
:style="`width: ${imgWidth}px;`"
|
|
259
|
+
>
|
|
260
|
+
<div
|
|
261
|
+
:class="[
|
|
262
|
+
'table-cell w-full h-0 bg-no-repeat',
|
|
263
|
+
darkSrc ? `dark:bg-[url('${escapeForClass(darkSrc)}')]!` : '',
|
|
264
|
+
motionSrc ? `motion-safe:bg-[url('${escapeForClass(motionSrc)}')]!` : '',
|
|
265
|
+
]"
|
|
266
|
+
:style="{
|
|
267
|
+
paddingBottom: ratio!.paddingBottom,
|
|
268
|
+
backgroundImage: `url('${escapeForCssUrl(src)}')`,
|
|
269
|
+
backgroundSize: size,
|
|
270
|
+
backgroundPosition: position,
|
|
271
|
+
}"
|
|
272
|
+
/>
|
|
273
|
+
</div>
|
|
274
|
+
</a>
|
|
236
275
|
<div
|
|
276
|
+
v-else
|
|
237
277
|
v-bind="{ ...attrs, class: undefined }"
|
|
238
278
|
role="img"
|
|
239
279
|
:aria-label="alt || undefined"
|
|
240
|
-
:class="['overflow-hidden table', parsedClass.className]"
|
|
241
|
-
:style="`width: ${imgWidth}px
|
|
280
|
+
:class="['overflow-hidden table max-w-full', parsedClass.className]"
|
|
281
|
+
:style="`width: ${imgWidth}px;`"
|
|
242
282
|
>
|
|
243
283
|
<div
|
|
244
284
|
:class="[
|
|
@@ -256,10 +296,20 @@ const imgStyle = 'max-width: 100%; vertical-align: middle;'
|
|
|
256
296
|
</div>
|
|
257
297
|
<NotMsoAfter v-if="outlookFallback" />
|
|
258
298
|
</template>
|
|
299
|
+
<a v-else-if="href && usePicture" :href="href">
|
|
300
|
+
<picture>
|
|
301
|
+
<source v-if="darkSrc" :srcset="darkSrc" media="(prefers-color-scheme: dark)">
|
|
302
|
+
<source v-if="motionSrc" :srcset="motionSrc" :type="motionType || undefined" media="(prefers-reduced-motion: no-preference)">
|
|
303
|
+
<img v-bind="attrs" :src="src" :alt="alt" :width="imgWidth" :class="imgClass">
|
|
304
|
+
</picture>
|
|
305
|
+
</a>
|
|
259
306
|
<picture v-else-if="usePicture">
|
|
260
307
|
<source v-if="darkSrc" :srcset="darkSrc" media="(prefers-color-scheme: dark)">
|
|
261
308
|
<source v-if="motionSrc" :srcset="motionSrc" :type="motionType || undefined" media="(prefers-reduced-motion: no-preference)">
|
|
262
|
-
<img v-bind="attrs" :src="src" :alt="alt" :width="imgWidth" :
|
|
309
|
+
<img v-bind="attrs" :src="src" :alt="alt" :width="imgWidth" :class="imgClass">
|
|
263
310
|
</picture>
|
|
264
|
-
<
|
|
311
|
+
<a v-else-if="href" :href="href">
|
|
312
|
+
<img v-bind="attrs" :src="src" :alt="alt" :width="imgWidth" :class="imgClass">
|
|
313
|
+
</a>
|
|
314
|
+
<img v-else v-bind="attrs" :src="src" :alt="alt" :width="imgWidth" :class="imgClass">
|
|
265
315
|
</template>
|
package/dist/components/Link.vue
CHANGED
|
@@ -20,7 +20,7 @@ const mergedClass = computed(() => twMerge('no-underline', attrs.class as string
|
|
|
20
20
|
</script>
|
|
21
21
|
|
|
22
22
|
<template>
|
|
23
|
-
<a :href="href" v-bind="
|
|
23
|
+
<a :href="href" v-bind="{ ...$attrs, class: mergedClass }">
|
|
24
24
|
<slot />
|
|
25
25
|
</a>
|
|
26
26
|
</template>
|
|
@@ -24,8 +24,10 @@ function vnodesToText(nodes: unknown): string {
|
|
|
24
24
|
return ''
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Inbox preview budget. Pad with invisible fillers so the client
|
|
29
|
+
* doesn't pull body content into the snippet.
|
|
30
|
+
*/
|
|
29
31
|
const PREVIEW_LENGTH = 200
|
|
30
32
|
|
|
31
33
|
const text = computed(() => vnodesToText(slots.default?.()))
|
|
@@ -27,8 +27,10 @@ export default defineComponent({
|
|
|
27
27
|
if (!ctx.tailwindBlocks) ctx.tailwindBlocks = []
|
|
28
28
|
const id = `tw${ctx.tailwindBlocks.length}`
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Extract optional `#config` slot content as raw CSS. Evaluated at setup
|
|
32
|
+
* time; the slot is NOT rendered into the document.
|
|
33
|
+
*/
|
|
32
34
|
const css = slots.config ? vnodeText(slots.config()).trim() : undefined
|
|
33
35
|
|
|
34
36
|
ctx.tailwindBlocks.push({ id, css: css || undefined })
|
package/dist/components/Text.vue
CHANGED
|
@@ -23,7 +23,7 @@ const mergedClass = computed(() => twMerge(defaultClass.value, attrs.class as st
|
|
|
23
23
|
</script>
|
|
24
24
|
|
|
25
25
|
<template>
|
|
26
|
-
<component :is="props.as" v-bind="
|
|
26
|
+
<component :is="props.as" v-bind="{ ...$attrs, class: mergedClass }">
|
|
27
27
|
<slot />
|
|
28
28
|
</component>
|
|
29
29
|
</template>
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { computed, createStaticVNode } from 'vue'
|
|
3
|
+
import type { PropType } from 'vue'
|
|
4
|
+
import { normalizeToPixels } from './utils.ts'
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
name: 'Vml',
|
|
8
|
+
props: {
|
|
9
|
+
/**
|
|
10
|
+
* VML shape to render.
|
|
11
|
+
*
|
|
12
|
+
* - `rect` (default) — rectangle
|
|
13
|
+
* - `roundrect` — rectangle with rounded corners (`arcsize`)
|
|
14
|
+
* - `oval` — ellipse fitted to width/height
|
|
15
|
+
* - `line` — straight line between `from` and `to`
|
|
16
|
+
*
|
|
17
|
+
* @default 'rect'
|
|
18
|
+
*/
|
|
19
|
+
shape: {
|
|
20
|
+
type: String as PropType<'rect' | 'roundrect' | 'oval' | 'line'>,
|
|
21
|
+
default: 'rect',
|
|
22
|
+
validator: (v: string) => ['rect', 'roundrect', 'oval', 'line'].includes(v),
|
|
23
|
+
},
|
|
24
|
+
/**
|
|
25
|
+
* Corner radius for `roundrect`, as a fraction of the shorter side.
|
|
26
|
+
*
|
|
27
|
+
* Range 0–1. Ignored for other shapes.
|
|
28
|
+
*
|
|
29
|
+
* @example '0.1'
|
|
30
|
+
*/
|
|
31
|
+
arcsize: {
|
|
32
|
+
type: [String, Number],
|
|
33
|
+
},
|
|
34
|
+
/**
|
|
35
|
+
* Start coordinate for `shape="line"` as `"x,y"`.
|
|
36
|
+
*
|
|
37
|
+
* Required when `shape="line"`. Ignored otherwise.
|
|
38
|
+
*
|
|
39
|
+
* @example '0,0'
|
|
40
|
+
* @example '10pt,10pt'
|
|
41
|
+
*/
|
|
42
|
+
from: {
|
|
43
|
+
type: String,
|
|
44
|
+
validator: (v: string) => /^[\d.]+(px|pt|%|em|rem)?,[\d.]+(px|pt|%|em|rem)?$/.test(v.replace(/\s/g, '')),
|
|
45
|
+
},
|
|
46
|
+
/**
|
|
47
|
+
* End coordinate for `shape="line"` as `"x,y"`.
|
|
48
|
+
*
|
|
49
|
+
* Required when `shape="line"`. Ignored otherwise.
|
|
50
|
+
*
|
|
51
|
+
* @example '600,0'
|
|
52
|
+
* @example '100%,0'
|
|
53
|
+
*/
|
|
54
|
+
to: {
|
|
55
|
+
type: String,
|
|
56
|
+
validator: (v: string) => /^[\d.]+(px|pt|%|em|rem)?,[\d.]+(px|pt|%|em|rem)?$/.test(v.replace(/\s/g, '')),
|
|
57
|
+
},
|
|
58
|
+
/**
|
|
59
|
+
* Width of the shape. Ignored for `line`.
|
|
60
|
+
*
|
|
61
|
+
* Accepts a number (treated as pixels) or a string with units.
|
|
62
|
+
*
|
|
63
|
+
* @default '600px'
|
|
64
|
+
*/
|
|
65
|
+
width: {
|
|
66
|
+
type: [String, Number],
|
|
67
|
+
default: '600px',
|
|
68
|
+
},
|
|
69
|
+
/**
|
|
70
|
+
* Height of the shape. Ignored for `line`.
|
|
71
|
+
*
|
|
72
|
+
* When not set, the shape auto-sizes to fit its content.
|
|
73
|
+
*/
|
|
74
|
+
height: {
|
|
75
|
+
type: [String, Number],
|
|
76
|
+
default: null,
|
|
77
|
+
},
|
|
78
|
+
/**
|
|
79
|
+
* VML fill type.
|
|
80
|
+
*
|
|
81
|
+
* - `solid` — solid color fill (default in VML when omitted)
|
|
82
|
+
* - `gradient` — linear gradient
|
|
83
|
+
* - `gradientradial` — radial gradient
|
|
84
|
+
* - `tile` — repeat image to fill
|
|
85
|
+
* - `pattern` — tile at original size
|
|
86
|
+
* - `frame` — scale image to fill
|
|
87
|
+
*
|
|
88
|
+
* Only emitted on `<v:fill>` when set.
|
|
89
|
+
*/
|
|
90
|
+
type: {
|
|
91
|
+
type: String as PropType<'solid' | 'gradient' | 'gradientradial' | 'tile' | 'pattern' | 'frame'>,
|
|
92
|
+
},
|
|
93
|
+
/**
|
|
94
|
+
* URL of a fill image.
|
|
95
|
+
*
|
|
96
|
+
* When set, a `<v:fill>` child is emitted with this `src`.
|
|
97
|
+
*/
|
|
98
|
+
src: {
|
|
99
|
+
type: String,
|
|
100
|
+
},
|
|
101
|
+
/**
|
|
102
|
+
* Primary fill color on `<v:fill>` (start color for gradients).
|
|
103
|
+
*
|
|
104
|
+
* @example '#3b82f6'
|
|
105
|
+
*/
|
|
106
|
+
color: {
|
|
107
|
+
type: String,
|
|
108
|
+
},
|
|
109
|
+
/**
|
|
110
|
+
* Secondary fill color for gradient fills.
|
|
111
|
+
*
|
|
112
|
+
* @example '#1d4ed8'
|
|
113
|
+
*/
|
|
114
|
+
color2: {
|
|
115
|
+
type: String,
|
|
116
|
+
},
|
|
117
|
+
/**
|
|
118
|
+
* Gradient direction in degrees (0–360).
|
|
119
|
+
*
|
|
120
|
+
* @example 90
|
|
121
|
+
*/
|
|
122
|
+
angle: {
|
|
123
|
+
type: [String, Number],
|
|
124
|
+
},
|
|
125
|
+
/**
|
|
126
|
+
* Gradient midpoint (0–100, percentage of distance from start).
|
|
127
|
+
*
|
|
128
|
+
* @example 50
|
|
129
|
+
*/
|
|
130
|
+
focus: {
|
|
131
|
+
type: [String, Number],
|
|
132
|
+
},
|
|
133
|
+
/**
|
|
134
|
+
* Radial gradient focus size as `"x,y"` fractions.
|
|
135
|
+
*
|
|
136
|
+
* @example '0,0'
|
|
137
|
+
*/
|
|
138
|
+
focussize: {
|
|
139
|
+
type: String,
|
|
140
|
+
validator: (v: string) => /^-?[\d.]+,-?[\d.]+$/.test(v.replace(/\s/g, '')),
|
|
141
|
+
},
|
|
142
|
+
/**
|
|
143
|
+
* Radial gradient focus position as `"x,y"` fractions.
|
|
144
|
+
*
|
|
145
|
+
* @example '0.5,0.5'
|
|
146
|
+
*/
|
|
147
|
+
focusposition: {
|
|
148
|
+
type: String,
|
|
149
|
+
validator: (v: string) => /^-?[\d.]+,-?[\d.]+$/.test(v.replace(/\s/g, '')),
|
|
150
|
+
},
|
|
151
|
+
/**
|
|
152
|
+
* Comma-separated dimensions for the fill image.
|
|
153
|
+
*
|
|
154
|
+
* @example '300px,200px'
|
|
155
|
+
*/
|
|
156
|
+
sizes: {
|
|
157
|
+
type: String,
|
|
158
|
+
validator: (v: string) => /^[\d.]+(px|%|em|rem)?(,[\d.]+(px|%|em|rem)?)+$/.test(v.replace(/\s/g, '')),
|
|
159
|
+
},
|
|
160
|
+
/**
|
|
161
|
+
* Fill origin offset as comma-separated fractional values.
|
|
162
|
+
*
|
|
163
|
+
* Overridden by `backgroundPosition` if both are set.
|
|
164
|
+
*
|
|
165
|
+
* @example '-0.5,-0.5'
|
|
166
|
+
*/
|
|
167
|
+
origin: {
|
|
168
|
+
type: String,
|
|
169
|
+
validator: (v: string) => /^-?[\d.]+(,-?[\d.]+)+$/.test(v.replace(/\s/g, '')),
|
|
170
|
+
},
|
|
171
|
+
/**
|
|
172
|
+
* Fill position offset as comma-separated fractional values.
|
|
173
|
+
*
|
|
174
|
+
* Overridden by `backgroundPosition` if both are set.
|
|
175
|
+
*
|
|
176
|
+
* @example '0.5,0.5'
|
|
177
|
+
*/
|
|
178
|
+
position: {
|
|
179
|
+
type: String,
|
|
180
|
+
validator: (v: string) => /^-?[\d.]+(,-?[\d.]+)+$/.test(v.replace(/\s/g, '')),
|
|
181
|
+
},
|
|
182
|
+
/**
|
|
183
|
+
* Convenience for image positioning. Maps to VML `origin` / `position`.
|
|
184
|
+
*
|
|
185
|
+
* First value is vertical (`top` | `center` | `bottom`).
|
|
186
|
+
* Second value is horizontal (`left` | `center` | `right`).
|
|
187
|
+
*
|
|
188
|
+
* @example 'center,center'
|
|
189
|
+
*/
|
|
190
|
+
backgroundPosition: {
|
|
191
|
+
type: String as PropType<
|
|
192
|
+
| 'top,left' | 'top,center' | 'top,right'
|
|
193
|
+
| 'center,left' | 'center,center' | 'center,right'
|
|
194
|
+
| 'bottom,left' | 'bottom,center' | 'bottom,right'
|
|
195
|
+
>,
|
|
196
|
+
validator: (v: string) => /^(top|center|bottom),(left|center|right)$/.test(v.replace(/\s/g, '')),
|
|
197
|
+
},
|
|
198
|
+
/**
|
|
199
|
+
* Aspect ratio constraint for the fill image.
|
|
200
|
+
*
|
|
201
|
+
* - `atleast` — image at least as large as the shape
|
|
202
|
+
* - `atmost` — image at most as large as the shape
|
|
203
|
+
*/
|
|
204
|
+
aspect: {
|
|
205
|
+
type: String as PropType<'atleast' | 'atmost'>,
|
|
206
|
+
},
|
|
207
|
+
/**
|
|
208
|
+
* Text box inset (padding) as `top,right,bottom,left`.
|
|
209
|
+
*
|
|
210
|
+
* @default '0,0,0,0'
|
|
211
|
+
*/
|
|
212
|
+
inset: {
|
|
213
|
+
type: String,
|
|
214
|
+
default: '0,0,0,0',
|
|
215
|
+
},
|
|
216
|
+
/**
|
|
217
|
+
* Whether the shape has a visible border.
|
|
218
|
+
*
|
|
219
|
+
* @default false (true for `shape="line"`)
|
|
220
|
+
*/
|
|
221
|
+
stroke: {
|
|
222
|
+
type: [Boolean, String],
|
|
223
|
+
default: null,
|
|
224
|
+
},
|
|
225
|
+
/**
|
|
226
|
+
* Border color. Setting this enables `stroke` automatically.
|
|
227
|
+
*
|
|
228
|
+
* @example '#000000'
|
|
229
|
+
*/
|
|
230
|
+
strokecolor: {
|
|
231
|
+
type: String,
|
|
232
|
+
},
|
|
233
|
+
/**
|
|
234
|
+
* Whether the shape has a fill.
|
|
235
|
+
*
|
|
236
|
+
* @default true (false for `shape="line"`)
|
|
237
|
+
*/
|
|
238
|
+
fill: {
|
|
239
|
+
type: [Boolean, String],
|
|
240
|
+
default: null,
|
|
241
|
+
},
|
|
242
|
+
/**
|
|
243
|
+
* Fallback fill color on the shape element itself.
|
|
244
|
+
*
|
|
245
|
+
* Rendered when no `<v:fill>` child is emitted or the fill image
|
|
246
|
+
* cannot be loaded.
|
|
247
|
+
*
|
|
248
|
+
* @example '#3b82f6'
|
|
249
|
+
*/
|
|
250
|
+
fillcolor: {
|
|
251
|
+
type: String,
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
setup(props, { slots }) {
|
|
255
|
+
const backgroundPositionMap: Record<string, string> = {
|
|
256
|
+
'top,left': '-0.5,-0.5',
|
|
257
|
+
'top,center': '0,-0.5',
|
|
258
|
+
'top,right': '0.5,-0.5',
|
|
259
|
+
'center,left': '-0.5,0',
|
|
260
|
+
'center,center': '0,0',
|
|
261
|
+
'center,right': '0.5,0',
|
|
262
|
+
'bottom,left': '-0.5,0.5',
|
|
263
|
+
'bottom,center': '0,0.5',
|
|
264
|
+
'bottom,right': '0.5,0.5',
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const resolvedOrigin = computed(() => props.origin ?? (props.backgroundPosition ? backgroundPositionMap[props.backgroundPosition.replace(/\s/g, '')] : undefined))
|
|
268
|
+
const resolvedPosition = computed(() => props.position ?? (props.backgroundPosition ? backgroundPositionMap[props.backgroundPosition.replace(/\s/g, '')] : undefined))
|
|
269
|
+
|
|
270
|
+
const hasFillChild = computed(() => {
|
|
271
|
+
return props.type !== undefined
|
|
272
|
+
|| props.src !== undefined
|
|
273
|
+
|| props.color !== undefined
|
|
274
|
+
|| props.color2 !== undefined
|
|
275
|
+
|| props.angle !== undefined
|
|
276
|
+
|| props.focus !== undefined
|
|
277
|
+
|| props.focussize !== undefined
|
|
278
|
+
|| props.focusposition !== undefined
|
|
279
|
+
|| props.sizes !== undefined
|
|
280
|
+
|| props.aspect !== undefined
|
|
281
|
+
|| resolvedOrigin.value !== undefined
|
|
282
|
+
|| resolvedPosition.value !== undefined
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
const before = computed(() => {
|
|
286
|
+
const isLine = props.shape === 'line'
|
|
287
|
+
const element = `v:${props.shape}`
|
|
288
|
+
|
|
289
|
+
const toBool = (v: boolean | string) => v === true || v === 'true' ? 'true' : 'false'
|
|
290
|
+
|
|
291
|
+
const defaultFill = isLine ? false : true
|
|
292
|
+
const defaultStroke = isLine ? true : false
|
|
293
|
+
const fillResolved = props.fill === null ? defaultFill : props.fill
|
|
294
|
+
const strokeResolved = props.stroke === null ? defaultStroke : props.stroke
|
|
295
|
+
|
|
296
|
+
const styleParts: string[] = []
|
|
297
|
+
if (!isLine) {
|
|
298
|
+
styleParts.push(`width: ${normalizeToPixels(props.width)}`)
|
|
299
|
+
if (props.height) styleParts.push(`height: ${normalizeToPixels(props.height)}`)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const shapeAttrs = [
|
|
303
|
+
`fill="${props.fillcolor ? 'true' : toBool(fillResolved)}"`,
|
|
304
|
+
`stroke="${props.strokecolor ? 'true' : toBool(strokeResolved)}"`,
|
|
305
|
+
styleParts.length ? `style="${styleParts.join('; ')};"` : '',
|
|
306
|
+
props.strokecolor ? `strokecolor="${props.strokecolor}"` : '',
|
|
307
|
+
props.fillcolor ? `fillcolor="${props.fillcolor}"` : '',
|
|
308
|
+
props.shape === 'roundrect' && props.arcsize !== undefined ? `arcsize="${props.arcsize}"` : '',
|
|
309
|
+
isLine && props.from ? `from="${props.from}"` : '',
|
|
310
|
+
isLine && props.to ? `to="${props.to}"` : '',
|
|
311
|
+
].filter(Boolean).join(' ')
|
|
312
|
+
|
|
313
|
+
const fillAttrs = hasFillChild.value
|
|
314
|
+
? [
|
|
315
|
+
props.type ? `type="${props.type}"` : '',
|
|
316
|
+
props.src ? `src="${props.src}"` : '',
|
|
317
|
+
props.color ? `color="${props.color}"` : '',
|
|
318
|
+
props.color2 ? `color2="${props.color2}"` : '',
|
|
319
|
+
props.angle !== undefined ? `angle="${props.angle}"` : '',
|
|
320
|
+
props.focus !== undefined ? `focus="${props.focus}"` : '',
|
|
321
|
+
props.focussize ? `focussize="${props.focussize}"` : '',
|
|
322
|
+
props.focusposition ? `focusposition="${props.focusposition}"` : '',
|
|
323
|
+
props.sizes ? `sizes="${props.sizes}"` : '',
|
|
324
|
+
props.aspect ? `aspect="${props.aspect}"` : '',
|
|
325
|
+
resolvedOrigin.value ? `origin="${resolvedOrigin.value}"` : '',
|
|
326
|
+
resolvedPosition.value ? `position="${resolvedPosition.value}"` : '',
|
|
327
|
+
].filter(Boolean).join(' ')
|
|
328
|
+
: ''
|
|
329
|
+
|
|
330
|
+
const lines = [
|
|
331
|
+
`<!--[if mso]>`,
|
|
332
|
+
`<${element} xmlns:v="urn:schemas-microsoft-com:vml" ${shapeAttrs}>`,
|
|
333
|
+
]
|
|
334
|
+
if (hasFillChild.value) {
|
|
335
|
+
lines.push(`<v:fill ${fillAttrs} />`)
|
|
336
|
+
}
|
|
337
|
+
lines.push(`<v:textbox inset="${props.inset}" style="mso-fit-shape-to-text: true">`)
|
|
338
|
+
lines.push(`<div><![endif]-->`)
|
|
339
|
+
return lines.join('')
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
const after = computed(() => {
|
|
343
|
+
const element = `v:${props.shape}`
|
|
344
|
+
return `<!--[if mso]></div></v:textbox></${element}><![endif]-->`
|
|
345
|
+
})
|
|
346
|
+
|
|
347
|
+
return () => [
|
|
348
|
+
createStaticVNode(before.value, 1),
|
|
349
|
+
slots.default?.(),
|
|
350
|
+
createStaticVNode(after.value, 1),
|
|
351
|
+
]
|
|
352
|
+
},
|
|
353
|
+
}
|
|
354
|
+
</script>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","names":[],"sources":["../../src/components/utils.ts"],"mappings":";iBAAgB,iBAAA,CAAkB,
|
|
1
|
+
{"version":3,"file":"utils.d.ts","names":[],"sources":["../../src/components/utils.ts"],"mappings":";iBAAgB,iBAAA,CAAkB,KAAsB;AAAxD;;;;AAAwD;AAiBxD;;;AAjBA,iBAiBgB,MAAA,CAAO,MAAc;AAAA,iBAKrB,eAAA,CAAgB,QAAgB;AAAA,iBAQhC,eAAA,CAAgB,QAAgB;AAAA,iBAIhC,gBAAA,CAAiB,QAAgB;AAAA,iBAQjC,gBAAA,CAAiB,QAAgB;;AApBD;AAQhD;;;;cAsBa,mBAAA;EAAA,eAGH,kBAAA;EAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","names":[],"sources":["../../src/components/utils.ts"],"sourcesContent":["export function normalizeToPixels(value: string | number): string {\n if (typeof value === 'number' || Number.isFinite(Number(value))) {\n return `${value}px`\n }\n return value\n}\n\nconst counters: Record<string, number> = {}\n\n/**\n * Module-scoped sequential ID generator. Used by components to mint\n * unique marker ids (e.g. `c1`, `c2`) for the post-render transformer.\n *\n * Must live here (not inside `<script setup>`) because Vue compiles\n * `<script setup>` into the component's `setup()` function — any\n * `let counter = 0` there resets per instance, causing id collisions.\n */\nexport function nextId(prefix: string): string {\n counters[prefix] = (counters[prefix] ?? 0) + 1\n return `${prefix}${counters[prefix]}`\n}\n\nexport function hasWidthUtility(classStr: string): boolean {\n return classStr.split(/\\s+/).some((c) => {\n const utility = c.split(':').pop() ?? ''\n const clean = utility.replace(/^!/, '')\n return /^(w-|max-w-|min-w-)/.test(clean)\n })\n}\n\nexport function hasWidthInStyle(styleStr: string): boolean {\n return /(?:^|;\\s*)(?:max-width|width)\\s*:/i.test(styleStr)\n}\n\nexport function hasHeightUtility(classStr: string): boolean {\n return classStr.split(/\\s+/).some((c) => {\n const utility = c.split(':').pop() ?? ''\n const clean = utility.replace(/^!/, '')\n return /^(h-|max-h-|min-h-)/.test(clean)\n })\n}\n\nexport function hasHeightInStyle(styleStr: string): boolean {\n return /(?:^|;\\s*)(?:max-height|height)\\s*:/i.test(styleStr)\n}\n\n/**\n * Shared prop for components that emit MSO/VML fallback markup. The\n * `null` default acts as the \"unset\" sentinel — `useOutlookFallback`\n * treats `null` as inherit-from-ancestor (root default `true`),\n * letting users override per-component without losing inheritance.\n */\nexport const outlookFallbackProp = {\n type: Boolean,\n default: null,\n} as const\n\n"],"mappings":";AAAA,SAAgB,kBAAkB,OAAgC;CAChE,IAAI,OAAO,UAAU,YAAY,OAAO,SAAS,OAAO,
|
|
1
|
+
{"version":3,"file":"utils.js","names":[],"sources":["../../src/components/utils.ts"],"sourcesContent":["export function normalizeToPixels(value: string | number): string {\n if (typeof value === 'number' || Number.isFinite(Number(value))) {\n return `${value}px`\n }\n return value\n}\n\nconst counters: Record<string, number> = {}\n\n/**\n * Module-scoped sequential ID generator. Used by components to mint\n * unique marker ids (e.g. `c1`, `c2`) for the post-render transformer.\n *\n * Must live here (not inside `<script setup>`) because Vue compiles\n * `<script setup>` into the component's `setup()` function — any\n * `let counter = 0` there resets per instance, causing id collisions.\n */\nexport function nextId(prefix: string): string {\n counters[prefix] = (counters[prefix] ?? 0) + 1\n return `${prefix}${counters[prefix]}`\n}\n\nexport function hasWidthUtility(classStr: string): boolean {\n return classStr.split(/\\s+/).some((c) => {\n const utility = c.split(':').pop() ?? ''\n const clean = utility.replace(/^!/, '')\n return /^(w-|max-w-|min-w-)/.test(clean)\n })\n}\n\nexport function hasWidthInStyle(styleStr: string): boolean {\n return /(?:^|;\\s*)(?:max-width|width)\\s*:/i.test(styleStr)\n}\n\nexport function hasHeightUtility(classStr: string): boolean {\n return classStr.split(/\\s+/).some((c) => {\n const utility = c.split(':').pop() ?? ''\n const clean = utility.replace(/^!/, '')\n return /^(h-|max-h-|min-h-)/.test(clean)\n })\n}\n\nexport function hasHeightInStyle(styleStr: string): boolean {\n return /(?:^|;\\s*)(?:max-height|height)\\s*:/i.test(styleStr)\n}\n\n/**\n * Shared prop for components that emit MSO/VML fallback markup. The\n * `null` default acts as the \"unset\" sentinel — `useOutlookFallback`\n * treats `null` as inherit-from-ancestor (root default `true`),\n * letting users override per-component without losing inheritance.\n */\nexport const outlookFallbackProp = {\n type: Boolean,\n default: null,\n} as const\n\n"],"mappings":";AAAA,SAAgB,kBAAkB,OAAgC;CAChE,IAAI,OAAO,UAAU,YAAY,OAAO,SAAS,OAAO,KAAK,CAAC,GAC5D,OAAO,GAAG,MAAM;CAElB,OAAO;AACT;AAEA,MAAM,WAAmC,CAAC;;;;;;;;;AAU1C,SAAgB,OAAO,QAAwB;CAC7C,SAAS,WAAW,SAAS,WAAW,KAAK;CAC7C,OAAO,GAAG,SAAS,SAAS;AAC9B;AAEA,SAAgB,gBAAgB,UAA2B;CACzD,OAAO,SAAS,MAAM,KAAK,EAAE,MAAM,MAAM;EAEvC,MAAM,SADU,EAAE,MAAM,GAAG,EAAE,IAAI,KAAK,IAChB,QAAQ,MAAM,EAAE;EACtC,OAAO,sBAAsB,KAAK,KAAK;CACzC,CAAC;AACH;AAEA,SAAgB,gBAAgB,UAA2B;CACzD,OAAO,qCAAqC,KAAK,QAAQ;AAC3D;AAEA,SAAgB,iBAAiB,UAA2B;CAC1D,OAAO,SAAS,MAAM,KAAK,EAAE,MAAM,MAAM;EAEvC,MAAM,SADU,EAAE,MAAM,GAAG,EAAE,IAAI,KAAK,IAChB,QAAQ,MAAM,EAAE;EACtC,OAAO,sBAAsB,KAAK,KAAK;CACzC,CAAC;AACH;AAEA,SAAgB,iBAAiB,UAA2B;CAC1D,OAAO,uCAAuC,KAAK,QAAQ;AAC7D;;;;;;;AAQA,MAAa,sBAAsB;CACjC,MAAM;CACN,SAAS;AACX"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defineConfig.js","names":[],"sources":["../../src/composables/defineConfig.ts"],"sourcesContent":["import { getCurrentInstance, inject, provide } from 'vue'\nimport { createDefu } from 'defu'\nimport { MaizzleConfigKey } from './useConfig.ts'\nimport { RenderContextKey } from './renderContext.ts'\nimport type { MaizzleConfig } from '../types/index.ts'\n\nconst merge = createDefu((obj, key, value) => {\n if (Array.isArray(obj[key])) {\n obj[key] = value\n return true\n }\n})\n\n/**\n * Define Maizzle config.\n *\n * In maizzle.config.ts: typed identity function, returns the config as-is\n * In Vue SFC `<script setup>`: merges with the global config and provides\n * the result to child components via `useConfig()`\n */\nexport function defineConfig(data: Partial<MaizzleConfig> = {}): MaizzleConfig {\n // Inside a Vue SFC — merge with global config and provide to children\n if (getCurrentInstance()) {\n const globalConfig = inject(MaizzleConfigKey, {} as MaizzleConfig)\n const merged = merge(data, globalConfig) as MaizzleConfig\n\n const ctx = inject(RenderContextKey)\n if (ctx) ctx.sfcConfig = merged\n\n provide(MaizzleConfigKey, merged)\n\n return merged\n }\n\n // Outside Vue (maizzle.config.ts) — just return the config\n return data as MaizzleConfig\n}\n"],"mappings":";;;;;AAMA,MAAM,QAAQ,YAAY,KAAK,KAAK,UAAU;CAC5C,IAAI,MAAM,QAAQ,IAAI,
|
|
1
|
+
{"version":3,"file":"defineConfig.js","names":[],"sources":["../../src/composables/defineConfig.ts"],"sourcesContent":["import { getCurrentInstance, inject, provide } from 'vue'\nimport { createDefu } from 'defu'\nimport { MaizzleConfigKey } from './useConfig.ts'\nimport { RenderContextKey } from './renderContext.ts'\nimport type { MaizzleConfig } from '../types/index.ts'\n\nconst merge = createDefu((obj, key, value) => {\n if (Array.isArray(obj[key])) {\n obj[key] = value\n return true\n }\n})\n\n/**\n * Define Maizzle config.\n *\n * In maizzle.config.ts: typed identity function, returns the config as-is\n * In Vue SFC `<script setup>`: merges with the global config and provides\n * the result to child components via `useConfig()`\n */\nexport function defineConfig(data: Partial<MaizzleConfig> = {}): MaizzleConfig {\n // Inside a Vue SFC — merge with global config and provide to children\n if (getCurrentInstance()) {\n const globalConfig = inject(MaizzleConfigKey, {} as MaizzleConfig)\n const merged = merge(data, globalConfig) as MaizzleConfig\n\n const ctx = inject(RenderContextKey)\n if (ctx) ctx.sfcConfig = merged\n\n provide(MaizzleConfigKey, merged)\n\n return merged\n }\n\n // Outside Vue (maizzle.config.ts) — just return the config\n return data as MaizzleConfig\n}\n"],"mappings":";;;;;AAMA,MAAM,QAAQ,YAAY,KAAK,KAAK,UAAU;CAC5C,IAAI,MAAM,QAAQ,IAAI,IAAI,GAAG;EAC3B,IAAI,OAAO;EACX,OAAO;CACT;AACF,CAAC;;;;;;;;AASD,SAAgB,aAAa,OAA+B,CAAC,GAAkB;CAE7E,IAAI,mBAAmB,GAAG;EAExB,MAAM,SAAS,MAAM,MADA,OAAO,kBAAkB,CAAC,CACT,CAAC;EAEvC,MAAM,MAAM,OAAO,gBAAgB;EACnC,IAAI,KAAK,IAAI,YAAY;EAEzB,QAAQ,kBAAkB,MAAM;EAEhC,OAAO;CACT;CAGA,OAAO;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderContext.d.ts","names":[],"sources":["../../src/composables/renderContext.ts"],"mappings":";;;;;;UAKiB,gBAAA;EACf,MAAA;EACA,IAAA;EACA,WAAA;EACA,GAAA;AAAA;AAAA,UAGe,aAAA;EACf,EAAA;EANA;EAQA,
|
|
1
|
+
{"version":3,"file":"renderContext.d.ts","names":[],"sources":["../../src/composables/renderContext.ts"],"mappings":";;;;;;UAKiB,gBAAA;EACf,MAAA;EACA,IAAA;EACA,WAAA;EACA,GAAA;AAAA;AAAA,UAGe,aAAA;EACf,EAAA;EANA;EAQA,GAAG;AAAA;AAAA,UAGY,aAAA;EACf,OAAA;EACA,SAAA;IAAc,IAAA;IAAc,WAAA;EAAA;EAC5B,SAAA,GAAY,aAAA;EACZ,gBAAA,EAAkB,KAAA;IAAQ,IAAA,EAAM,SAAA;IAAW,OAAA,EAAS,QAAA,CAAS,SAAA;EAAA;EAC7D,SAAA,GAAY,mBAAA;EACZ,KAAA,GAAQ,gBAAA;EACR,cAAA,GAAiB,aAAA;AAAA;AAAA,cAGN,gBAAA,EAAkB,YAAY,CAAC,aAAA"}
|