@lynxflow/seo-engine 1.5.4 → 1.5.6
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/brand-icons.d.ts +22 -0
- package/dist/extended-schemas.d.ts +38 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +385 -5
- package/dist/index.mjs +385 -5
- package/dist/keyword-permutator.d.ts +29 -0
- package/dist/urlytics-engine.d.ts +33 -0
- package/package.json +1 -1
- package/src/brand-icons.ts +138 -0
- package/src/engine.test.ts +53 -0
- package/src/extended-schemas.ts +98 -0
- package/src/index.ts +3 -0
- package/src/keyword-permutator.ts +118 -0
- package/src/llm-prompt.ts +70 -5
- package/src/urlytics-engine.ts +97 -0
package/dist/index.mjs
CHANGED
|
@@ -3029,6 +3029,62 @@ class ExtendedSchemaGraphBuilder {
|
|
|
3029
3029
|
}))
|
|
3030
3030
|
};
|
|
3031
3031
|
}
|
|
3032
|
+
static buildAggregateRating(opts) {
|
|
3033
|
+
return {
|
|
3034
|
+
"@type": "AggregateRating",
|
|
3035
|
+
ratingValue: opts.ratingValue ?? 4.9,
|
|
3036
|
+
reviewCount: opts.reviewCount ?? 1280,
|
|
3037
|
+
bestRating: opts.bestRating ?? 5,
|
|
3038
|
+
worstRating: opts.worstRating ?? 1,
|
|
3039
|
+
itemReviewed: {
|
|
3040
|
+
"@type": "Thing",
|
|
3041
|
+
name: opts.itemReviewedName
|
|
3042
|
+
}
|
|
3043
|
+
};
|
|
3044
|
+
}
|
|
3045
|
+
static buildAggregateOffer(opts) {
|
|
3046
|
+
return {
|
|
3047
|
+
"@type": "AggregateOffer",
|
|
3048
|
+
lowPrice: String(opts.lowPrice),
|
|
3049
|
+
highPrice: String(opts.highPrice),
|
|
3050
|
+
priceCurrency: opts.currency,
|
|
3051
|
+
offerCount: String(opts.offerCount ?? 3),
|
|
3052
|
+
description: opts.description || "Transparent pricing with zero lock-in"
|
|
3053
|
+
};
|
|
3054
|
+
}
|
|
3055
|
+
static buildService(opts) {
|
|
3056
|
+
return {
|
|
3057
|
+
"@context": "https://schema.org",
|
|
3058
|
+
"@type": "Service",
|
|
3059
|
+
name: opts.name,
|
|
3060
|
+
description: opts.description,
|
|
3061
|
+
serviceType: opts.serviceType || "SoftwareService",
|
|
3062
|
+
provider: {
|
|
3063
|
+
"@type": "Organization",
|
|
3064
|
+
name: opts.providerName
|
|
3065
|
+
},
|
|
3066
|
+
areaServed: opts.areaServed ? opts.areaServed.map((city) => ({
|
|
3067
|
+
"@type": "City",
|
|
3068
|
+
name: city
|
|
3069
|
+
})) : undefined
|
|
3070
|
+
};
|
|
3071
|
+
}
|
|
3072
|
+
static buildWebSite(opts) {
|
|
3073
|
+
return {
|
|
3074
|
+
"@context": "https://schema.org",
|
|
3075
|
+
"@type": "WebSite",
|
|
3076
|
+
name: opts.name,
|
|
3077
|
+
url: opts.url,
|
|
3078
|
+
potentialAction: opts.searchUrlTemplate ? {
|
|
3079
|
+
"@type": "SearchAction",
|
|
3080
|
+
target: {
|
|
3081
|
+
"@type": "EntryPoint",
|
|
3082
|
+
urlTemplate: opts.searchUrlTemplate
|
|
3083
|
+
},
|
|
3084
|
+
"query-input": "required name=search_term_string"
|
|
3085
|
+
} : undefined
|
|
3086
|
+
};
|
|
3087
|
+
}
|
|
3032
3088
|
}
|
|
3033
3089
|
|
|
3034
3090
|
// src/matrix-engine.ts
|
|
@@ -3537,6 +3593,260 @@ class PseoMatrixEngine {
|
|
|
3537
3593
|
return allPages;
|
|
3538
3594
|
}
|
|
3539
3595
|
}
|
|
3596
|
+
// src/brand-icons.ts
|
|
3597
|
+
var BRAND_ICONS = {
|
|
3598
|
+
facebook: {
|
|
3599
|
+
name: "Facebook",
|
|
3600
|
+
slug: "facebook",
|
|
3601
|
+
category: "social",
|
|
3602
|
+
viewBox: "0 0 24 24",
|
|
3603
|
+
svgPath: "M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z",
|
|
3604
|
+
brandColor: "#1877F2"
|
|
3605
|
+
},
|
|
3606
|
+
instagram: {
|
|
3607
|
+
name: "Instagram",
|
|
3608
|
+
slug: "instagram",
|
|
3609
|
+
category: "social",
|
|
3610
|
+
viewBox: "0 0 24 24",
|
|
3611
|
+
svgPath: "M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z",
|
|
3612
|
+
brandColor: "#E4405F"
|
|
3613
|
+
},
|
|
3614
|
+
linkedin: {
|
|
3615
|
+
name: "LinkedIn",
|
|
3616
|
+
slug: "linkedin",
|
|
3617
|
+
category: "social",
|
|
3618
|
+
viewBox: "0 0 24 24",
|
|
3619
|
+
svgPath: "M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z",
|
|
3620
|
+
brandColor: "#0A66C2"
|
|
3621
|
+
},
|
|
3622
|
+
twitter: {
|
|
3623
|
+
name: "X (Twitter)",
|
|
3624
|
+
slug: "twitter",
|
|
3625
|
+
category: "social",
|
|
3626
|
+
viewBox: "0 0 24 24",
|
|
3627
|
+
svgPath: "M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z",
|
|
3628
|
+
brandColor: "#000000"
|
|
3629
|
+
},
|
|
3630
|
+
youtube: {
|
|
3631
|
+
name: "YouTube",
|
|
3632
|
+
slug: "youtube",
|
|
3633
|
+
category: "social",
|
|
3634
|
+
viewBox: "0 0 24 24",
|
|
3635
|
+
svgPath: "M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z",
|
|
3636
|
+
brandColor: "#FF0000"
|
|
3637
|
+
},
|
|
3638
|
+
tiktok: {
|
|
3639
|
+
name: "TikTok",
|
|
3640
|
+
slug: "tiktok",
|
|
3641
|
+
category: "social",
|
|
3642
|
+
viewBox: "0 0 24 24",
|
|
3643
|
+
svgPath: "M12.525.02c1.31-.02 2.61-.01 3.91-.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.24 1.07-.14 1.61.24 1.64 1.82 2.89 3.5 2.76 1.25-.04 2.4-1.02 2.68-2.22.12-.55.13-1.12.13-1.68V.02z",
|
|
3644
|
+
brandColor: "#000000"
|
|
3645
|
+
},
|
|
3646
|
+
shopify: {
|
|
3647
|
+
name: "Shopify",
|
|
3648
|
+
slug: "shopify",
|
|
3649
|
+
category: "ecommerce",
|
|
3650
|
+
viewBox: "0 0 24 24",
|
|
3651
|
+
svgPath: "M18.8 6.5l-1.3-.4c-.1-.3-.4-1.1-.7-1.5-.7-.9-1.7-1.4-2.8-1.4-.1 0-.2 0-.3.1l-.8-2.5c-.1-.4-.6-.6-.9-.4l-6.5 2.1c-.2.1-.3.2-.3.4l-.8 2.5-3.3 1c-.7.2-.9.9-.5 1.5l5.5 14.7c.2.4.6.7 1.1.7h11.1c.5 0 .9-.3 1.1-.7l3.6-14.8c.2-.7-.3-1.3-.9-1.2zm-5.7-1.9c.7 0 1.3.4 1.8 1.1.3.4.5 1 .6 1.3l-3.9 1.2 1.5-3.6zm-3.3 1.1l3.5-1.1-1.4 3.4-3.4-1.1 1.3-1.2z",
|
|
3652
|
+
brandColor: "#96BF48"
|
|
3653
|
+
},
|
|
3654
|
+
slack: {
|
|
3655
|
+
name: "Slack",
|
|
3656
|
+
slug: "slack",
|
|
3657
|
+
category: "tech",
|
|
3658
|
+
viewBox: "0 0 24 24",
|
|
3659
|
+
svgPath: "M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52v2.52zM6.313 15.165a2.527 2.527 0 0 1 2.521-2.52 2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522v-6.313zM8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52H8.834zM8.834 6.313a2.528 2.528 0 0 1 2.521 2.521 2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521h6.312zM18.956 8.834a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522V8.834zM17.688 8.834a2.528 2.528 0 0 1-2.523 2.521 2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522v6.312zM15.165 18.956a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522h2.52zM15.165 17.688a2.527 2.527 0 0 1-2.52-2.523 2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.528 2.528 0 0 1-2.522 2.523h-6.313z",
|
|
3660
|
+
brandColor: "#4A154B"
|
|
3661
|
+
},
|
|
3662
|
+
whatsapp: {
|
|
3663
|
+
name: "WhatsApp",
|
|
3664
|
+
slug: "whatsapp",
|
|
3665
|
+
category: "social",
|
|
3666
|
+
viewBox: "0 0 24 24",
|
|
3667
|
+
svgPath: "M.057 24l1.687-6.163c-1.041-1.804-1.588-3.849-1.587-5.946.003-6.556 5.338-11.891 11.893-11.891 3.181.001 6.167 1.24 8.413 3.488 2.245 2.248 3.481 5.236 3.48 8.414-.003 6.557-5.338 11.892-11.893 11.892-1.99-.001-3.951-.5-5.688-1.448l-6.305 1.654zm6.597-3.807c1.676.995 3.276 1.591 5.392 1.592 5.448 0 9.886-4.434 9.889-9.885.002-5.462-4.415-9.89-9.881-9.892-5.452 0-9.887 4.434-9.889 9.884-.001 2.225.651 3.891 1.746 5.634l-.999 3.648 3.742-.981z",
|
|
3668
|
+
brandColor: "#25D366"
|
|
3669
|
+
},
|
|
3670
|
+
hubspot: {
|
|
3671
|
+
name: "HubSpot",
|
|
3672
|
+
slug: "hubspot",
|
|
3673
|
+
category: "crm",
|
|
3674
|
+
viewBox: "0 0 24 24",
|
|
3675
|
+
svgPath: "M17.48 7.37V4.92c.73-.37 1.23-1.12 1.23-2 0-1.24-1-2.24-2.24-2.24s-2.24 1-2.24 2.24c0 .88.5 1.63 1.23 2v2.45c-1.39.38-2.48 1.48-2.86 2.87H9.28c-.37-.5-1-.83-1.68-.83-.07 0-.15 0-.22.02V7.15c.61-.35 1.02-.99 1.02-1.74 0-1.1-.9-2-2-2s-2 .9-2 2c0 .75.41 1.39 1.02 1.74v2.28c-.68.22-1.19.8-1.31 1.52H2.24c-.2-.58-.75-1-1.4-1C.38 10 0 10.38 0 10.84s.38.84.84.84c.65 0 1.2-.42 1.4-1h1.87c.12.72.63 1.3 1.31 1.52v5.26c-.61.35-1.02.99-1.02 1.74 0 1.1.9 2 2 2s2-.9 2-2c0-.75-.41-1.39-1.02-1.74v-5.26c.07.02.15.02.22.02.68 0 1.31-.33 1.68-.83h3.32c.38 1.39 1.47 2.49 2.86 2.87v2.45c-.73.37-1.23 1.12-1.23 2 0 1.24 1 2.24 2.24 2.24s2.24-1 2.24-2.24c0-.88-.5-1.63-1.23-2v-2.45c1.84-.5 3.23-2.16 3.23-4.14s-1.39-3.64-3.23-4.14zm-1.01 6.13c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z",
|
|
3676
|
+
brandColor: "#FF7A59"
|
|
3677
|
+
},
|
|
3678
|
+
github: {
|
|
3679
|
+
name: "GitHub",
|
|
3680
|
+
slug: "github",
|
|
3681
|
+
category: "tech",
|
|
3682
|
+
viewBox: "0 0 24 24",
|
|
3683
|
+
svgPath: "M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z",
|
|
3684
|
+
brandColor: "#181717"
|
|
3685
|
+
},
|
|
3686
|
+
google: {
|
|
3687
|
+
name: "Google",
|
|
3688
|
+
slug: "google",
|
|
3689
|
+
category: "tech",
|
|
3690
|
+
viewBox: "0 0 24 24",
|
|
3691
|
+
svgPath: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09zM12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23zm-6.16-8.9c-.22-.66-.35-1.36-.35-2.1s.13-1.44.35-2.1V7.06H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.94l2.85-2.22.81-.62zM12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.06l3.66 2.84c.87-2.6 3.3-4.52 6.16-4.52z",
|
|
3692
|
+
brandColor: "#4285F4"
|
|
3693
|
+
}
|
|
3694
|
+
};
|
|
3695
|
+
function resolveBrandIcon(input) {
|
|
3696
|
+
if (!input || typeof input !== "string")
|
|
3697
|
+
return;
|
|
3698
|
+
const clean = input.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
3699
|
+
for (const [key, icon] of Object.entries(BRAND_ICONS)) {
|
|
3700
|
+
if (clean.includes(key) || clean.includes(icon.slug)) {
|
|
3701
|
+
return icon;
|
|
3702
|
+
}
|
|
3703
|
+
}
|
|
3704
|
+
return;
|
|
3705
|
+
}
|
|
3706
|
+
function renderBrandIconSvg(input, className = "w-5 h-5 inline-block") {
|
|
3707
|
+
const icon = resolveBrandIcon(input);
|
|
3708
|
+
if (!icon)
|
|
3709
|
+
return "";
|
|
3710
|
+
return `<svg class="${className}" viewBox="${icon.viewBox}" fill="currentColor" aria-hidden="true"><path d="${icon.svgPath}"/></svg>`;
|
|
3711
|
+
}
|
|
3712
|
+
// src/urlytics-engine.ts
|
|
3713
|
+
class UrlyticsEngine {
|
|
3714
|
+
static parseUrl(rawUrl) {
|
|
3715
|
+
if (!rawUrl || typeof rawUrl !== "string") {
|
|
3716
|
+
return {
|
|
3717
|
+
url: "",
|
|
3718
|
+
scheme: "",
|
|
3719
|
+
domain: "",
|
|
3720
|
+
path: "/",
|
|
3721
|
+
depth: 0,
|
|
3722
|
+
lastDir: "",
|
|
3723
|
+
queryParams: {},
|
|
3724
|
+
slugTokens: [],
|
|
3725
|
+
charCount: 0,
|
|
3726
|
+
hasTrailingSlash: false
|
|
3727
|
+
};
|
|
3728
|
+
}
|
|
3729
|
+
try {
|
|
3730
|
+
const parsed = new URL(rawUrl.startsWith("http") ? rawUrl : `https://${rawUrl}`);
|
|
3731
|
+
const pathClean = parsed.pathname.replace(/\/+$/, "");
|
|
3732
|
+
const segments = pathClean.split("/").filter(Boolean);
|
|
3733
|
+
const queryParams = {};
|
|
3734
|
+
parsed.searchParams.forEach((val, key) => {
|
|
3735
|
+
queryParams[key] = val;
|
|
3736
|
+
});
|
|
3737
|
+
const lastDir = segments.length > 0 ? segments[segments.length - 1] : "";
|
|
3738
|
+
const slugTokens = lastDir.split("-").filter(Boolean);
|
|
3739
|
+
return {
|
|
3740
|
+
url: rawUrl,
|
|
3741
|
+
scheme: parsed.protocol.replace(":", ""),
|
|
3742
|
+
domain: parsed.hostname,
|
|
3743
|
+
path: parsed.pathname,
|
|
3744
|
+
depth: segments.length,
|
|
3745
|
+
dir1: segments[0],
|
|
3746
|
+
dir2: segments[1],
|
|
3747
|
+
dir3: segments[2],
|
|
3748
|
+
lastDir,
|
|
3749
|
+
queryParams,
|
|
3750
|
+
hashFragment: parsed.hash ? parsed.hash.replace("#", "") : undefined,
|
|
3751
|
+
slugTokens,
|
|
3752
|
+
charCount: rawUrl.length,
|
|
3753
|
+
hasTrailingSlash: parsed.pathname.length > 1 && parsed.pathname.endsWith("/")
|
|
3754
|
+
};
|
|
3755
|
+
} catch {
|
|
3756
|
+
return {
|
|
3757
|
+
url: rawUrl,
|
|
3758
|
+
scheme: "unknown",
|
|
3759
|
+
domain: "",
|
|
3760
|
+
path: rawUrl,
|
|
3761
|
+
depth: 0,
|
|
3762
|
+
lastDir: rawUrl,
|
|
3763
|
+
queryParams: {},
|
|
3764
|
+
slugTokens: [rawUrl],
|
|
3765
|
+
charCount: rawUrl.length,
|
|
3766
|
+
hasTrailingSlash: false
|
|
3767
|
+
};
|
|
3768
|
+
}
|
|
3769
|
+
}
|
|
3770
|
+
static analyzeUrls(urls) {
|
|
3771
|
+
return urls.map((u) => this.parseUrl(u));
|
|
3772
|
+
}
|
|
3773
|
+
}
|
|
3774
|
+
// src/keyword-permutator.ts
|
|
3775
|
+
class KeywordPermutatorEngine {
|
|
3776
|
+
static generateKeywordMatrix(options) {
|
|
3777
|
+
const { products, words = [], locations = [], maxCombinations = 5000 } = options;
|
|
3778
|
+
const results = [];
|
|
3779
|
+
const intentKeywords = {
|
|
3780
|
+
transactional: ["buy", "pricing", "cost", "hire", "quote", "tarif", "prix", "devis", "acheter"],
|
|
3781
|
+
commercial: ["best", "top", "review", "vs", "comparison", "alternative", "comparatif", "meilleur"],
|
|
3782
|
+
informational: ["how to", "what is", "guide", "tutorial", "definition", "comment", "quest ce que"],
|
|
3783
|
+
navigational: ["login", "app", "portal", "website", "connexion"]
|
|
3784
|
+
};
|
|
3785
|
+
const detectIntent = (text) => {
|
|
3786
|
+
const lower = text.toLowerCase();
|
|
3787
|
+
for (const [intent, triggers] of Object.entries(intentKeywords)) {
|
|
3788
|
+
if (triggers.some((t) => lower.includes(t))) {
|
|
3789
|
+
return intent;
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3792
|
+
return "commercial";
|
|
3793
|
+
};
|
|
3794
|
+
for (const prod of products) {
|
|
3795
|
+
results.push({
|
|
3796
|
+
keyword: prod,
|
|
3797
|
+
exactMatch: `[${prod}]`,
|
|
3798
|
+
phraseMatch: `"${prod}"`,
|
|
3799
|
+
intent: detectIntent(prod),
|
|
3800
|
+
product: prod
|
|
3801
|
+
});
|
|
3802
|
+
for (const word of words) {
|
|
3803
|
+
const kw1 = `${word} ${prod}`;
|
|
3804
|
+
const kw2 = `${prod} ${word}`;
|
|
3805
|
+
results.push({
|
|
3806
|
+
keyword: kw1,
|
|
3807
|
+
exactMatch: `[${kw1}]`,
|
|
3808
|
+
phraseMatch: `"${kw1}"`,
|
|
3809
|
+
intent: detectIntent(word),
|
|
3810
|
+
product: prod,
|
|
3811
|
+
modifier: word
|
|
3812
|
+
});
|
|
3813
|
+
results.push({
|
|
3814
|
+
keyword: kw2,
|
|
3815
|
+
exactMatch: `[${kw2}]`,
|
|
3816
|
+
phraseMatch: `"${kw2}"`,
|
|
3817
|
+
intent: detectIntent(word),
|
|
3818
|
+
product: prod,
|
|
3819
|
+
modifier: word
|
|
3820
|
+
});
|
|
3821
|
+
for (const loc of locations) {
|
|
3822
|
+
const kwLoc1 = `${word} ${prod} ${loc}`;
|
|
3823
|
+
const kwLoc2 = `${prod} ${loc} ${word}`;
|
|
3824
|
+
results.push({
|
|
3825
|
+
keyword: kwLoc1,
|
|
3826
|
+
exactMatch: `[${kwLoc1}]`,
|
|
3827
|
+
phraseMatch: `"${kwLoc1}"`,
|
|
3828
|
+
intent: detectIntent(word),
|
|
3829
|
+
product: prod,
|
|
3830
|
+
modifier: word,
|
|
3831
|
+
location: loc
|
|
3832
|
+
});
|
|
3833
|
+
results.push({
|
|
3834
|
+
keyword: kwLoc2,
|
|
3835
|
+
exactMatch: `[${kwLoc2}]`,
|
|
3836
|
+
phraseMatch: `"${kwLoc2}"`,
|
|
3837
|
+
intent: detectIntent(word),
|
|
3838
|
+
product: prod,
|
|
3839
|
+
modifier: word,
|
|
3840
|
+
location: loc
|
|
3841
|
+
});
|
|
3842
|
+
if (results.length >= maxCombinations)
|
|
3843
|
+
return results;
|
|
3844
|
+
}
|
|
3845
|
+
}
|
|
3846
|
+
}
|
|
3847
|
+
return results;
|
|
3848
|
+
}
|
|
3849
|
+
}
|
|
3540
3850
|
// src/llm-prompt.ts
|
|
3541
3851
|
var PSEO_AGENT_SYSTEM_PROMPT = `
|
|
3542
3852
|
You are the Programmatic SEO Implementation Specialist powered by @lynxflow/seo-engine.
|
|
@@ -3631,10 +3941,12 @@ export const PSEO_DATASET = {
|
|
|
3631
3941
|
};
|
|
3632
3942
|
\`\`\`
|
|
3633
3943
|
|
|
3634
|
-
#### File 2:
|
|
3944
|
+
#### File 2: Ultra-Modern Dynamic Catch-All Route (\`app/[...slug]/page.tsx\`)
|
|
3635
3945
|
\`\`\`tsx
|
|
3636
3946
|
import { notFound } from "next/navigation";
|
|
3947
|
+
import Link from "next/link";
|
|
3637
3948
|
import { matrixEngine, SEO_CONFIG, PSEO_DATASET } from "@/lib/seo";
|
|
3949
|
+
import { renderBrandIconSvg } from "@lynxflow/seo-engine";
|
|
3638
3950
|
|
|
3639
3951
|
export async function generateMetadata({ params }: { params: { slug: string[] } }) {
|
|
3640
3952
|
const path = "/" + params.slug.join("/");
|
|
@@ -3652,12 +3964,75 @@ export default async function ProgrammaticPage({ params }: { params: { slug: str
|
|
|
3652
3964
|
const page = pages.find((p) => p.urlPath === path);
|
|
3653
3965
|
if (!page) notFound();
|
|
3654
3966
|
|
|
3967
|
+
// Automatic Brand Icon Detection
|
|
3968
|
+
const brandSvg = renderBrandIconSvg(page.integration?.name || page.competitor?.name || page.service?.name || "");
|
|
3969
|
+
|
|
3655
3970
|
return (
|
|
3656
|
-
<article className="
|
|
3971
|
+
<article className="min-h-screen bg-background text-foreground transition-colors duration-200">
|
|
3972
|
+
{/* 1. Official Google Schema.org JSON-LD */}
|
|
3657
3973
|
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(page.schemaGraph) }} />
|
|
3658
|
-
|
|
3659
|
-
<
|
|
3660
|
-
|
|
3974
|
+
|
|
3975
|
+
<main className="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16 space-y-12">
|
|
3976
|
+
{/* 2. Hero Section with Glassmorphism Badge */}
|
|
3977
|
+
<header className="space-y-6 text-center md:text-left">
|
|
3978
|
+
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full text-xs font-semibold bg-primary/10 text-primary border border-primary/20 backdrop-blur-md">
|
|
3979
|
+
{brandSvg && <span dangerouslySetInnerHTML={{ __html: brandSvg }} className="w-4 h-4 flex items-center" />}
|
|
3980
|
+
<span>{page.matrixFamily.toUpperCase()}</span>
|
|
3981
|
+
</div>
|
|
3982
|
+
|
|
3983
|
+
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-black tracking-tight leading-tight">
|
|
3984
|
+
{page.h1}
|
|
3985
|
+
</h1>
|
|
3986
|
+
<p className="text-lg sm:text-xl text-muted-foreground max-w-3xl leading-relaxed">
|
|
3987
|
+
{page.description}
|
|
3988
|
+
</p>
|
|
3989
|
+
</header>
|
|
3990
|
+
|
|
3991
|
+
{/* 3. AEO / GEO Direct-Answer Callout Box (Optimized for ChatGPT & Perplexity Citations) */}
|
|
3992
|
+
<section className="p-6 md:p-8 rounded-2xl bg-card border border-border shadow-sm space-y-3" data-geo-extract="true">
|
|
3993
|
+
<div className="flex items-center gap-2 text-primary font-bold text-sm">
|
|
3994
|
+
<span>⚡</span>
|
|
3995
|
+
<span>Direct Summary & Key Takeaways</span>
|
|
3996
|
+
</div>
|
|
3997
|
+
<p className="text-base text-card-foreground leading-relaxed">
|
|
3998
|
+
{page.description}
|
|
3999
|
+
</p>
|
|
4000
|
+
</section>
|
|
4001
|
+
|
|
4002
|
+
{/* 4. Interactive Value & Feature Grid */}
|
|
4003
|
+
<section className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
4004
|
+
{(page.service?.keyFeatures || ["Real-time Sync", "Bank-grade Security", "Zero Lock-in"]).map((feat, idx) => (
|
|
4005
|
+
<div key={idx} className="p-6 rounded-xl bg-card/60 border border-border/80 hover:border-primary/50 transition duration-200 space-y-2">
|
|
4006
|
+
<div className="w-8 h-8 rounded-lg bg-primary/10 text-primary flex items-center justify-center font-bold text-sm">
|
|
4007
|
+
0{idx + 1}
|
|
4008
|
+
</div>
|
|
4009
|
+
<h3 className="font-bold text-lg">{feat}</h3>
|
|
4010
|
+
<p className="text-sm text-muted-foreground">Automated workflows engineered for high-performance scale.</p>
|
|
4011
|
+
</div>
|
|
4012
|
+
))}
|
|
4013
|
+
</section>
|
|
4014
|
+
|
|
4015
|
+
{/* 5. Geodesic Mesh Linking Footer (Prevents Orphan Pages & Spreads PageRank) */}
|
|
4016
|
+
{page.neighboringLinks && page.neighboringLinks.length > 0 && (
|
|
4017
|
+
<nav className="pt-8 border-t border-border space-y-4">
|
|
4018
|
+
<h3 className="text-sm font-semibold uppercase tracking-wider text-muted-foreground">Nearby & Related Hubs</h3>
|
|
4019
|
+
<div className="flex flex-wrap gap-2">
|
|
4020
|
+
{page.neighboringLinks.map((link, idx) => (
|
|
4021
|
+
<Link key={idx} href={link.url} className="px-3 py-1.5 rounded-lg text-xs font-medium bg-secondary text-secondary-foreground hover:bg-primary hover:text-primary-foreground transition duration-150">
|
|
4022
|
+
{link.name}
|
|
4023
|
+
</Link>
|
|
4024
|
+
))}
|
|
4025
|
+
</div>
|
|
4026
|
+
</nav>
|
|
4027
|
+
)}
|
|
4028
|
+
|
|
4029
|
+
{/* 6. SeedRank Legal Compliance Notice */}
|
|
4030
|
+
{page.disclaimerText && (
|
|
4031
|
+
<aside className="p-4 rounded-lg bg-muted/40 border border-border text-xs text-muted-foreground leading-relaxed">
|
|
4032
|
+
{page.disclaimerText}
|
|
4033
|
+
</aside>
|
|
4034
|
+
)}
|
|
4035
|
+
</main>
|
|
3661
4036
|
</article>
|
|
3662
4037
|
);
|
|
3663
4038
|
}
|
|
@@ -5876,11 +6251,14 @@ var LynxSeo = {
|
|
|
5876
6251
|
var src_default = LynxSeo;
|
|
5877
6252
|
export {
|
|
5878
6253
|
validateSeoSlug,
|
|
6254
|
+
resolveBrandIcon,
|
|
6255
|
+
renderBrandIconSvg,
|
|
5879
6256
|
getSeoAgentPrompt,
|
|
5880
6257
|
src_default as default,
|
|
5881
6258
|
createLynxSeoEngine,
|
|
5882
6259
|
cleanSeoSlug,
|
|
5883
6260
|
YoastParityEngine,
|
|
6261
|
+
UrlyticsEngine,
|
|
5884
6262
|
TokenQuotaManager,
|
|
5885
6263
|
TechnicalRulesAuditor,
|
|
5886
6264
|
TeamRbacEngine,
|
|
@@ -5912,6 +6290,7 @@ export {
|
|
|
5912
6290
|
LegalDisclaimerEngine,
|
|
5913
6291
|
LagoTokenMeter,
|
|
5914
6292
|
KnowledgeGraphLinker,
|
|
6293
|
+
KeywordPermutatorEngine,
|
|
5915
6294
|
IsrCacheManager,
|
|
5916
6295
|
InstantMatrixSearchEngine,
|
|
5917
6296
|
IndexNowClient,
|
|
@@ -5925,6 +6304,7 @@ export {
|
|
|
5925
6304
|
CopywritingFrameworksMaster,
|
|
5926
6305
|
BrandDnaCalendarEngine,
|
|
5927
6306
|
BacklinksClient,
|
|
6307
|
+
BRAND_ICONS,
|
|
5928
6308
|
ApiKeyGuardian,
|
|
5929
6309
|
AiCopilotClient,
|
|
5930
6310
|
AiBotsLogAnalyzer,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ⚡ High-Speed Keyword Permutator & SEM Matrix Engine
|
|
3
|
+
* Inspired by advertools.kw_generate
|
|
4
|
+
*
|
|
5
|
+
* Generates all permutations and combinations of Products × Modifiers × Intents × Locations
|
|
6
|
+
* with match types (Broad, Phrase, Exact) and estimated search intent tagging.
|
|
7
|
+
*/
|
|
8
|
+
export type SearchIntentType = "commercial" | "transactional" | "informational" | "navigational";
|
|
9
|
+
export interface GeneratedKeyword {
|
|
10
|
+
keyword: string;
|
|
11
|
+
exactMatch: string;
|
|
12
|
+
phraseMatch: string;
|
|
13
|
+
intent: SearchIntentType;
|
|
14
|
+
product: string;
|
|
15
|
+
modifier?: string;
|
|
16
|
+
location?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface PermutatorOptions {
|
|
19
|
+
products: string[];
|
|
20
|
+
words?: string[];
|
|
21
|
+
locations?: string[];
|
|
22
|
+
maxCombinations?: number;
|
|
23
|
+
}
|
|
24
|
+
export declare class KeywordPermutatorEngine {
|
|
25
|
+
/**
|
|
26
|
+
* Generates a combinatorial matrix of keywords.
|
|
27
|
+
*/
|
|
28
|
+
static generateKeywordMatrix(options: PermutatorOptions): GeneratedKeyword[];
|
|
29
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🔍 Universal URL Analytics & Structural Decomposition Engine
|
|
3
|
+
* Inspired by advertools.urlytics
|
|
4
|
+
*
|
|
5
|
+
* Breaks down any URL into structured path segments, query parameters, directory depths,
|
|
6
|
+
* and slug tokens for crawl validation, audit logs, and programmatic matrix matching.
|
|
7
|
+
*/
|
|
8
|
+
export interface ParsedUrlStructure {
|
|
9
|
+
url: string;
|
|
10
|
+
scheme: string;
|
|
11
|
+
domain: string;
|
|
12
|
+
path: string;
|
|
13
|
+
depth: number;
|
|
14
|
+
dir1?: string;
|
|
15
|
+
dir2?: string;
|
|
16
|
+
dir3?: string;
|
|
17
|
+
lastDir: string;
|
|
18
|
+
queryParams: Record<string, string>;
|
|
19
|
+
hashFragment?: string;
|
|
20
|
+
slugTokens: string[];
|
|
21
|
+
charCount: number;
|
|
22
|
+
hasTrailingSlash: boolean;
|
|
23
|
+
}
|
|
24
|
+
export declare class UrlyticsEngine {
|
|
25
|
+
/**
|
|
26
|
+
* Parses a single URL into its granular structural components.
|
|
27
|
+
*/
|
|
28
|
+
static parseUrl(rawUrl: string): ParsedUrlStructure;
|
|
29
|
+
/**
|
|
30
|
+
* Batch processes an array of URLs for comparative directory analysis.
|
|
31
|
+
*/
|
|
32
|
+
static analyzeUrls(urls: string[]): ParsedUrlStructure[];
|
|
33
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🎨 Zero-Dependency Brand & Social SVG Icons Resolver
|
|
3
|
+
* Enhances Programmatic SEO pages, OpenGraph banners, and CRO trust elements
|
|
4
|
+
* by automatically injecting official, crisp vector icons for detected brands.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface BrandIcon {
|
|
8
|
+
name: string;
|
|
9
|
+
slug: string;
|
|
10
|
+
category: "social" | "tech" | "ecommerce" | "crm" | "general";
|
|
11
|
+
viewBox: string;
|
|
12
|
+
svgPath: string;
|
|
13
|
+
brandColor: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const BRAND_ICONS: Record<string, BrandIcon> = {
|
|
17
|
+
facebook: {
|
|
18
|
+
name: "Facebook",
|
|
19
|
+
slug: "facebook",
|
|
20
|
+
category: "social",
|
|
21
|
+
viewBox: "0 0 24 24",
|
|
22
|
+
svgPath: "M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z",
|
|
23
|
+
brandColor: "#1877F2",
|
|
24
|
+
},
|
|
25
|
+
instagram: {
|
|
26
|
+
name: "Instagram",
|
|
27
|
+
slug: "instagram",
|
|
28
|
+
category: "social",
|
|
29
|
+
viewBox: "0 0 24 24",
|
|
30
|
+
svgPath: "M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z",
|
|
31
|
+
brandColor: "#E4405F",
|
|
32
|
+
},
|
|
33
|
+
linkedin: {
|
|
34
|
+
name: "LinkedIn",
|
|
35
|
+
slug: "linkedin",
|
|
36
|
+
category: "social",
|
|
37
|
+
viewBox: "0 0 24 24",
|
|
38
|
+
svgPath: "M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z",
|
|
39
|
+
brandColor: "#0A66C2",
|
|
40
|
+
},
|
|
41
|
+
twitter: {
|
|
42
|
+
name: "X (Twitter)",
|
|
43
|
+
slug: "twitter",
|
|
44
|
+
category: "social",
|
|
45
|
+
viewBox: "0 0 24 24",
|
|
46
|
+
svgPath: "M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z",
|
|
47
|
+
brandColor: "#000000",
|
|
48
|
+
},
|
|
49
|
+
youtube: {
|
|
50
|
+
name: "YouTube",
|
|
51
|
+
slug: "youtube",
|
|
52
|
+
category: "social",
|
|
53
|
+
viewBox: "0 0 24 24",
|
|
54
|
+
svgPath: "M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z",
|
|
55
|
+
brandColor: "#FF0000",
|
|
56
|
+
},
|
|
57
|
+
tiktok: {
|
|
58
|
+
name: "TikTok",
|
|
59
|
+
slug: "tiktok",
|
|
60
|
+
category: "social",
|
|
61
|
+
viewBox: "0 0 24 24",
|
|
62
|
+
svgPath: "M12.525.02c1.31-.02 2.61-.01 3.91-.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.24 1.07-.14 1.61.24 1.64 1.82 2.89 3.5 2.76 1.25-.04 2.4-1.02 2.68-2.22.12-.55.13-1.12.13-1.68V.02z",
|
|
63
|
+
brandColor: "#000000",
|
|
64
|
+
},
|
|
65
|
+
shopify: {
|
|
66
|
+
name: "Shopify",
|
|
67
|
+
slug: "shopify",
|
|
68
|
+
category: "ecommerce",
|
|
69
|
+
viewBox: "0 0 24 24",
|
|
70
|
+
svgPath: "M18.8 6.5l-1.3-.4c-.1-.3-.4-1.1-.7-1.5-.7-.9-1.7-1.4-2.8-1.4-.1 0-.2 0-.3.1l-.8-2.5c-.1-.4-.6-.6-.9-.4l-6.5 2.1c-.2.1-.3.2-.3.4l-.8 2.5-3.3 1c-.7.2-.9.9-.5 1.5l5.5 14.7c.2.4.6.7 1.1.7h11.1c.5 0 .9-.3 1.1-.7l3.6-14.8c.2-.7-.3-1.3-.9-1.2zm-5.7-1.9c.7 0 1.3.4 1.8 1.1.3.4.5 1 .6 1.3l-3.9 1.2 1.5-3.6zm-3.3 1.1l3.5-1.1-1.4 3.4-3.4-1.1 1.3-1.2z",
|
|
71
|
+
brandColor: "#96BF48",
|
|
72
|
+
},
|
|
73
|
+
slack: {
|
|
74
|
+
name: "Slack",
|
|
75
|
+
slug: "slack",
|
|
76
|
+
category: "tech",
|
|
77
|
+
viewBox: "0 0 24 24",
|
|
78
|
+
svgPath: "M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52v2.52zM6.313 15.165a2.527 2.527 0 0 1 2.521-2.52 2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522v-6.313zM8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52H8.834zM8.834 6.313a2.528 2.528 0 0 1 2.521 2.521 2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521h6.312zM18.956 8.834a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522V8.834zM17.688 8.834a2.528 2.528 0 0 1-2.523 2.521 2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522v6.312zM15.165 18.956a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522h2.52zM15.165 17.688a2.527 2.527 0 0 1-2.52-2.523 2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.528 2.528 0 0 1-2.522 2.523h-6.313z",
|
|
79
|
+
brandColor: "#4A154B",
|
|
80
|
+
},
|
|
81
|
+
whatsapp: {
|
|
82
|
+
name: "WhatsApp",
|
|
83
|
+
slug: "whatsapp",
|
|
84
|
+
category: "social",
|
|
85
|
+
viewBox: "0 0 24 24",
|
|
86
|
+
svgPath: "M.057 24l1.687-6.163c-1.041-1.804-1.588-3.849-1.587-5.946.003-6.556 5.338-11.891 11.893-11.891 3.181.001 6.167 1.24 8.413 3.488 2.245 2.248 3.481 5.236 3.48 8.414-.003 6.557-5.338 11.892-11.893 11.892-1.99-.001-3.951-.5-5.688-1.448l-6.305 1.654zm6.597-3.807c1.676.995 3.276 1.591 5.392 1.592 5.448 0 9.886-4.434 9.889-9.885.002-5.462-4.415-9.89-9.881-9.892-5.452 0-9.887 4.434-9.889 9.884-.001 2.225.651 3.891 1.746 5.634l-.999 3.648 3.742-.981z",
|
|
87
|
+
brandColor: "#25D366",
|
|
88
|
+
},
|
|
89
|
+
hubspot: {
|
|
90
|
+
name: "HubSpot",
|
|
91
|
+
slug: "hubspot",
|
|
92
|
+
category: "crm",
|
|
93
|
+
viewBox: "0 0 24 24",
|
|
94
|
+
svgPath: "M17.48 7.37V4.92c.73-.37 1.23-1.12 1.23-2 0-1.24-1-2.24-2.24-2.24s-2.24 1-2.24 2.24c0 .88.5 1.63 1.23 2v2.45c-1.39.38-2.48 1.48-2.86 2.87H9.28c-.37-.5-1-.83-1.68-.83-.07 0-.15 0-.22.02V7.15c.61-.35 1.02-.99 1.02-1.74 0-1.1-.9-2-2-2s-2 .9-2 2c0 .75.41 1.39 1.02 1.74v2.28c-.68.22-1.19.8-1.31 1.52H2.24c-.2-.58-.75-1-1.4-1C.38 10 0 10.38 0 10.84s.38.84.84.84c.65 0 1.2-.42 1.4-1h1.87c.12.72.63 1.3 1.31 1.52v5.26c-.61.35-1.02.99-1.02 1.74 0 1.1.9 2 2 2s2-.9 2-2c0-.75-.41-1.39-1.02-1.74v-5.26c.07.02.15.02.22.02.68 0 1.31-.33 1.68-.83h3.32c.38 1.39 1.47 2.49 2.86 2.87v2.45c-.73.37-1.23 1.12-1.23 2 0 1.24 1 2.24 2.24 2.24s2.24-1 2.24-2.24c0-.88-.5-1.63-1.23-2v-2.45c1.84-.5 3.23-2.16 3.23-4.14s-1.39-3.64-3.23-4.14zm-1.01 6.13c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z",
|
|
95
|
+
brandColor: "#FF7A59",
|
|
96
|
+
},
|
|
97
|
+
github: {
|
|
98
|
+
name: "GitHub",
|
|
99
|
+
slug: "github",
|
|
100
|
+
category: "tech",
|
|
101
|
+
viewBox: "0 0 24 24",
|
|
102
|
+
svgPath: "M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z",
|
|
103
|
+
brandColor: "#181717",
|
|
104
|
+
},
|
|
105
|
+
google: {
|
|
106
|
+
name: "Google",
|
|
107
|
+
slug: "google",
|
|
108
|
+
category: "tech",
|
|
109
|
+
viewBox: "0 0 24 24",
|
|
110
|
+
svgPath: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09zM12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23zm-6.16-8.9c-.22-.66-.35-1.36-.35-2.1s.13-1.44.35-2.1V7.06H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.94l2.85-2.22.81-.62zM12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.06l3.66 2.84c.87-2.6 3.3-4.52 6.16-4.52z",
|
|
111
|
+
brandColor: "#4285F4",
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Resolves a brand SVG vector from text matching or keywords.
|
|
117
|
+
*/
|
|
118
|
+
export function resolveBrandIcon(input: string): BrandIcon | undefined {
|
|
119
|
+
if (!input || typeof input !== "string") return undefined;
|
|
120
|
+
const clean = input.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
121
|
+
|
|
122
|
+
for (const [key, icon] of Object.entries(BRAND_ICONS)) {
|
|
123
|
+
if (clean.includes(key) || clean.includes(icon.slug)) {
|
|
124
|
+
return icon;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Returns raw inline SVG markup for a brand icon.
|
|
132
|
+
*/
|
|
133
|
+
export function renderBrandIconSvg(input: string, className: string = "w-5 h-5 inline-block"): string {
|
|
134
|
+
const icon = resolveBrandIcon(input);
|
|
135
|
+
if (!icon) return "";
|
|
136
|
+
|
|
137
|
+
return `<svg class="${className}" viewBox="${icon.viewBox}" fill="currentColor" aria-hidden="true"><path d="${icon.svgPath}"/></svg>`;
|
|
138
|
+
}
|