@kgalexander/mcreate 1.0.12 → 1.0.13

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.
@@ -14719,6 +14719,7 @@ import { useEffect as useEffect15, useState as useState16, useRef as useRef11 }
14719
14719
 
14720
14720
  // src/validate/helpers.ts
14721
14721
  var MERGE_FIELD_REGEX2 = /\{\{([a-zA-Z_][a-zA-Z0-9_]*)\}\}/g;
14722
+ var HREF_REGEX = /<a[^>]+href=["']([^"']+)["'][^>]*>(.*?)<\/a>/gi;
14722
14723
  function extractMergeFields(node) {
14723
14724
  const fields = [];
14724
14725
  if (!node || typeof node !== "object") return fields;
@@ -14764,6 +14765,77 @@ function extractEmptyLinks(node) {
14764
14765
  }
14765
14766
  return results;
14766
14767
  }
14768
+ var PROPERTY_TYPES = /* @__PURE__ */ new Set(["property-card", "property-card-single-two", "property-card-triple-item"]);
14769
+ function getLinkType(node) {
14770
+ if (node.type === "button") return "button";
14771
+ if (node.type === "social-item") return "social";
14772
+ if (node.type === "image") return "image";
14773
+ if (PROPERTY_TYPES.has(node.type)) return "property";
14774
+ if (node.type === "text") return "text";
14775
+ return "unknown";
14776
+ }
14777
+ function getLinkText(node) {
14778
+ if (node.type === "button") return node.data?.value?.content || "Button";
14779
+ if (node.type === "social-item") return node.data?.value?.socialType || node.attributes?.alt || "Social";
14780
+ if (node.type === "image") return node.attributes?.alt || "Image";
14781
+ if (PROPERTY_TYPES.has(node.type)) return node.attributes?.address || node.attributes?.city || "Property";
14782
+ return "";
14783
+ }
14784
+ function extractLinks(template) {
14785
+ const seen = /* @__PURE__ */ new Map();
14786
+ const results = [];
14787
+ let position = 0;
14788
+ function addLink(url, linkType, linkText) {
14789
+ if (!url || !url.trim()) return;
14790
+ if (url.startsWith("mailto:")) return;
14791
+ if (url.startsWith("data:")) return;
14792
+ const trimmed = url.trim();
14793
+ if (seen.has(trimmed)) return;
14794
+ position++;
14795
+ seen.set(trimmed, results.length);
14796
+ results.push({
14797
+ url: trimmed,
14798
+ link_type: linkType,
14799
+ link_text: linkText || "",
14800
+ link_position: position
14801
+ });
14802
+ }
14803
+ function walk(node, isCompanyFooter) {
14804
+ if (!node || typeof node !== "object") return;
14805
+ const nodeIsFooter = isCompanyFooter || node.data?.value?.isCompanyFooter === true;
14806
+ const href = node.attributes?.href;
14807
+ if (href && node.type) {
14808
+ const linkType = nodeIsFooter ? "company_brand" : getLinkType(node);
14809
+ const linkText = getLinkText(node);
14810
+ addLink(href, linkType, linkText);
14811
+ }
14812
+ if (node.type === "text") {
14813
+ const content = node.data?.value?.content;
14814
+ if (typeof content === "string") {
14815
+ HREF_REGEX.lastIndex = 0;
14816
+ let match;
14817
+ while ((match = HREF_REGEX.exec(content)) !== null) {
14818
+ const [, linkUrl, linkText] = match;
14819
+ if (linkUrl) {
14820
+ addLink(linkUrl, nodeIsFooter ? "company_brand" : "text", linkText?.replace(/<[^>]*>/g, "") || "");
14821
+ }
14822
+ }
14823
+ }
14824
+ }
14825
+ if (Array.isArray(node.children)) {
14826
+ for (const child of node.children) {
14827
+ walk(child, nodeIsFooter);
14828
+ }
14829
+ }
14830
+ if (Array.isArray(node.content)) {
14831
+ for (const page of node.content) {
14832
+ walk(page, false);
14833
+ }
14834
+ }
14835
+ }
14836
+ walk(template, false);
14837
+ return results;
14838
+ }
14767
14839
 
14768
14840
  // src/validate/index.ts
14769
14841
  var PROPERTY_CARD_TYPES = /* @__PURE__ */ new Set(["property-card", "property-card-single-two", "property-card-triple-item"]);
@@ -16186,6 +16258,7 @@ export {
16186
16258
  SidebarProvider,
16187
16259
  useSidebarContext,
16188
16260
  Textarea,
16261
+ extractLinks,
16189
16262
  validate_editor_onPreview,
16190
16263
  validate_campaign_onCreate,
16191
16264
  campaign_validation_warnings,
@@ -5,7 +5,7 @@ import {
5
5
  MAILLOW_EMAIL_EDITOR_VERSION,
6
6
  Preview,
7
7
  useEditorStore
8
- } from "./chunk-RKZFFE24.mjs";
8
+ } from "./chunk-JDECYTJI.mjs";
9
9
  export {
10
10
  Editor,
11
11
  History,
package/dist/index.d.mts CHANGED
@@ -343,6 +343,13 @@ interface PreviewValidationType {
343
343
  is_over_size_limit: boolean;
344
344
  placeholder_property_images: number;
345
345
  }
346
+ type LinkType = 'social' | 'property' | 'button' | 'text' | 'image' | 'company_brand' | 'unknown';
347
+ interface ExtractedLink {
348
+ url: string;
349
+ link_type: LinkType;
350
+ link_text: string;
351
+ link_position: number;
352
+ }
346
353
 
347
354
  /**
348
355
  * Validate a template when the user previews the template
@@ -361,6 +368,13 @@ declare function campaign_validation_warnings(): {
361
368
  missing_links: boolean;
362
369
  };
363
370
 
371
+ /**
372
+ * Recursively extract all links from template JSON.
373
+ * Link type is derived from the element type — no HTML parsing needed
374
+ * (except for inline <a> tags in text elements).
375
+ */
376
+ declare function extractLinks(template: TemplateJSON): ExtractedLink[];
377
+
364
378
  /**
365
379
  * JSON to MJML Converter
366
380
  * Converts template JSON to MJML string for rendering
@@ -380,4 +394,4 @@ interface RenderOptions {
380
394
  */
381
395
  declare function json2mjml(template: TemplateJSON, mode?: RenderMode, options?: RenderOptions): string;
382
396
 
383
- export { Editor, type ImageData, MAX_TEMPLATE_SIZE, type MergeField, type MergeFieldType, type MissingLinkType, type OnDeleteCallback, type OnDuplicateCallback, type OnExitCallback, type OnImageUploadCallback, type OnSaveCallback, type OnToastCallback, OtherFonts, type PaidLevel, type PreviewValidationType, type TemplateJSON, TemplatePage, type ToastOptions, type ToastType, campaign_validation_warnings, emailSafeFonts, json2mjml, validate_campaign_onCreate, validate_editor_onPreview };
397
+ export { Editor, type ExtractedLink, type ImageData, type LinkType, MAX_TEMPLATE_SIZE, type MergeField, type MergeFieldType, type MissingLinkType, type OnDeleteCallback, type OnDuplicateCallback, type OnExitCallback, type OnImageUploadCallback, type OnSaveCallback, type OnToastCallback, OtherFonts, type PaidLevel, type PreviewValidationType, type TemplateJSON, TemplatePage, type ToastOptions, type ToastType, campaign_validation_warnings, emailSafeFonts, extractLinks, json2mjml, validate_campaign_onCreate, validate_editor_onPreview };
package/dist/index.d.ts CHANGED
@@ -343,6 +343,13 @@ interface PreviewValidationType {
343
343
  is_over_size_limit: boolean;
344
344
  placeholder_property_images: number;
345
345
  }
346
+ type LinkType = 'social' | 'property' | 'button' | 'text' | 'image' | 'company_brand' | 'unknown';
347
+ interface ExtractedLink {
348
+ url: string;
349
+ link_type: LinkType;
350
+ link_text: string;
351
+ link_position: number;
352
+ }
346
353
 
347
354
  /**
348
355
  * Validate a template when the user previews the template
@@ -361,6 +368,13 @@ declare function campaign_validation_warnings(): {
361
368
  missing_links: boolean;
362
369
  };
363
370
 
371
+ /**
372
+ * Recursively extract all links from template JSON.
373
+ * Link type is derived from the element type — no HTML parsing needed
374
+ * (except for inline <a> tags in text elements).
375
+ */
376
+ declare function extractLinks(template: TemplateJSON): ExtractedLink[];
377
+
364
378
  /**
365
379
  * JSON to MJML Converter
366
380
  * Converts template JSON to MJML string for rendering
@@ -380,4 +394,4 @@ interface RenderOptions {
380
394
  */
381
395
  declare function json2mjml(template: TemplateJSON, mode?: RenderMode, options?: RenderOptions): string;
382
396
 
383
- export { Editor, type ImageData, MAX_TEMPLATE_SIZE, type MergeField, type MergeFieldType, type MissingLinkType, type OnDeleteCallback, type OnDuplicateCallback, type OnExitCallback, type OnImageUploadCallback, type OnSaveCallback, type OnToastCallback, OtherFonts, type PaidLevel, type PreviewValidationType, type TemplateJSON, TemplatePage, type ToastOptions, type ToastType, campaign_validation_warnings, emailSafeFonts, json2mjml, validate_campaign_onCreate, validate_editor_onPreview };
397
+ export { Editor, type ExtractedLink, type ImageData, type LinkType, MAX_TEMPLATE_SIZE, type MergeField, type MergeFieldType, type MissingLinkType, type OnDeleteCallback, type OnDuplicateCallback, type OnExitCallback, type OnImageUploadCallback, type OnSaveCallback, type OnToastCallback, OtherFonts, type PaidLevel, type PreviewValidationType, type TemplateJSON, TemplatePage, type ToastOptions, type ToastType, campaign_validation_warnings, emailSafeFonts, extractLinks, json2mjml, validate_campaign_onCreate, validate_editor_onPreview };
package/dist/index.js CHANGED
@@ -15473,11 +15473,83 @@ function extractEmptyLinks(node) {
15473
15473
  }
15474
15474
  return results;
15475
15475
  }
15476
- var MERGE_FIELD_REGEX2;
15476
+ function getLinkType(node) {
15477
+ if (node.type === "button") return "button";
15478
+ if (node.type === "social-item") return "social";
15479
+ if (node.type === "image") return "image";
15480
+ if (PROPERTY_TYPES.has(node.type)) return "property";
15481
+ if (node.type === "text") return "text";
15482
+ return "unknown";
15483
+ }
15484
+ function getLinkText(node) {
15485
+ if (node.type === "button") return node.data?.value?.content || "Button";
15486
+ if (node.type === "social-item") return node.data?.value?.socialType || node.attributes?.alt || "Social";
15487
+ if (node.type === "image") return node.attributes?.alt || "Image";
15488
+ if (PROPERTY_TYPES.has(node.type)) return node.attributes?.address || node.attributes?.city || "Property";
15489
+ return "";
15490
+ }
15491
+ function extractLinks(template) {
15492
+ const seen = /* @__PURE__ */ new Map();
15493
+ const results = [];
15494
+ let position = 0;
15495
+ function addLink(url, linkType, linkText) {
15496
+ if (!url || !url.trim()) return;
15497
+ if (url.startsWith("mailto:")) return;
15498
+ if (url.startsWith("data:")) return;
15499
+ const trimmed = url.trim();
15500
+ if (seen.has(trimmed)) return;
15501
+ position++;
15502
+ seen.set(trimmed, results.length);
15503
+ results.push({
15504
+ url: trimmed,
15505
+ link_type: linkType,
15506
+ link_text: linkText || "",
15507
+ link_position: position
15508
+ });
15509
+ }
15510
+ function walk(node, isCompanyFooter) {
15511
+ if (!node || typeof node !== "object") return;
15512
+ const nodeIsFooter = isCompanyFooter || node.data?.value?.isCompanyFooter === true;
15513
+ const href = node.attributes?.href;
15514
+ if (href && node.type) {
15515
+ const linkType = nodeIsFooter ? "company_brand" : getLinkType(node);
15516
+ const linkText = getLinkText(node);
15517
+ addLink(href, linkType, linkText);
15518
+ }
15519
+ if (node.type === "text") {
15520
+ const content = node.data?.value?.content;
15521
+ if (typeof content === "string") {
15522
+ HREF_REGEX.lastIndex = 0;
15523
+ let match;
15524
+ while ((match = HREF_REGEX.exec(content)) !== null) {
15525
+ const [, linkUrl, linkText] = match;
15526
+ if (linkUrl) {
15527
+ addLink(linkUrl, nodeIsFooter ? "company_brand" : "text", linkText?.replace(/<[^>]*>/g, "") || "");
15528
+ }
15529
+ }
15530
+ }
15531
+ }
15532
+ if (Array.isArray(node.children)) {
15533
+ for (const child of node.children) {
15534
+ walk(child, nodeIsFooter);
15535
+ }
15536
+ }
15537
+ if (Array.isArray(node.content)) {
15538
+ for (const page of node.content) {
15539
+ walk(page, false);
15540
+ }
15541
+ }
15542
+ }
15543
+ walk(template, false);
15544
+ return results;
15545
+ }
15546
+ var MERGE_FIELD_REGEX2, HREF_REGEX, PROPERTY_TYPES;
15477
15547
  var init_helpers2 = __esm({
15478
15548
  "src/validate/helpers.ts"() {
15479
15549
  "use strict";
15480
15550
  MERGE_FIELD_REGEX2 = /\{\{([a-zA-Z_][a-zA-Z0-9_]*)\}\}/g;
15551
+ HREF_REGEX = /<a[^>]+href=["']([^"']+)["'][^>]*>(.*?)<\/a>/gi;
15552
+ PROPERTY_TYPES = /* @__PURE__ */ new Set(["property-card", "property-card-single-two", "property-card-triple-item"]);
15481
15553
  }
15482
15554
  });
15483
15555
 
@@ -16921,6 +16993,7 @@ __export(index_exports, {
16921
16993
  TemplatePage: () => TemplatePage,
16922
16994
  campaign_validation_warnings: () => campaign_validation_warnings,
16923
16995
  emailSafeFonts: () => emailSafeFonts,
16996
+ extractLinks: () => extractLinks,
16924
16997
  json2mjml: () => json2mjml,
16925
16998
  validate_campaign_onCreate: () => validate_campaign_onCreate,
16926
16999
  validate_editor_onPreview: () => validate_editor_onPreview
@@ -29507,6 +29580,7 @@ function TemplatePage({
29507
29580
  init_configuration();
29508
29581
  init_fonts();
29509
29582
  init_validate();
29583
+ init_helpers2();
29510
29584
  init_json2mjml();
29511
29585
  // Annotate the CommonJS export names for ESM import in node:
29512
29586
  0 && (module.exports = {
@@ -29516,6 +29590,7 @@ init_json2mjml();
29516
29590
  TemplatePage,
29517
29591
  campaign_validation_warnings,
29518
29592
  emailSafeFonts,
29593
+ extractLinks,
29519
29594
  json2mjml,
29520
29595
  validate_campaign_onCreate,
29521
29596
  validate_editor_onPreview
package/dist/index.mjs CHANGED
@@ -51,6 +51,7 @@ import {
51
51
  campaign_validation_warnings,
52
52
  cn,
53
53
  emailSafeFonts,
54
+ extractLinks,
54
55
  formatBorder,
55
56
  getElementDisplayName,
56
57
  getParentByIdx,
@@ -64,7 +65,7 @@ import {
64
65
  useSidebarContext,
65
66
  validate_campaign_onCreate,
66
67
  validate_editor_onPreview
67
- } from "./chunk-RKZFFE24.mjs";
68
+ } from "./chunk-JDECYTJI.mjs";
68
69
 
69
70
  // src/core/editor/components/email-template-v2/header.tsx
70
71
  import { ArrowLeftIcon, CopyIcon, MegaphoneIcon, MoreHorizontalIcon, PencilIcon, SendIcon, TrashIcon } from "lucide-react";
@@ -12283,7 +12284,7 @@ function useAutoSave() {
12283
12284
  // src/core/editor/components/email-template-v2/template-page.tsx
12284
12285
  import "react-json-view-lite/dist/index.css";
12285
12286
  import { jsx as jsx75, jsxs as jsxs60 } from "react/jsx-runtime";
12286
- var Editor2 = lazy(() => import("./core-IGOHPWMU.mjs").then((module) => ({
12287
+ var Editor2 = lazy(() => import("./core-IZXIUSSS.mjs").then((module) => ({
12287
12288
  default: module.Editor
12288
12289
  })));
12289
12290
  function TemplatePage({
@@ -12369,6 +12370,7 @@ export {
12369
12370
  TemplatePage,
12370
12371
  campaign_validation_warnings,
12371
12372
  emailSafeFonts,
12373
+ extractLinks,
12372
12374
  json2mjml,
12373
12375
  validate_campaign_onCreate,
12374
12376
  validate_editor_onPreview
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kgalexander/mcreate",
3
- "version": "1.0.12",
3
+ "version": "1.0.13",
4
4
  "description": "Maillow email template editor",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",