@maildeno/editor 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +426 -0
  3. package/dist/RowPreviewLegacyComponent-CXsY4P-R.js +439 -0
  4. package/dist/RowPreviewLegacyComponent-osWbuByc.js +439 -0
  5. package/dist/RowPreviewRow-D-U1mV4v.js +107 -0
  6. package/dist/RowPreviewRow-MSc_unf3.js +107 -0
  7. package/dist/adapters/index.d.ts +7 -0
  8. package/dist/adapters/localStorageAdapter.d.ts +6 -0
  9. package/dist/adapters/types.d.ts +83 -0
  10. package/dist/baseStyles.d.ts +13 -0
  11. package/dist/blocks/canvasStyleHelpers.d.ts +17 -0
  12. package/dist/blocks/definitions/anchor.d.ts +2 -0
  13. package/dist/blocks/definitions/button.d.ts +2 -0
  14. package/dist/blocks/definitions/divider.d.ts +2 -0
  15. package/dist/blocks/definitions/heading.d.ts +2 -0
  16. package/dist/blocks/definitions/image.d.ts +2 -0
  17. package/dist/blocks/definitions/list.d.ts +2 -0
  18. package/dist/blocks/definitions/menu.d.ts +2 -0
  19. package/dist/blocks/definitions/paragraph.d.ts +2 -0
  20. package/dist/blocks/definitions/socials.d.ts +2 -0
  21. package/dist/blocks/definitions/spacer.d.ts +2 -0
  22. package/dist/blocks/definitions/video.d.ts +2 -0
  23. package/dist/blocks/index.d.ts +8 -0
  24. package/dist/blocks/registry.d.ts +23 -0
  25. package/dist/blocks/types.d.ts +73 -0
  26. package/dist/composables/emailBuilder/components/useComponentStyleEditor.d.ts +72 -0
  27. package/dist/composables/emailBuilder/components/useProductRowActions.d.ts +3 -0
  28. package/dist/composables/emailBuilder/components/useProductRows.d.ts +59 -0
  29. package/dist/composables/emailBuilder/core/config/componentConfig.d.ts +29 -0
  30. package/dist/composables/emailBuilder/core/link-tags/useLinkTagInsert.d.ts +13 -0
  31. package/dist/composables/emailBuilder/core/merge-tags/mergeTagDefinitions.d.ts +64 -0
  32. package/dist/composables/emailBuilder/core/merge-tags/resolveMergeTags.d.ts +27 -0
  33. package/dist/composables/emailBuilder/core/ui/useLayerHoverScroll.d.ts +20 -0
  34. package/dist/composables/emailBuilder/core/ui/useLayoutDrag.d.ts +85 -0
  35. package/dist/composables/emailBuilder/core/ui/useRichTextEditors.d.ts +6 -0
  36. package/dist/composables/emailBuilder/core/ui/visibilityBadgeHelpers.d.ts +22 -0
  37. package/dist/composables/emailBuilder/core/useEmailBuilder.d.ts +274 -0
  38. package/dist/composables/emailBuilder/core/useEmailBuilderDefault.d.ts +507 -0
  39. package/dist/composables/emailBuilder/core/useEmailBuilderOperations.d.ts +31 -0
  40. package/dist/composables/emailBuilder/core/useEmailBuilderVisibility.d.ts +31 -0
  41. package/dist/composables/emailBuilder/export/actions/download.d.ts +24 -0
  42. package/dist/composables/emailBuilder/export/generators/html.d.ts +3 -0
  43. package/dist/composables/emailBuilder/export/generators/mjml.d.ts +6 -0
  44. package/dist/composables/emailBuilder/export/generators/react-email.d.ts +61 -0
  45. package/dist/composables/emailBuilder/export/helpers/generatorHelpers.d.ts +53 -0
  46. package/dist/composables/emailBuilder/export/logic/espLogicWrapper.d.ts +76 -0
  47. package/dist/composables/emailBuilder/export/logic/reactLogicWrapper.d.ts +31 -0
  48. package/dist/composables/emailBuilder/export/merge-tags/mergeTagMapper.d.ts +42 -0
  49. package/dist/composables/emailBuilder/export/merge-tags/reactMergeTagMapper.d.ts +26 -0
  50. package/dist/composables/emailBuilder/export/shared/containers.d.ts +18 -0
  51. package/dist/composables/emailBuilder/export/shared/styles.d.ts +12 -0
  52. package/dist/composables/emailBuilder/export/shared/tagIdentifier.d.ts +1 -0
  53. package/dist/composables/emailBuilder/export/targets/htmlExport.d.ts +20 -0
  54. package/dist/composables/emailBuilder/export/targets/jsonExport.d.ts +28 -0
  55. package/dist/composables/emailBuilder/export/targets/mjmlExport.d.ts +19 -0
  56. package/dist/composables/emailBuilder/export/targets/reactEmailExport.d.ts +20 -0
  57. package/dist/composables/emailBuilder/export/transformers/espWrapper.d.ts +2 -0
  58. package/dist/composables/emailBuilder/export/transformers/formatting.d.ts +3 -0
  59. package/dist/composables/emailBuilder/export/transformers/minify.d.ts +33 -0
  60. package/dist/composables/emailBuilder/export/transformers/tagMapper.d.ts +29 -0
  61. package/dist/composables/emailBuilder/export/types/export.d.ts +24 -0
  62. package/dist/composables/emailBuilder/export/useEmailExportEngine.d.ts +63 -0
  63. package/dist/composables/emailBuilder/export/useEmailExportMobileStyles.d.ts +3 -0
  64. package/dist/composables/emailBuilder/export/useExportSettings.d.ts +5 -0
  65. package/dist/composables/emailBuilder/health/accessibilityChecker.d.ts +64 -0
  66. package/dist/composables/emailBuilder/health/healthIndicator.d.ts +19 -0
  67. package/dist/composables/emailBuilder/history/useEmailBuilderHistory.d.ts +26 -0
  68. package/dist/composables/emailBuilder/history/useHistory.d.ts +16 -0
  69. package/dist/composables/emailBuilder/persistence/useEmailBuilderPersistence.d.ts +38 -0
  70. package/dist/composables/emailBuilder/preview/useClientPreview.d.ts +127 -0
  71. package/dist/composables/emailBuilder/transform/pipeline/hydrate.d.ts +54 -0
  72. package/dist/composables/emailBuilder/transform/pipeline/optimize.d.ts +101 -0
  73. package/dist/composables/emailBuilder/transform/pipeline/optimizeAI.d.ts +125 -0
  74. package/dist/composables/emailBuilder/types/emailBuilder.d.ts +79 -0
  75. package/dist/composables/system/useColor.d.ts +26 -0
  76. package/dist/composables/system/useDeviceDetection.d.ts +5 -0
  77. package/dist/composables/system/useGoogleFonts.d.ts +11 -0
  78. package/dist/composables/system/useImageUpload.d.ts +23 -0
  79. package/dist/composables/system/useSavedRowsPanel.d.ts +35 -0
  80. package/dist/composables/ui/useConfirm.d.ts +33 -0
  81. package/dist/composables/ui/useInfoDialog.d.ts +22 -0
  82. package/dist/composables/ui/useTeleportTarget.d.ts +49 -0
  83. package/dist/composables/ui/useToast.d.ts +32 -0
  84. package/dist/devStyleMirror.d.ts +20 -0
  85. package/dist/documentScopedStyles.d.ts +39 -0
  86. package/dist/editor.css +2411 -0
  87. package/dist/element-DN22fZmQ.js +72000 -0
  88. package/dist/element-scoped-styles.css +2411 -0
  89. package/dist/element.d.ts +6 -0
  90. package/dist/element.js +2 -0
  91. package/dist/esp/registry.d.ts +33 -0
  92. package/dist/estree-C12ASyI-.js +7993 -0
  93. package/dist/html-DGrfikay.js +7353 -0
  94. package/dist/index.d.ts +12 -0
  95. package/dist/index.js +71955 -0
  96. package/dist/init.d.ts +85 -0
  97. package/dist/init.js +130 -0
  98. package/dist/lightDom.d.ts +30 -0
  99. package/dist/merge-tags/registry.d.ts +33 -0
  100. package/dist/shadowStyles.d.ts +8 -0
  101. package/dist/standalone-Bd2oQFm3.js +3639 -0
  102. package/dist/theme.d.ts +132 -0
  103. package/dist/typescript-D-z2dDu9.js +23190 -0
  104. package/dist/utils/generateId.d.ts +1 -0
  105. package/dist/utils/shadowDom.d.ts +30 -0
  106. package/dist/vue.runtime.esm-bundler-DwQEK7WO.js +9028 -0
  107. package/package.json +99 -0
@@ -0,0 +1,83 @@
1
+ /**
2
+ * The storage adapter interface. This is the actual product boundary — see
3
+ * the project README's "Positioning" section: the editor is MIT-licensed
4
+ * and fully functional against createLocalStorageAdapter() below, with zero
5
+ * backend. A hosted adapter (implementing the same interface against a real
6
+ * API) is a separate package, not part of this one.
7
+ *
8
+ * Deliberately does NOT cover useEmailBuilderPersistence's autosave draft —
9
+ * that's a single-slot, 30-day-TTL crash-recovery buffer that's always
10
+ * localStorage regardless of which adapter is active (explicitly designed
11
+ * to be cleared once a "real" save happens through this interface, not a
12
+ * replacement for it). Confirmed still wired and working via Header.vue's
13
+ * initFromStorage() call — untouched by this batch.
14
+ */
15
+ export interface TemplateSnapshot {
16
+ rows: Record<string, any>[];
17
+ canvasStyles: Record<string, any>;
18
+ name?: string;
19
+ tags?: string[];
20
+ updatedAt?: string;
21
+ }
22
+ /** One entry in the saved-templates list. Deliberately a summary, not a full
23
+ * TemplateSnapshot — listing shouldn't require loading every template's rows,
24
+ * which for a cloud adapter would mean fetching entire documents just to
25
+ * render a list. Selecting one calls loadTemplate(id) for the real content. */
26
+ export interface TemplateSummary {
27
+ templateId: string;
28
+ name?: string;
29
+ updatedAt?: string;
30
+ }
31
+ export interface SavedRow {
32
+ id: string;
33
+ name: string;
34
+ createdAt: string;
35
+ row: Record<string, any>;
36
+ }
37
+ export interface EditorStorageAdapter {
38
+ loadTemplate(templateId?: string): Promise<TemplateSnapshot | null>;
39
+ saveTemplate(snapshot: TemplateSnapshot, templateId?: string): Promise<{
40
+ templateId: string;
41
+ }>;
42
+ /**
43
+ * Lists saved templates for the saved-templates panel.
44
+ *
45
+ * Optional on purpose: adding it as a required method would break every
46
+ * adapter already written against this interface. When absent, the panel
47
+ * says so rather than appearing broken — the same capability-gating used
48
+ * for onSendTestEmail, where a feature that can't be backed simply isn't
49
+ * offered.
50
+ */
51
+ listTemplates(): Promise<TemplateSummary[]>;
52
+ /** Optional, and only surfaced in the panel when implemented. */
53
+ deleteTemplate(templateId: string): Promise<void>;
54
+ listSavedRows(): Promise<SavedRow[]>;
55
+ saveSavedRow(row: Record<string, any>, name: string): Promise<SavedRow | null>;
56
+ deleteSavedRow(id: string): Promise<void>;
57
+ renameSavedRow(id: string, name: string): Promise<void>;
58
+ cloneSavedRowForCanvas(id: string): Record<string, any> | null;
59
+ uploadImage(file: File): Promise<string>;
60
+ }
61
+ /**
62
+ * What a host actually passes as `storageAdapter`.
63
+ *
64
+ * Every method is optional. Anything you leave out falls back to the
65
+ * built-in localStorage adapter, so you can override only the parts you
66
+ * care about:
67
+ *
68
+ * ```ts
69
+ * // Upload images to your own storage; keep everything else local.
70
+ * init({ storageAdapter: { uploadImage: (file) => uploadToS3(file) } });
71
+ * ```
72
+ *
73
+ * Internally the editor always works with a complete `EditorStorageAdapter`
74
+ * — `provideStorageAdapter` merges your partial over the defaults — so no
75
+ * call site has to guard against a missing method.
76
+ *
77
+ * One thing worth knowing: the fallback is per-method, not per-area. If you
78
+ * implement `saveTemplate` against your own backend but leave
79
+ * `listTemplates` out, the saved-templates panel will list whatever is in
80
+ * localStorage rather than your backend. The editor warns about that
81
+ * specific mismatch in development.
82
+ */
83
+ export type PartialStorageAdapter = Partial<EditorStorageAdapter>;
@@ -0,0 +1,13 @@
1
+ /** Tailwind, the editor's own CSS and the Satoshi font, as independent CSS
2
+ * strings — each becomes its own <style> tag wherever the caller injects them. */
3
+ export declare function getBaseStyles(): string[];
4
+ /**
5
+ * Injects getBaseStyles() directly into document.head, exactly once no
6
+ * matter how many times it's called — called from EmailEditor.vue on
7
+ * every mount, so multiple <EmailEditor> instances on one page (or one
8
+ * instance unmounting and remounting) don't each inject a duplicate copy.
9
+ * Genuinely module-level, not per-component-instance state: this file is
10
+ * only ever evaluated once, the first time anything imports it, regardless
11
+ * of how many EmailEditor instances get created afterward.
12
+ */
13
+ export declare function injectBaseStylesIntoHead(): void;
@@ -0,0 +1,17 @@
1
+ import { Ref } from 'vue';
2
+ /**
3
+ * Applies mobile prop overrides when in mobile preview mode. Depends on
4
+ * previewMode, not just the component's own props — extracted verbatim
5
+ * from CanvasComponent.vue's local resolveProps.
6
+ */
7
+ export declare function resolveMobileProps(p: any, previewMode: Ref<string>): any;
8
+ /** Extracted verbatim from CanvasComponent.vue's local resolveBackground. */
9
+ export declare function resolveBackground(p: any): string;
10
+ /**
11
+ * Desktop/mobile-hide opacity class — extracted verbatim from
12
+ * CanvasComponent.vue's local hideClass computed.
13
+ */
14
+ export declare function useHideClass(componentProps: Ref<{
15
+ mobileHide?: boolean;
16
+ desktopHide?: boolean;
17
+ }>, previewMode: Ref<string>): import('vue').ComputedRef<string[]>;
@@ -0,0 +1,2 @@
1
+ import { BlockDefinition } from '../types';
2
+ export declare const anchorBlock: BlockDefinition;
@@ -0,0 +1,2 @@
1
+ import { BlockDefinition } from '../types';
2
+ export declare const buttonBlock: BlockDefinition;
@@ -0,0 +1,2 @@
1
+ import { BlockDefinition } from '../types';
2
+ export declare const dividerBlock: BlockDefinition;
@@ -0,0 +1,2 @@
1
+ import { BlockDefinition } from '../types';
2
+ export declare const headingBlock: BlockDefinition;
@@ -0,0 +1,2 @@
1
+ import { BlockDefinition } from '../types';
2
+ export declare const imageBlock: BlockDefinition;
@@ -0,0 +1,2 @@
1
+ import { BlockDefinition } from '../types';
2
+ export declare const listBlock: BlockDefinition;
@@ -0,0 +1,2 @@
1
+ import { BlockDefinition } from '../types';
2
+ export declare const menuBlock: BlockDefinition;
@@ -0,0 +1,2 @@
1
+ import { BlockDefinition } from '../types';
2
+ export declare const paragraphBlock: BlockDefinition;
@@ -0,0 +1,2 @@
1
+ import { BlockDefinition } from '../types';
2
+ export declare const socialsBlock: BlockDefinition;
@@ -0,0 +1,2 @@
1
+ import { BlockDefinition } from '../types';
2
+ export declare const spacerBlock: BlockDefinition;
@@ -0,0 +1,2 @@
1
+ import { BlockDefinition } from '../types';
2
+ export declare const videoBlock: BlockDefinition;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Registers every built-in block — all 11, matching the type list confirmed
3
+ * against the real generators back at the start of this migration
4
+ * (paragraph, heading, image, video, list, button, anchor, divider,
5
+ * spacer, menu, socials). Idempotent — safe to call multiple times (e.g.
6
+ * if more than one <EmailEditor> mounts on a page), only registers once.
7
+ */
8
+ export declare function registerBuiltInBlocks(): void;
@@ -0,0 +1,23 @@
1
+ import { BlockDefinition } from './types';
2
+ /**
3
+ * Registers a block type.
4
+ *
5
+ * ```ts
6
+ * registerBlock({
7
+ * name: "product-card",
8
+ * label: "Product Card",
9
+ * icon: "<svg …>",
10
+ * schema: { … },
11
+ * renderCanvas, renderSettings, renderEmail,
12
+ * });
13
+ * ```
14
+ *
15
+ * A two-argument form, `registerBlock(id, definition)`, is also accepted. The
16
+ * definition already carries `name`, so the separate id is redundant — but the
17
+ * built-in blocks are registered that way and external code may be too, so
18
+ * both keep working. When both are given, the explicit id wins.
19
+ */
20
+ export declare function registerBlock(definition: BlockDefinition): void;
21
+ export declare function registerBlock(id: string, definition: BlockDefinition): void;
22
+ export declare function getBlock(id: string): BlockDefinition | undefined;
23
+ export declare function getAllBlocks(): ReadonlyMap<string, BlockDefinition>;
@@ -0,0 +1,73 @@
1
+ import { Component } from 'vue';
2
+ /**
3
+ * Confirmed against the real generators (html.ts/mjml.ts/react-email.ts):
4
+ * each is a switch(type) dispatcher with no default case — an unmapped type
5
+ * renders as an empty string, silently. That's why all three export
6
+ * renderers are required, not optional-with-fallback.
7
+ *
8
+ * Confirmed against CanvasComponent.vue and ContentTab.vue: block dispatch
9
+ * actually happens in five places, not three — canvas rendering and the
10
+ * settings panel are separate v-if chains the original README draft didn't
11
+ * account for. Same "no silent gap" rule applies here for consistency:
12
+ * renderCanvas and renderSettings are required too.
13
+ */
14
+ export interface BlockRenderContext {
15
+ uid: string;
16
+ marginStyle: string;
17
+ paddingStyle: string;
18
+ escapeHtml: (s: string) => string;
19
+ escapeAttr: (s: string) => string;
20
+ safeUrl: (s: string) => string;
21
+ resolveBgCss: (gradient: any, fallback: string) => string;
22
+ resolveBgVml: (gradient: any, fallback: string) => {
23
+ vmlOpen: string;
24
+ vmlClose: string;
25
+ };
26
+ getResponsiveClasses: (desktopHide: boolean, mobileHide: boolean, suffix?: string) => string;
27
+ /** React Email target only — builds JSX style objects instead of CSS
28
+ * strings (confirmed: hand-rolled, not the real react/mjml packages). */
29
+ react: {
30
+ parseCssString: (css: string) => Record<string, string>;
31
+ parseMarginPaddingDiscrete: (margin?: any, padding?: any) => Record<string, string>;
32
+ normalizeFontFamily: (value: string) => string;
33
+ styleObj: (obj: Record<string, any>) => string;
34
+ normalizeInlineStylesToReact: (html: string) => string;
35
+ buildInlineJsx: (component: "Text" | "Heading", className: string, styleRecord: Record<string, any>, children: string, as?: string) => string;
36
+ };
37
+ }
38
+ export interface BlockDefinition {
39
+ name: string;
40
+ /**
41
+ * Shown in the sidebar tooltip and the block's info dialog. Falls back to
42
+ * `name` when omitted.
43
+ */
44
+ label?: string;
45
+ /**
46
+ * Sidebar icon. Either a raw SVG string or a Vue component.
47
+ *
48
+ * A string is the common case — paste the SVG from whatever icon set you
49
+ * use, no import or build step. It is rendered inline so it inherits
50
+ * `currentColor` and the surrounding sizing, which keeps a custom block
51
+ * visually consistent with the built-in ones.
52
+ *
53
+ * Without an icon a registered block still works, but has no sidebar
54
+ * entry — it can only be added programmatically.
55
+ */
56
+ icon?: string | Component;
57
+ /** Drives future tooling (docs, a generic settings fallback for
58
+ * third-party blocks). Built-ins point renderSettings at their existing
59
+ * bespoke panel instead of relying on this to generate one. */
60
+ schema: Record<string, unknown>;
61
+ /** Vue component, receives a single `component` prop — the block's own
62
+ * node from the tree ({ id, componentType, props, ... }). Matches the
63
+ * existing renderer pattern (ImageRenderer, AnchorRenderer, etc). */
64
+ renderCanvas: Component;
65
+ /** Vue component, no props — reads the selected component itself via
66
+ * useEmailBuilder(), same as every existing panel (ButtonPanel, etc). */
67
+ renderSettings: Component;
68
+ renderEmail: {
69
+ html: (props: any, ctx: BlockRenderContext) => string;
70
+ mjml: (props: any, ctx: BlockRenderContext) => string;
71
+ reactEmail: (props: any, ctx: BlockRenderContext) => string;
72
+ };
73
+ }
@@ -0,0 +1,72 @@
1
+ import { Ref, ComputedRef } from 'vue';
2
+ export interface GradientStop {
3
+ color: string;
4
+ position: number;
5
+ }
6
+ export interface GradientConfig {
7
+ type: "linear" | "radial";
8
+ direction: string;
9
+ colors: GradientStop[];
10
+ }
11
+ export interface BackgroundValue {
12
+ useGradient: boolean;
13
+ solid: string;
14
+ gradient: GradientConfig;
15
+ }
16
+ export type EditMode = "desktop" | "mobile";
17
+ export interface BorderProps {
18
+ width: number;
19
+ color: string;
20
+ style: "solid" | "dashed" | "dotted";
21
+ radius?: number;
22
+ }
23
+ /**
24
+ * Loose-but-honest shape for any component the style editor touches.
25
+ * Every key is optional because different component types carry different
26
+ * subsets (Image has no backgroundGradient, Anchor has no border, etc.).
27
+ *
28
+ * `mobile` is typed as required-but-nullable-fields because
29
+ * useEmailBuilderDefaults always seeds it on instantiation. Index signature
30
+ * preserves access to any key without enumerating every prop per component.
31
+ */
32
+ export interface StyledComponentProps {
33
+ mobile: Record<string, any> | null;
34
+ border?: BorderProps;
35
+ backgroundColor?: string;
36
+ backgroundGradient?: BackgroundValue | null;
37
+ desktopHide?: boolean;
38
+ mobileHide?: boolean;
39
+ [key: string]: any;
40
+ }
41
+ export interface StyledComponent {
42
+ id: string;
43
+ type: string;
44
+ props: StyledComponentProps;
45
+ }
46
+ export declare function useComponentStyleEditor(component: Ref<StyledComponent | null> | ComputedRef<StyledComponent | null>, editMode: Ref<EditMode> | ComputedRef<EditMode>): {
47
+ displayValue: (key: string) => any;
48
+ isOverridden: (key: string) => boolean;
49
+ canReset: (key: string) => boolean;
50
+ setProp: (key: string, value: any) => void;
51
+ resetProp: (key: string) => void;
52
+ incrementProp: (key: string, step?: number) => void;
53
+ decrementProp: (key: string, step?: number, min?: number) => void;
54
+ displayBorderValue: (key: keyof BorderProps) => any;
55
+ setBorderProp: (key: keyof BorderProps, value: any) => void;
56
+ incrementBorderProp: (key: keyof BorderProps) => void;
57
+ decrementBorderProp: (key: keyof BorderProps) => void;
58
+ isBorderOverridden: () => boolean;
59
+ resetBorder: () => void;
60
+ displayBackground: () => BackgroundValue;
61
+ setBackground: (value: BackgroundValue) => void;
62
+ isBackgroundOverridden: () => boolean;
63
+ resetBackground: () => void;
64
+ toggleGradient: (useGradient: boolean) => void;
65
+ setGradientColors: (colors: GradientStop[]) => void;
66
+ setGradientConfig: (config: Partial<GradientConfig>) => void;
67
+ useTransparentBg: import('vue').WritableComputedRef<boolean, boolean>;
68
+ bgColor: ComputedRef<any>;
69
+ setBgColor: (value: string) => void;
70
+ toggleDesktopHide: () => void;
71
+ toggleMobileHide: () => void;
72
+ };
@@ -0,0 +1,3 @@
1
+ export declare function useProductRowActions(): {
2
+ saveProductRowWithToast: (row: Record<string, any>, name: string) => Promise<boolean>;
3
+ };
@@ -0,0 +1,59 @@
1
+ import { SavedRow, EditorStorageAdapter } from '../../../adapters/types';
2
+ declare function createProductRowsInstance(storageAdapterInstance?: EditorStorageAdapter): {
3
+ productRows: import('vue').Ref<{
4
+ id: string;
5
+ name: string;
6
+ createdAt: string;
7
+ row: Record<string, any>;
8
+ }[], SavedRow[] | {
9
+ id: string;
10
+ name: string;
11
+ createdAt: string;
12
+ row: Record<string, any>;
13
+ }[]>;
14
+ isFetching: import('vue').Ref<boolean, boolean>;
15
+ fetchRows: () => Promise<void>;
16
+ saveProductRow: (row: Record<string, any>, name: string) => Promise<SavedRow | null>;
17
+ deleteProductRow: (entryId: string) => Promise<void>;
18
+ renameProductRow: (entryId: string, newName: string) => Promise<{
19
+ id: string;
20
+ name: string;
21
+ createdAt: string;
22
+ row: Record<string, any>;
23
+ }[]>;
24
+ cloneRowForCanvas: (entryId: string) => Record<string, any> | null;
25
+ };
26
+ type ProductRowsInstance = ReturnType<typeof createProductRowsInstance>;
27
+ /** Called once, at the EmailEditor root. storageAdapterInstance is the
28
+ * already-created return value of provideStorageAdapter() — same reasoning
29
+ * as useEmailBuilder.ts's matching parameter: inject() never sees a
30
+ * component's own provides, so useStorageAdapter() called from anywhere
31
+ * within EmailEditor.vue's own setup() can never find what
32
+ * provideStorageAdapter() (also called there) just provided. */
33
+ export declare function provideProductRows(storageAdapterInstance?: EditorStorageAdapter): {
34
+ productRows: import('vue').Ref<{
35
+ id: string;
36
+ name: string;
37
+ createdAt: string;
38
+ row: Record<string, any>;
39
+ }[], SavedRow[] | {
40
+ id: string;
41
+ name: string;
42
+ createdAt: string;
43
+ row: Record<string, any>;
44
+ }[]>;
45
+ isFetching: import('vue').Ref<boolean, boolean>;
46
+ fetchRows: () => Promise<void>;
47
+ saveProductRow: (row: Record<string, any>, name: string) => Promise<SavedRow | null>;
48
+ deleteProductRow: (entryId: string) => Promise<void>;
49
+ renameProductRow: (entryId: string, newName: string) => Promise<{
50
+ id: string;
51
+ name: string;
52
+ createdAt: string;
53
+ row: Record<string, any>;
54
+ }[]>;
55
+ cloneRowForCanvas: (entryId: string) => Record<string, any> | null;
56
+ };
57
+ /** Every other file calls this exactly as before. */
58
+ export declare const useProductRows: () => ProductRowsInstance;
59
+ export {};
@@ -0,0 +1,29 @@
1
+ import { VisibilityOperator } from '../useEmailBuilderVisibility';
2
+ export interface VisibilityRule {
3
+ tag: string;
4
+ operator: VisibilityOperator;
5
+ value: string;
6
+ }
7
+ export interface VisibilityGroup {
8
+ match: "all" | "any";
9
+ rules: VisibilityRule[];
10
+ }
11
+ export interface VisibilityConfig {
12
+ enabled: boolean;
13
+ match: "all" | "any";
14
+ rules: VisibilityRule[];
15
+ groups?: VisibilityGroup[];
16
+ }
17
+ export interface HideConfig {
18
+ desktopHide: boolean;
19
+ mobileHide: boolean;
20
+ }
21
+ export interface BaseComponentConfig extends HideConfig {
22
+ visibility: VisibilityConfig;
23
+ }
24
+ export declare const createDefaultVisibility: () => VisibilityConfig;
25
+ export declare const createDefaultHide: () => HideConfig;
26
+ export declare const createDefaultBaseConfig: () => BaseComponentConfig;
27
+ export declare const DEFAULT_VISIBILITY: Readonly<VisibilityConfig>;
28
+ export declare const DEFAULT_HIDE: Readonly<HideConfig>;
29
+ export declare const DEFAULT_BASE_CONFIG: Readonly<BaseComponentConfig>;
@@ -0,0 +1,13 @@
1
+ import { ComputedRef } from 'vue';
2
+ interface LinkComponent {
3
+ props: Record<string, any>;
4
+ }
5
+ export declare const useLinkTagInsert: (component: ComputedRef<LinkComponent | undefined>, defaultField?: string) => {
6
+ lastFocusedField: import('vue').Ref<string, string>;
7
+ trackCursor: (field: string, event: Event) => void;
8
+ handleTagInsert: ({ tag, default: fallback }: {
9
+ tag: string;
10
+ default?: string;
11
+ }, inputRefs?: Record<string, any>) => void;
12
+ };
13
+ export {};
@@ -0,0 +1,64 @@
1
+ /**
2
+ * mergeTagDefinitions.ts
3
+ * Shared utilities for merge tag resolution, detection, and export flattening.
4
+ *
5
+ * Used by:
6
+ * - EmailBuilderCanvasComponent.vue (preview resolution)
7
+ * - LinkTagTab.vue (auto-detection)
8
+ * - MergeTagTab.vue (auto-detection)
9
+ * - mergeTagMapper.ts (export transformation)
10
+ * - Build / export pipeline (flattening)
11
+ */
12
+ /**
13
+ * Extract all unique merge tag names from a string.
14
+ * Safe to call on both rich-text HTML strings and plain strings.
15
+ */
16
+ export declare const extractMergeTags: (text: string) => string[];
17
+ /**
18
+ * Extract all unique merge tags along with their inline defaults.
19
+ *
20
+ * Returns a map of tagName → fallback (empty string when no default is
21
+ * specified). When the same tag appears multiple times with different
22
+ * defaults the first-seen default wins.
23
+ *
24
+ * Used by the export pipeline to carry fallback values through to
25
+ * mergeTagMapper.transformToken().
26
+ */
27
+ export declare const extractMergeTagsWithDefaults: (text: string) => Map<string, string>;
28
+ /**
29
+ * Resolve merge tags in a plain string using the preview context.
30
+ *
31
+ * Resolution priority (mirrors RichTextEditor.vue buildPreviewHTML):
32
+ * 1. Explicit value in context → "Philip"
33
+ * 2. Inline default in tag syntax → {{ name|'Friend' }} → "Friend"
34
+ * 3. Neither set → "{{ name }}" (visually unresolved)
35
+ *
36
+ * When previewActive is false the original string is returned unchanged,
37
+ * so callers can bind this unconditionally without a v-if guard.
38
+ */
39
+ export declare const resolveString: (str: string, context: Record<string, string>, previewActive: boolean) => string;
40
+ /**
41
+ * Flatten a plain string for canonical storage — normalises {{ tag }} tokens
42
+ * to a clean, consistent `{{ tag }}` format, stripping any accidental extras.
43
+ *
44
+ * For rich-text HTML (paragraph / heading) use flattenRichTextContent()
45
+ * instead — that one handles span[data-merge] nodes produced by TipTap.
46
+ */
47
+ export declare const flattenPlainString: (str: string) => string;
48
+ /**
49
+ * Flatten TipTap-produced HTML into the canonical {{ tag }} / {{ tag|'default' }}
50
+ * format for storage. Replaces every <span data-merge="name"> with {{ name }},
51
+ * and preserves the data-merge-default attribute as the inline default so the
52
+ * fallback survives the storage round-trip and reaches transformHTML() at
53
+ * export time.
54
+ *
55
+ * Examples:
56
+ * <span data-merge="name" data-merge-default="Tony">…</span> → {{ name|'Tony' }}
57
+ * <span data-merge="name">…</span> → {{ name }}
58
+ *
59
+ * This is the STORAGE flattener — it produces canonical {{ }} tokens.
60
+ * For ESP-specific export use mergeTagMapper.transformHTML() instead.
61
+ *
62
+ * Safe to call in SSR — uses a plain regex, no DOMParser.
63
+ */
64
+ export declare const flattenRichTextContent: (html: string) => string;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Substitutes every {{ tag }} placeholder found in `source` with the
3
+ * corresponding value from `context`.
4
+ *
5
+ * Resolution order for each placeholder:
6
+ * 1. Use the context value when the tag key is present and non-empty.
7
+ * 2. Fall back to the pipe-default when defined: {{ tag|'Fallback' }}
8
+ * 3. Leave the raw placeholder as-is so it stays visible in the output.
9
+ *
10
+ * @param source - The raw string that may contain {{ tag }} placeholders.
11
+ * @param context - Key→value map built by the tab component (keys already
12
+ * normalised with `normaliseKey`).
13
+ * @param active - When false the function is a no-op and returns `source`
14
+ * unchanged. Pass `mergeTagPreviewActive.value` /
15
+ * `linkTagPreviewActive.value` directly.
16
+ */
17
+ export declare const resolveTags: (source: string, context: Record<string, string>, active: boolean) => string;
18
+ /**
19
+ * Convenience overload that accepts the full context object and both active
20
+ * flags, applying merge tags first and link tags second.
21
+ *
22
+ * This ordering matters because merge tags appear inside rich-text HTML
23
+ * (paragraph / heading content) while link tags typically live in href
24
+ * attributes and anchor text — there is no semantic overlap, but doing
25
+ * merge → link keeps the substitution deterministic.
26
+ */
27
+ export declare const resolveAllTags: (source: string, mergeContext: Record<string, string>, mergeActive: boolean, linkContext: Record<string, string>, linkActive: boolean) => string;
@@ -0,0 +1,20 @@
1
+ import { Ref } from 'vue';
2
+ export declare function useLayerHoverScroll(layerHoveredId: Ref<string | null>, options?: {
3
+ canvasSelector?: string;
4
+ behavior?: ScrollBehavior;
5
+ delay?: number;
6
+ /**
7
+ * Margin (in px) from the viewport edges within which the target is
8
+ * considered "comfortably visible" and no scroll is needed. Default 80 px
9
+ * gives breathing room on both sides without being so generous that
10
+ * elements land right against an edge.
11
+ */
12
+ inViewMargin?: number;
13
+ /**
14
+ * If the target is more than this many pixels from the current scroll
15
+ * position, use instant jump instead of smooth scroll. Default 600 px
16
+ * covers roughly half a typical viewport — anything farther than that
17
+ * should teleport, not animate.
18
+ */
19
+ instantThreshold?: number;
20
+ }): void;
@@ -0,0 +1,85 @@
1
+ export type LayoutDragPayload = {
2
+ type: "row";
3
+ columns: number | number[];
4
+ } | {
5
+ type: "spacer";
6
+ } | {
7
+ type: "product-row";
8
+ entryId: string;
9
+ }
10
+ /** Drop a nested row inside an existing column (child insertion) */
11
+ | {
12
+ type: "nested-row";
13
+ columns: number | number[];
14
+ targetColumnId: string;
15
+ targetIndex: number;
16
+ };
17
+ type LayoutDragInstance = ReturnType<typeof createLayoutDragInstance>;
18
+ /** Called once, at the EmailEditor root. */
19
+ export declare function provideLayoutDrag(): {
20
+ isLayoutDragActive: import('vue').Ref<boolean, boolean>;
21
+ layoutDragPayload: import('vue').Ref<{
22
+ type: "row";
23
+ columns: number | number[];
24
+ } | {
25
+ type: "spacer";
26
+ } | {
27
+ type: "product-row";
28
+ entryId: string;
29
+ } | {
30
+ type: "nested-row";
31
+ columns: number | number[];
32
+ targetColumnId: string;
33
+ targetIndex: number;
34
+ } | null, LayoutDragPayload | {
35
+ type: "row";
36
+ columns: number | number[];
37
+ } | {
38
+ type: "spacer";
39
+ } | {
40
+ type: "product-row";
41
+ entryId: string;
42
+ } | {
43
+ type: "nested-row";
44
+ columns: number | number[];
45
+ targetColumnId: string;
46
+ targetIndex: number;
47
+ } | null>;
48
+ startLayoutDrag: (e: DragEvent, payload: LayoutDragPayload) => void;
49
+ endLayoutDrag: () => void;
50
+ };
51
+ /** Every other file calls this exactly as before. */
52
+ export declare const useLayoutDrag: () => LayoutDragInstance;
53
+ declare function createLayoutDragInstance(): {
54
+ isLayoutDragActive: import('vue').Ref<boolean, boolean>;
55
+ layoutDragPayload: import('vue').Ref<{
56
+ type: "row";
57
+ columns: number | number[];
58
+ } | {
59
+ type: "spacer";
60
+ } | {
61
+ type: "product-row";
62
+ entryId: string;
63
+ } | {
64
+ type: "nested-row";
65
+ columns: number | number[];
66
+ targetColumnId: string;
67
+ targetIndex: number;
68
+ } | null, LayoutDragPayload | {
69
+ type: "row";
70
+ columns: number | number[];
71
+ } | {
72
+ type: "spacer";
73
+ } | {
74
+ type: "product-row";
75
+ entryId: string;
76
+ } | {
77
+ type: "nested-row";
78
+ columns: number | number[];
79
+ targetColumnId: string;
80
+ targetIndex: number;
81
+ } | null>;
82
+ startLayoutDrag: (e: DragEvent, payload: LayoutDragPayload) => void;
83
+ endLayoutDrag: () => void;
84
+ };
85
+ export {};
@@ -0,0 +1,6 @@
1
+ import { Editor } from '@tiptap/vue-3';
2
+ export declare function useRichTextEditors(): {
3
+ registerEditor: (id: string, editor: Editor) => void;
4
+ unregisterEditor: (id: string) => void;
5
+ editorFor: (id: string | null | undefined) => Editor | undefined;
6
+ };