@deneb-ui/cli 2.0.11 → 2.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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deneb-ui/cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.13",
|
|
4
4
|
"description": "Official developer CLI for DENEB UI — Validating, scaffolding, testing, and packaging storefront templates. Created by Chamika Gayashan & Induranga Kawishwara.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"deneb": "bin/index.js",
|
|
@@ -280,6 +280,11 @@
|
|
|
280
280
|
"key": "contactHeading",
|
|
281
281
|
"type": "text",
|
|
282
282
|
"label": "Contact Column Heading"
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
"key": "instagramUrl",
|
|
286
|
+
"type": "text",
|
|
287
|
+
"label": "Instagram Profile URL"
|
|
283
288
|
}
|
|
284
289
|
]
|
|
285
290
|
}
|
|
@@ -589,6 +594,16 @@
|
|
|
589
594
|
"type": "textarea",
|
|
590
595
|
"label": "WhatsApp Section Description"
|
|
591
596
|
},
|
|
597
|
+
{
|
|
598
|
+
"key": "whatsappCtaLabel",
|
|
599
|
+
"type": "text",
|
|
600
|
+
"label": "WhatsApp CTA Button Label"
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
"key": "whatsappCtaUrl",
|
|
604
|
+
"type": "text",
|
|
605
|
+
"label": "WhatsApp CTA Link URL"
|
|
606
|
+
},
|
|
592
607
|
{
|
|
593
608
|
"key": "categoryLabel1",
|
|
594
609
|
"type": "text",
|
|
@@ -1019,7 +1034,8 @@
|
|
|
1019
1034
|
"supportLink1": "Shipping & Returns",
|
|
1020
1035
|
"supportLink2": "Size Guide",
|
|
1021
1036
|
"supportLink3": "WhatsApp Orders",
|
|
1022
|
-
"contactHeading": "Contact"
|
|
1037
|
+
"contactHeading": "Contact",
|
|
1038
|
+
"instagramUrl": "https://instagram.com"
|
|
1023
1039
|
}
|
|
1024
1040
|
},
|
|
1025
1041
|
"home": {
|
|
@@ -1080,6 +1096,8 @@
|
|
|
1080
1096
|
"whatsappLabel": "Easy Ordering",
|
|
1081
1097
|
"whatsappTitle": "Order Directly via WhatsApp",
|
|
1082
1098
|
"whatsappDesc": "Select your size, add to your bag, and send your order in one tap. Our team confirms availability within hours.",
|
|
1099
|
+
"whatsappCtaLabel": "Start a Conversation",
|
|
1100
|
+
"whatsappCtaUrl": "https://wa.me/94771234567?text=Hi%2C%20I'd%20like%20to%20order%3A%0A%0AI'm%20interested%20in%20VANTA%20footwear.%20Can%20you%20help%20me%20choose%20a%20pair%3F",
|
|
1083
1101
|
"categoryLabel1": "Performance",
|
|
1084
1102
|
"categoryLabel2": "Sneakers",
|
|
1085
1103
|
"categoryLabel3": "Lifestyle",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vanta-shoes-store",
|
|
3
|
-
"label": "
|
|
4
|
-
"description": "
|
|
5
|
-
"savedAt": "2026-09-
|
|
3
|
+
"label": "Vanta Shoes Storefront",
|
|
4
|
+
"description": "Calibrated Vanta shoes store recipe with WhatsApp CTA, Instagram social link, product grid, and hero slides.",
|
|
5
|
+
"savedAt": "2026-09-08T17:20:49.206Z",
|
|
6
6
|
"signatures": {
|
|
7
7
|
"keywords": [
|
|
8
8
|
"vanta-shoes-store",
|
|
@@ -55,6 +55,40 @@
|
|
|
55
55
|
"autoIndex": true,
|
|
56
56
|
"defaultCardPrefix": "product"
|
|
57
57
|
},
|
|
58
|
+
"actionRules": {
|
|
59
|
+
"splitActionAndLabel": true,
|
|
60
|
+
"whatsappKeywords": [
|
|
61
|
+
"whatsapp",
|
|
62
|
+
"wa.me",
|
|
63
|
+
"order via whatsapp",
|
|
64
|
+
"chat on whatsapp"
|
|
65
|
+
],
|
|
66
|
+
"actionFieldMapping": {
|
|
67
|
+
"whatsapp": {
|
|
68
|
+
"urlField": "whatsappCtaUrl",
|
|
69
|
+
"labelField": "whatsappCtaLabel"
|
|
70
|
+
},
|
|
71
|
+
"order": {
|
|
72
|
+
"urlField": "orderCtaUrl",
|
|
73
|
+
"labelField": "orderCtaLabel"
|
|
74
|
+
},
|
|
75
|
+
"promo": {
|
|
76
|
+
"urlField": "promoCtaUrl",
|
|
77
|
+
"labelField": "promoCtaLabel"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"socialRules": {
|
|
82
|
+
"platforms": [
|
|
83
|
+
"instagram",
|
|
84
|
+
"facebook",
|
|
85
|
+
"twitter",
|
|
86
|
+
"tiktok",
|
|
87
|
+
"youtube",
|
|
88
|
+
"linkedin"
|
|
89
|
+
],
|
|
90
|
+
"targetPath": "common.footer"
|
|
91
|
+
},
|
|
58
92
|
"sections": [
|
|
59
93
|
{
|
|
60
94
|
"id": "common",
|
|
@@ -255,6 +289,11 @@
|
|
|
255
289
|
"key": "contactHeading",
|
|
256
290
|
"type": "text",
|
|
257
291
|
"label": "Contact Column Heading"
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
"key": "instagramUrl",
|
|
295
|
+
"type": "text",
|
|
296
|
+
"label": "Instagram Profile URL"
|
|
258
297
|
}
|
|
259
298
|
]
|
|
260
299
|
}
|
|
@@ -564,6 +603,16 @@
|
|
|
564
603
|
"type": "textarea",
|
|
565
604
|
"label": "WhatsApp Section Description"
|
|
566
605
|
},
|
|
606
|
+
{
|
|
607
|
+
"key": "whatsappCtaLabel",
|
|
608
|
+
"type": "text",
|
|
609
|
+
"label": "WhatsApp CTA Button Label"
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
"key": "whatsappCtaUrl",
|
|
613
|
+
"type": "text",
|
|
614
|
+
"label": "WhatsApp CTA Link URL"
|
|
615
|
+
},
|
|
567
616
|
{
|
|
568
617
|
"key": "categoryLabel1",
|
|
569
618
|
"type": "text",
|
|
@@ -994,7 +1043,8 @@
|
|
|
994
1043
|
"supportLink1": "Shipping & Returns",
|
|
995
1044
|
"supportLink2": "Size Guide",
|
|
996
1045
|
"supportLink3": "WhatsApp Orders",
|
|
997
|
-
"contactHeading": "Contact"
|
|
1046
|
+
"contactHeading": "Contact",
|
|
1047
|
+
"instagramUrl": "https://instagram.com"
|
|
998
1048
|
}
|
|
999
1049
|
},
|
|
1000
1050
|
"home": {
|
|
@@ -1055,6 +1105,8 @@
|
|
|
1055
1105
|
"whatsappLabel": "Easy Ordering",
|
|
1056
1106
|
"whatsappTitle": "Order Directly via WhatsApp",
|
|
1057
1107
|
"whatsappDesc": "Select your size, add to your bag, and send your order in one tap. Our team confirms availability within hours.",
|
|
1108
|
+
"whatsappCtaLabel": "Start a Conversation",
|
|
1109
|
+
"whatsappCtaUrl": "https://wa.me/94771234567?text=Hi%2C%20I'd%20like%20to%20order%3A%0A%0AI'm%20interested%20in%20VANTA%20footwear.%20Can%20you%20help%20me%20choose%20a%20pair%3F",
|
|
1058
1110
|
"categoryLabel1": "Performance",
|
|
1059
1111
|
"categoryLabel2": "Sneakers",
|
|
1060
1112
|
"categoryLabel3": "Lifestyle",
|
|
@@ -133,6 +133,19 @@ function saveRecipeFromProject(projectDir, recipeName = 'custom-storefront', opt
|
|
|
133
133
|
autoIndex: true,
|
|
134
134
|
defaultCardPrefix: 'product',
|
|
135
135
|
},
|
|
136
|
+
actionRules: {
|
|
137
|
+
splitActionAndLabel: true,
|
|
138
|
+
whatsappKeywords: ['whatsapp', 'wa.me', 'order via whatsapp', 'chat on whatsapp'],
|
|
139
|
+
actionFieldMapping: {
|
|
140
|
+
whatsapp: { urlField: 'whatsappCtaUrl', labelField: 'whatsappCtaLabel' },
|
|
141
|
+
order: { urlField: 'orderCtaUrl', labelField: 'orderCtaLabel' },
|
|
142
|
+
promo: { urlField: 'promoCtaUrl', labelField: 'promoCtaLabel' },
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
socialRules: {
|
|
146
|
+
platforms: ['instagram', 'facebook', 'twitter', 'tiktok', 'youtube', 'linkedin'],
|
|
147
|
+
targetPath: 'common.footer',
|
|
148
|
+
},
|
|
136
149
|
sections: manifest.editorSchema?.sections || [],
|
|
137
150
|
defaults: siteData.content || {},
|
|
138
151
|
};
|
|
@@ -406,7 +406,94 @@ function transformFileContent(filePath, pageKey, extractedData, backupDir, proje
|
|
|
406
406
|
return `<span data-preview-field-path="${pageKey}.${fieldKey}"${attrs}>{siteData?.content?.${pageKey}?.${fieldKey} || ${JSON.stringify(trimmed)}}</span>`;
|
|
407
407
|
});
|
|
408
408
|
|
|
409
|
-
// 9.
|
|
409
|
+
// 9. Adaptive Action-Link Splitter & Social Media Handler (100% Fivora Contract Safe)
|
|
410
|
+
// 9a. WhatsApp & Direct Communication Links
|
|
411
|
+
const waRegex = /<a(\s+[^>]*?)href="([^"]*(?:wa\.me|whatsapp)[^"]*)"([^>]*)>([\s\S]*?)<\/a>/gi;
|
|
412
|
+
code = code.replace(waRegex, (match, preHref = '', href, postHref = '', innerContent) => {
|
|
413
|
+
if (preHref.includes('data-preview-field-path') || postHref.includes('data-preview-field-path')) {
|
|
414
|
+
return match;
|
|
415
|
+
}
|
|
416
|
+
const urlKey = 'whatsappCtaUrl';
|
|
417
|
+
const labelKey = 'whatsappCtaLabel';
|
|
418
|
+
extractedData[urlKey] = href;
|
|
419
|
+
|
|
420
|
+
// Extract text from innerContent
|
|
421
|
+
const textMatch = innerContent.match(/>([^<>{}]+)</) || [null, innerContent.replace(/<[^>]+>/g, '').trim()];
|
|
422
|
+
const text = (textMatch[1] || 'Order via WhatsApp').trim();
|
|
423
|
+
extractedData[labelKey] = text;
|
|
424
|
+
|
|
425
|
+
elementCount += 2;
|
|
426
|
+
fileModified = true;
|
|
427
|
+
|
|
428
|
+
// Wrap inner label cleanly in span if not already wrapped
|
|
429
|
+
let updatedInner = innerContent;
|
|
430
|
+
if (/<span(\s+[^>]*)?>([^<>{}]+)<\/span>/i.test(innerContent)) {
|
|
431
|
+
updatedInner = innerContent.replace(
|
|
432
|
+
/<span(\s+[^>]*)?>([^<>{}]+)<\/span>/i,
|
|
433
|
+
`<span data-preview-field-path="${pageKey}.${labelKey}"$1>{siteData?.content?.${pageKey}?.${labelKey} || ${JSON.stringify(text)}}</span>`
|
|
434
|
+
);
|
|
435
|
+
} else if (text && innerContent.includes(text)) {
|
|
436
|
+
updatedInner = innerContent.replace(
|
|
437
|
+
text,
|
|
438
|
+
`<span data-preview-field-path="${pageKey}.${labelKey}">{siteData?.content?.${pageKey}?.${labelKey} || ${JSON.stringify(text)}}</span>`
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return `<a${preHref}href={siteData?.content?.${pageKey}?.${urlKey} || ${JSON.stringify(href)}} data-preview-field-path="${pageKey}.${urlKey}"${postHref}>${updatedInner}</a>`;
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
// 9b. Social Media Icon Links (Instagram, Facebook, TikTok, Twitter/X, YouTube, LinkedIn)
|
|
446
|
+
const socialRegex = /<a(\s+[^>]*?)href="([^"]*(?:instagram|facebook|tiktok|twitter|youtube|linkedin)\.com[^"]*)"([^>]*)>([\s\S]*?)<\/a>/gi;
|
|
447
|
+
code = code.replace(socialRegex, (match, preHref = '', href, postHref = '', innerContent) => {
|
|
448
|
+
if (preHref.includes('data-preview-field-path') || postHref.includes('data-preview-field-path')) {
|
|
449
|
+
return match;
|
|
450
|
+
}
|
|
451
|
+
const platMatch = href.match(/(instagram|facebook|tiktok|twitter|youtube|linkedin)\.com/i);
|
|
452
|
+
const platform = platMatch ? platMatch[1].toLowerCase() : 'social';
|
|
453
|
+
const fieldKey = `${platform}Url`;
|
|
454
|
+
|
|
455
|
+
extractedData[fieldKey] = href;
|
|
456
|
+
elementCount++;
|
|
457
|
+
fileModified = true;
|
|
458
|
+
|
|
459
|
+
return `<a${preHref}href={siteData?.content?.common?.footer?.${fieldKey} || siteData?.content?.${pageKey}?.${fieldKey} || ${JSON.stringify(href)}} data-preview-field-path="common.footer.${fieldKey}"${postHref}>${innerContent}</a>`;
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
// 9c. Button / CTA Action Links with Text (Split URL on <a> and Label on <span>)
|
|
463
|
+
const btnLinkRegex = /<a(\s+[^>]*?class(?:Name)?="[^"]*(?:btn|button|cta|action|rounded|bg-)[^"]*"[^>]*?)href="([^"]+)"([^>]*)>([\s\S]*?)<\/a>/gi;
|
|
464
|
+
code = code.replace(btnLinkRegex, (match, preHref = '', href, postHref = '', innerContent) => {
|
|
465
|
+
if (preHref.includes('data-preview-field-path') || postHref.includes('data-preview-field-path') || href.startsWith('#') || href.includes('javascript:')) {
|
|
466
|
+
return match;
|
|
467
|
+
}
|
|
468
|
+
const textOnly = innerContent.replace(/<[^>]+>/g, '').trim();
|
|
469
|
+
if (!textOnly || textOnly.length < 2) return match;
|
|
470
|
+
|
|
471
|
+
const rawUrlKey = toFieldKey(textOnly, 'ctaUrl', elementCount + 1);
|
|
472
|
+
const urlKey = getUniqueKey(rawUrlKey);
|
|
473
|
+
const labelKey = urlKey.replace(/Url$/, 'Label') || getUniqueKey('ctaLabel');
|
|
474
|
+
|
|
475
|
+
extractedData[urlKey] = href;
|
|
476
|
+
extractedData[labelKey] = textOnly;
|
|
477
|
+
elementCount += 2;
|
|
478
|
+
fileModified = true;
|
|
479
|
+
|
|
480
|
+
let updatedInner = innerContent;
|
|
481
|
+
if (/<span(\s+[^>]*)?>([^<>{}]+)<\/span>/i.test(innerContent)) {
|
|
482
|
+
updatedInner = innerContent.replace(
|
|
483
|
+
/<span(\s+[^>]*)?>([^<>{}]+)<\/span>/i,
|
|
484
|
+
`<span data-preview-field-path="${pageKey}.${labelKey}"$1>{siteData?.content?.${pageKey}?.${labelKey} || ${JSON.stringify(textOnly)}}</span>`
|
|
485
|
+
);
|
|
486
|
+
} else {
|
|
487
|
+
updatedInner = innerContent.replace(
|
|
488
|
+
textOnly,
|
|
489
|
+
`<span data-preview-field-path="${pageKey}.${labelKey}">{siteData?.content?.${pageKey}?.${labelKey} || ${JSON.stringify(textOnly)}}</span>`
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
return `<a${preHref}href={siteData?.content?.${pageKey}?.${urlKey} || ${JSON.stringify(href)}} data-preview-field-path="${pageKey}.${urlKey}"${postHref}>${updatedInner}</a>`;
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
// 9d. Standard Anchor Links with Plain Text
|
|
410
497
|
const linkRegex = /<a(\s+[^>]*)?>([^<>{}]+)<\/a>/g;
|
|
411
498
|
code = code.replace(linkRegex, (match, attrs = '', text) => {
|
|
412
499
|
const trimmed = text.trim().replace(/\s+/g, ' ');
|
|
@@ -651,16 +738,32 @@ function generateTemplateData(projectDir, projectName, detectedPages, extractedB
|
|
|
651
738
|
// If active recipe has defined sections, seed them
|
|
652
739
|
if (activeRecipe && Array.isArray(activeRecipe.sections)) {
|
|
653
740
|
for (const sec of activeRecipe.sections) {
|
|
654
|
-
if (sec.id === 'common')
|
|
741
|
+
if (sec.id === 'common') {
|
|
742
|
+
const commonSec = editorSections.find((s) => s.id === 'common');
|
|
743
|
+
if (commonSec && Array.isArray(sec.fields)) {
|
|
744
|
+
for (const rf of sec.fields) {
|
|
745
|
+
if (!commonSec.fields.some((f) => f.key === rf.key)) {
|
|
746
|
+
commonSec.fields.push(rf);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
continue;
|
|
751
|
+
}
|
|
655
752
|
editorSections.push(sec);
|
|
656
753
|
}
|
|
657
754
|
}
|
|
658
755
|
|
|
659
|
-
// Seed recipe defaults if available
|
|
756
|
+
// Seed recipe defaults if available with deep merge for common/footer
|
|
660
757
|
if (activeRecipe && activeRecipe.defaults) {
|
|
661
758
|
for (const [secKey, secVal] of Object.entries(activeRecipe.defaults)) {
|
|
662
|
-
if (!content[secKey])
|
|
663
|
-
|
|
759
|
+
if (!content[secKey]) {
|
|
760
|
+
content[secKey] = JSON.parse(JSON.stringify(secVal));
|
|
761
|
+
} else if (typeof secVal === 'object' && !Array.isArray(secVal)) {
|
|
762
|
+
content[secKey] = { ...secVal, ...content[secKey] };
|
|
763
|
+
if (secVal.footer && content[secKey].footer) {
|
|
764
|
+
content[secKey].footer = { ...secVal.footer, ...content[secKey].footer };
|
|
765
|
+
}
|
|
766
|
+
}
|
|
664
767
|
}
|
|
665
768
|
}
|
|
666
769
|
|