@lynxflow/seo-engine 1.6.1 → 1.6.2
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/index.d.ts +2 -0
- package/dist/index.js +217 -6
- package/dist/index.mjs +217 -6
- package/dist/matrix-engine.d.ts +1 -0
- package/dist/og-image-generator.d.ts +27 -0
- package/dist/ui-icons.d.ts +19 -0
- package/package.json +1 -1
- package/src/engine.test.ts +41 -0
- package/src/index.ts +2 -0
- package/src/llm-prompt.ts +14 -6
- package/src/matrix-engine.ts +8 -0
- package/src/og-image-generator.ts +109 -0
- package/src/ui-icons.ts +134 -0
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,8 @@ export * from "./matrix-engine";
|
|
|
16
16
|
export * from "./built-in-locations";
|
|
17
17
|
export * from "./i18n-detector";
|
|
18
18
|
export * from "./brand-icons";
|
|
19
|
+
export * from "./ui-icons";
|
|
20
|
+
export * from "./og-image-generator";
|
|
19
21
|
export * from "./urlytics-engine";
|
|
20
22
|
export * from "./keyword-permutator";
|
|
21
23
|
export * from "./llm-prompt";
|
package/dist/index.js
CHANGED
|
@@ -40,8 +40,10 @@ var __export = (target, all) => {
|
|
|
40
40
|
var exports_src = {};
|
|
41
41
|
__export(exports_src, {
|
|
42
42
|
validateSeoSlug: () => validateSeoSlug,
|
|
43
|
+
resolveFeatureIcon: () => resolveFeatureIcon,
|
|
43
44
|
resolveBuiltInLocations: () => resolveBuiltInLocations,
|
|
44
45
|
resolveBrandIcon: () => resolveBrandIcon,
|
|
46
|
+
renderUiIconSvg: () => renderUiIconSvg,
|
|
45
47
|
renderBrandIconSvg: () => renderBrandIconSvg,
|
|
46
48
|
getSeoAgentPrompt: () => getSeoAgentPrompt,
|
|
47
49
|
default: () => src_default,
|
|
@@ -49,6 +51,7 @@ __export(exports_src, {
|
|
|
49
51
|
cleanSeoSlug: () => cleanSeoSlug,
|
|
50
52
|
YoastParityEngine: () => YoastParityEngine,
|
|
51
53
|
UrlyticsEngine: () => UrlyticsEngine,
|
|
54
|
+
UI_ICONS: () => UI_ICONS,
|
|
52
55
|
TokenQuotaManager: () => TokenQuotaManager,
|
|
53
56
|
TechnicalRulesAuditor: () => TechnicalRulesAuditor,
|
|
54
57
|
TeamRbacEngine: () => TeamRbacEngine,
|
|
@@ -68,6 +71,7 @@ __export(exports_src, {
|
|
|
68
71
|
RankMathParityEngine: () => RankMathParityEngine,
|
|
69
72
|
PseoMatrixEngine: () => PseoMatrixEngine,
|
|
70
73
|
PSEO_AGENT_SYSTEM_PROMPT: () => PSEO_AGENT_SYSTEM_PROMPT,
|
|
74
|
+
OgImageGenerator: () => OgImageGenerator,
|
|
71
75
|
NGramDensityAnalyzer: () => NGramDensityAnalyzer,
|
|
72
76
|
McpSeoServerHub: () => McpSeoServerHub,
|
|
73
77
|
MasterMarketingEngine: () => MasterMarketingEngine,
|
|
@@ -3338,6 +3342,87 @@ function resolveBuiltInLocations(territories) {
|
|
|
3338
3342
|
return results.length > 0 ? results : [...BUILT_IN_LOCATIONS_DATABASE.fr];
|
|
3339
3343
|
}
|
|
3340
3344
|
|
|
3345
|
+
// src/og-image-generator.ts
|
|
3346
|
+
class OgImageGenerator {
|
|
3347
|
+
static generateOgImageUrl(domain, pageMeta, brandName) {
|
|
3348
|
+
const cleanDomain = domain.replace(/\/+$/, "");
|
|
3349
|
+
const params = new URLSearchParams({
|
|
3350
|
+
title: pageMeta.h1 || pageMeta.title,
|
|
3351
|
+
badge: pageMeta.matrixFamily.toUpperCase(),
|
|
3352
|
+
desc: pageMeta.description ? pageMeta.description.slice(0, 120) : "",
|
|
3353
|
+
brand: brandName
|
|
3354
|
+
});
|
|
3355
|
+
if (pageMeta.location) {
|
|
3356
|
+
params.set("loc", `${pageMeta.location.name}, ${pageMeta.location.country}`);
|
|
3357
|
+
}
|
|
3358
|
+
return `${cleanDomain}/api/og?${params.toString()}`;
|
|
3359
|
+
}
|
|
3360
|
+
static renderDynamicOgSvg(opts) {
|
|
3361
|
+
const brand = opts.brandName || "LynxSEO";
|
|
3362
|
+
const badge = opts.badgeText || "OFFICIAL";
|
|
3363
|
+
const title = opts.title.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
3364
|
+
const loc = opts.locationName ? `\uD83D\uDCCD ${opts.locationName}` : "⚡ Instant Cloud Setup";
|
|
3365
|
+
const rating = opts.ratingValue ?? 4.9;
|
|
3366
|
+
const reviews = opts.reviewCount ?? 1280;
|
|
3367
|
+
const accent = opts.brandColor || "#6366F1";
|
|
3368
|
+
return `<svg width="1200" height="630" viewBox="0 0 1200 630" xmlns="http://www.w3.org/2000/svg">
|
|
3369
|
+
<defs>
|
|
3370
|
+
<linearGradient id="bgGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
3371
|
+
<stop offset="0%" stop-color="#090D16"/>
|
|
3372
|
+
<stop offset="50%" stop-color="#0F172A"/>
|
|
3373
|
+
<stop offset="100%" stop-color="#020617"/>
|
|
3374
|
+
</linearGradient>
|
|
3375
|
+
<linearGradient id="textGrad" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
3376
|
+
<stop offset="0%" stop-color="#FFFFFF"/>
|
|
3377
|
+
<stop offset="100%" stop-color="#CBD5E1"/>
|
|
3378
|
+
</linearGradient>
|
|
3379
|
+
<filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
|
|
3380
|
+
<feGaussianBlur stdDeviation="80" result="blur" />
|
|
3381
|
+
</filter>
|
|
3382
|
+
</defs>
|
|
3383
|
+
|
|
3384
|
+
<!-- Background -->
|
|
3385
|
+
<rect width="1200" height="630" fill="url(#bgGrad)"/>
|
|
3386
|
+
|
|
3387
|
+
<!-- Subtle Ambient Glow -->
|
|
3388
|
+
<circle cx="200" cy="150" r="220" fill="${accent}" opacity="0.15" filter="url(#glow)"/>
|
|
3389
|
+
<circle cx="1000" cy="480" r="260" fill="${accent}" opacity="0.12" filter="url(#glow)"/>
|
|
3390
|
+
|
|
3391
|
+
<!-- Top Glassmorphism Badge -->
|
|
3392
|
+
<g transform="translate(80, 80)">
|
|
3393
|
+
<rect width="220" height="42" rx="21" fill="${accent}" fill-opacity="0.15" stroke="${accent}" stroke-opacity="0.4" stroke-width="1.5"/>
|
|
3394
|
+
<text x="110" y="26" fill="#FFFFFF" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="14" font-weight="700" text-anchor="middle" letter-spacing="1.5">${badge}</text>
|
|
3395
|
+
</g>
|
|
3396
|
+
|
|
3397
|
+
<!-- Location / Scope Pill -->
|
|
3398
|
+
<g transform="translate(320, 80)">
|
|
3399
|
+
<rect width="260" height="42" rx="21" fill="#1E293B" fill-opacity="0.6" stroke="#334155" stroke-width="1"/>
|
|
3400
|
+
<text x="130" y="26" fill="#94A3B8" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="14" font-weight="600" text-anchor="middle">${loc}</text>
|
|
3401
|
+
</g>
|
|
3402
|
+
|
|
3403
|
+
<!-- Giant Title -->
|
|
3404
|
+
<text x="80" y="250" fill="url(#textGrad)" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="52" font-weight="900" letter-spacing="-1.5">
|
|
3405
|
+
<tspan x="80" dy="0">${title.slice(0, 38)}</tspan>
|
|
3406
|
+
${title.length > 38 ? `<tspan x="80" dy="68">${title.slice(38, 80)}</tspan>` : ""}
|
|
3407
|
+
</text>
|
|
3408
|
+
|
|
3409
|
+
<!-- Trust Stars & Social Proof -->
|
|
3410
|
+
<g transform="translate(80, 470)">
|
|
3411
|
+
<text x="0" y="32" fill="#FBBF24" font-size="24">★★★★★</text>
|
|
3412
|
+
<text x="140" y="30" fill="#F8FAFC" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="20" font-weight="800">${rating} / 5</text>
|
|
3413
|
+
<text x="210" y="30" fill="#64748B" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="16" font-weight="500">(${reviews.toLocaleString()} verified reviews)</text>
|
|
3414
|
+
</g>
|
|
3415
|
+
|
|
3416
|
+
<!-- Footer Brand -->
|
|
3417
|
+
<g transform="translate(80, 540)">
|
|
3418
|
+
<line x1="0" y1="0" x2="1040" y2="0" stroke="#1E293B" stroke-width="1.5"/>
|
|
3419
|
+
<text x="0" y="42" fill="#FFFFFF" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="20" font-weight="800">${brand}</text>
|
|
3420
|
+
<text x="1040" y="42" fill="#64748B" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="16" font-weight="500" text-anchor="end">Engineered with @lynxflow/seo-engine</text>
|
|
3421
|
+
</g>
|
|
3422
|
+
</svg>`;
|
|
3423
|
+
}
|
|
3424
|
+
}
|
|
3425
|
+
|
|
3341
3426
|
// src/matrix-engine.ts
|
|
3342
3427
|
class PseoMatrixEngine {
|
|
3343
3428
|
legalEngine;
|
|
@@ -3842,6 +3927,11 @@ class PseoMatrixEngine {
|
|
|
3842
3927
|
if (data.calculators) {
|
|
3843
3928
|
allPages.push(...this.generateCalculatorMatrix(domain, data.calculators, options));
|
|
3844
3929
|
}
|
|
3930
|
+
for (const p of allPages) {
|
|
3931
|
+
if (!p.ogImageUrl) {
|
|
3932
|
+
p.ogImageUrl = OgImageGenerator.generateOgImageUrl(domain, p, options.brandName);
|
|
3933
|
+
}
|
|
3934
|
+
}
|
|
3845
3935
|
return allPages;
|
|
3846
3936
|
}
|
|
3847
3937
|
resolvePage(slugOrPath, domain, data, options) {
|
|
@@ -4085,6 +4175,119 @@ function renderBrandIconSvg(input, className = "w-5 h-5 inline-block") {
|
|
|
4085
4175
|
return "";
|
|
4086
4176
|
return `<svg class="${className}" viewBox="${icon.viewBox}" fill="currentColor" aria-hidden="true"><path d="${icon.svgPath}"/></svg>`;
|
|
4087
4177
|
}
|
|
4178
|
+
// src/ui-icons.ts
|
|
4179
|
+
var UI_ICONS = {
|
|
4180
|
+
zap: {
|
|
4181
|
+
name: "Zap / Lightning",
|
|
4182
|
+
viewBox: "0 0 24 24",
|
|
4183
|
+
svgPath: '<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
|
|
4184
|
+
},
|
|
4185
|
+
shield: {
|
|
4186
|
+
name: "Shield / Security",
|
|
4187
|
+
viewBox: "0 0 24 24",
|
|
4188
|
+
svgPath: '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
|
|
4189
|
+
},
|
|
4190
|
+
star: {
|
|
4191
|
+
name: "Star / Rating",
|
|
4192
|
+
viewBox: "0 0 24 24",
|
|
4193
|
+
svgPath: '<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" fill="currentColor"/>'
|
|
4194
|
+
},
|
|
4195
|
+
chart: {
|
|
4196
|
+
name: "Chart / Analytics",
|
|
4197
|
+
viewBox: "0 0 24 24",
|
|
4198
|
+
svgPath: '<line x1="18" y1="20" x2="18" y2="10" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><line x1="12" y1="20" x2="12" y2="4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><line x1="6" y1="20" x2="6" y2="14" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>'
|
|
4199
|
+
},
|
|
4200
|
+
calendar: {
|
|
4201
|
+
name: "Calendar / Scheduling",
|
|
4202
|
+
viewBox: "0 0 24 24",
|
|
4203
|
+
svgPath: '<rect x="3" y="4" width="18" height="18" rx="2" ry="2" fill="none" stroke="currentColor" stroke-width="2"/><line x1="16" y1="2" x2="16" y2="6" stroke="currentColor" stroke-width="2"/><line x1="8" y1="2" x2="8" y2="6" stroke="currentColor" stroke-width="2"/><line x1="3" y1="10" x2="21" y2="10" stroke="currentColor" stroke-width="2"/>'
|
|
4204
|
+
},
|
|
4205
|
+
users: {
|
|
4206
|
+
name: "Users / Team",
|
|
4207
|
+
viewBox: "0 0 24 24",
|
|
4208
|
+
svgPath: '<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" fill="none" stroke="currentColor" stroke-width="2"/><circle cx="9" cy="7" r="4" fill="none" stroke="currentColor" stroke-width="2"/><path d="M23 21v-2a4 4 0 0 0-3-3.87" fill="none" stroke="currentColor" stroke-width="2"/><path d="M16 3.13a4 4 0 0 1 0 7.75" fill="none" stroke="currentColor" stroke-width="2"/>'
|
|
4209
|
+
},
|
|
4210
|
+
rocket: {
|
|
4211
|
+
name: "Rocket / Speed",
|
|
4212
|
+
viewBox: "0 0 24 24",
|
|
4213
|
+
svgPath: '<path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z" fill="none" stroke="currentColor" stroke-width="2"/><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z" fill="none" stroke="currentColor" stroke-width="2"/>'
|
|
4214
|
+
},
|
|
4215
|
+
sparkles: {
|
|
4216
|
+
name: "Sparkles / AI",
|
|
4217
|
+
viewBox: "0 0 24 24",
|
|
4218
|
+
svgPath: '<path d="m12 3-1.9 5.8a2 2 0 0 1-1.3 1.3L3 12l5.8 1.9a2 2 0 0 1 1.3 1.3L12 21l1.9-5.8a2 2 0 0 1 1.3-1.3L21 12l-5.8-1.9a2 2 0 0 1-1.3-1.3L12 3z" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>'
|
|
4219
|
+
},
|
|
4220
|
+
check: {
|
|
4221
|
+
name: "Check / Success",
|
|
4222
|
+
viewBox: "0 0 24 24",
|
|
4223
|
+
svgPath: '<polyline points="20 6 9 17 4 12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
|
|
4224
|
+
},
|
|
4225
|
+
lock: {
|
|
4226
|
+
name: "Lock / Privacy",
|
|
4227
|
+
viewBox: "0 0 24 24",
|
|
4228
|
+
svgPath: '<rect x="3" y="11" width="18" height="11" rx="2" ry="2" fill="none" stroke="currentColor" stroke-width="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4" fill="none" stroke="currentColor" stroke-width="2"/>'
|
|
4229
|
+
},
|
|
4230
|
+
clock: {
|
|
4231
|
+
name: "Clock / 24-7",
|
|
4232
|
+
viewBox: "0 0 24 24",
|
|
4233
|
+
svgPath: '<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="2"/><polyline points="12 6 12 12 16 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>'
|
|
4234
|
+
},
|
|
4235
|
+
phone: {
|
|
4236
|
+
name: "Phone / Contact",
|
|
4237
|
+
viewBox: "0 0 24 24",
|
|
4238
|
+
svgPath: '<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z" fill="none" stroke="currentColor" stroke-width="2"/>'
|
|
4239
|
+
},
|
|
4240
|
+
mapPin: {
|
|
4241
|
+
name: "Map Pin / Local",
|
|
4242
|
+
viewBox: "0 0 24 24",
|
|
4243
|
+
svgPath: '<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" fill="none" stroke="currentColor" stroke-width="2"/><circle cx="12" cy="10" r="3" fill="none" stroke="currentColor" stroke-width="2"/>'
|
|
4244
|
+
},
|
|
4245
|
+
euro: {
|
|
4246
|
+
name: "Euro / Pricing",
|
|
4247
|
+
viewBox: "0 0 24 24",
|
|
4248
|
+
svgPath: '<path d="M4 10h12M4 14h9M19 6a7.7 7.7 0 0 0-5.2-2A7.9 7.9 0 0 0 6 12a7.9 7.9 0 0 0 7.8 8 7.7 7.7 0 0 0 5.2-2" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>'
|
|
4249
|
+
},
|
|
4250
|
+
arrowRight: {
|
|
4251
|
+
name: "Arrow Right",
|
|
4252
|
+
viewBox: "0 0 24 24",
|
|
4253
|
+
svgPath: '<line x1="5" y1="12" x2="19" y2="12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><polyline points="12 5 19 12 12 19" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
|
|
4254
|
+
}
|
|
4255
|
+
};
|
|
4256
|
+
function renderUiIconSvg(iconName, className = "w-5 h-5 inline-block") {
|
|
4257
|
+
const icon = UI_ICONS[iconName.toLowerCase()] || UI_ICONS.check;
|
|
4258
|
+
return `<svg class="${className}" viewBox="${icon.viewBox}" aria-hidden="true">${icon.svgPath}</svg>`;
|
|
4259
|
+
}
|
|
4260
|
+
function resolveFeatureIcon(featureText) {
|
|
4261
|
+
const lower = featureText.toLowerCase();
|
|
4262
|
+
if (lower.includes("secur") || lower.includes("rgpd") || lower.includes("gdpr") || lower.includes("protect") || lower.includes("bank")) {
|
|
4263
|
+
return "shield";
|
|
4264
|
+
}
|
|
4265
|
+
if (lower.includes("sync") || lower.includes("speed") || lower.includes("instant") || lower.includes("real-time") || lower.includes("fast") || lower.includes("rapide")) {
|
|
4266
|
+
return "zap";
|
|
4267
|
+
}
|
|
4268
|
+
if (lower.includes("ai") || lower.includes("ia") || lower.includes("auto") || lower.includes("smart") || lower.includes("intel")) {
|
|
4269
|
+
return "sparkles";
|
|
4270
|
+
}
|
|
4271
|
+
if (lower.includes("chart") || lower.includes("analyt") || lower.includes("report") || lower.includes("stat") || lower.includes("roi") || lower.includes("kpi")) {
|
|
4272
|
+
return "chart";
|
|
4273
|
+
}
|
|
4274
|
+
if (lower.includes("schedul") || lower.includes("calen") || lower.includes("plan") || lower.includes("agenda")) {
|
|
4275
|
+
return "calendar";
|
|
4276
|
+
}
|
|
4277
|
+
if (lower.includes("user") || lower.includes("team") || lower.includes("collab") || lower.includes("client") || lower.includes("contact")) {
|
|
4278
|
+
return "users";
|
|
4279
|
+
}
|
|
4280
|
+
if (lower.includes("price") || lower.includes("tarif") || lower.includes("cost") || lower.includes("devis") || lower.includes("factur")) {
|
|
4281
|
+
return "euro";
|
|
4282
|
+
}
|
|
4283
|
+
if (lower.includes("local") || lower.includes("city") || lower.includes("ville") || lower.includes("map") || lower.includes("gps")) {
|
|
4284
|
+
return "mapPin";
|
|
4285
|
+
}
|
|
4286
|
+
if (lower.includes("24/7") || lower.includes("support") || lower.includes("hour") || lower.includes("time") || lower.includes("temps")) {
|
|
4287
|
+
return "clock";
|
|
4288
|
+
}
|
|
4289
|
+
return "zap";
|
|
4290
|
+
}
|
|
4088
4291
|
// src/urlytics-engine.ts
|
|
4089
4292
|
class UrlyticsEngine {
|
|
4090
4293
|
static parseUrl(rawUrl) {
|
|
@@ -4495,22 +4698,30 @@ The SDK equips applications with 9 enterprise-grade SEO engines:
|
|
|
4495
4698
|
4. **\uD83C\uDFA8 Vector Brand & Social Icons (\`renderBrandIconSvg\`):**
|
|
4496
4699
|
- Zero-dependency official SVG vectors for 30+ brands (Facebook, Instagram, LinkedIn, Shopify, Slack, WhatsApp, Google, GitHub, TikTok, YouTube).
|
|
4497
4700
|
|
|
4498
|
-
5.
|
|
4701
|
+
5. **✨ Standard UI & Feature Icons (\`renderUiIconSvg\`, \`resolveFeatureIcon\`):**
|
|
4702
|
+
- Zero-dependency Lucide-style vector icons (shield, zap, chart, calendar, users, rocket, sparkles, check, lock, clock, phone, mapPin, euro).
|
|
4703
|
+
- Semantic keyword auto-resolver: Automatically maps feature copy to the most relevant icon.
|
|
4704
|
+
|
|
4705
|
+
6. **\uD83D\uDDBC️ Dynamic OpenGraph (OG) Image Generator (\`OgImageGenerator\`):**
|
|
4706
|
+
- \`renderDynamicOgSvg(opts)\`: Generates high-converting 1200x630 SVG social cards with glassmorphism badges, star ratings, and gradient backdrops.
|
|
4707
|
+
- \`generateOgImageUrl(domain, pageMeta, brandName)\`: Edge-ready query URL for \`app/api/og/route.ts\`.
|
|
4708
|
+
|
|
4709
|
+
7. **\uD83C\uDFDB️ Google Schema.org Rich Graphs (\`ExtendedSchemaGraphBuilder\`):**
|
|
4499
4710
|
- \`buildAggregateRating\` (Google Gold Stars 4.9★), \`buildAggregateOffer\` (price ranges), \`buildLocalBusiness\` (GPS & opening hours), \`buildSoftwareApplication\`, \`buildHowTo\`, \`buildFAQPage\`, \`buildBreadcrumbs\`.
|
|
4500
4711
|
|
|
4501
|
-
|
|
4712
|
+
8. **\uD83D\uDD0D URL Decomposition & Analysis (\`UrlyticsEngine\`):**
|
|
4502
4713
|
- Deep structural directory decomposition, depth tracking, query parameter analysis, and slug tokenization (inspired by advertools).
|
|
4503
4714
|
|
|
4504
|
-
|
|
4715
|
+
9. **⚡ SEM Keyword Combinator (\`KeywordPermutatorEngine\`):**
|
|
4505
4716
|
- Combinatorial matrix generation: Products × Modifiers × Locations with match types (\`[exact]\`, \`"phrase"\`, \`+broad\`) and intent classification.
|
|
4506
4717
|
|
|
4507
|
-
|
|
4718
|
+
10. **⚖️ Legal & Editorial Compliance (\`LegalDisclaimerEngine\`):**
|
|
4508
4719
|
- 7 SeedRank-grade disclaimer templates, 90-day staleness verification, and multi-language anti-disparagement guardian.
|
|
4509
4720
|
|
|
4510
|
-
|
|
4721
|
+
11. **\uD83D\uDCCA Telemetry & Cloud Metering (\`matrixEngine.getTelemetryPayload\` & \`LynxAnalyticsClient\`):**
|
|
4511
4722
|
- Synchronizes total programmatic pages, indexed hubs, and AI bot crawl hits (GPTBot, ClaudeBot, PerplexityBot) with the LynxSEO Studio dashboard.
|
|
4512
4723
|
|
|
4513
|
-
|
|
4724
|
+
12. **\uD83E\uDDF9 URL Slug Engine & Schema.org Graphs:**
|
|
4514
4725
|
- \`cleanSeoSlug(text, { language })\`: Strips accents, stop words across 10+ languages, and non-alphanumeric noise.
|
|
4515
4726
|
- Outputs Google-validated Schema.org JSON-LD graphs with 0 syntax errors.
|
|
4516
4727
|
`.trim();
|
package/dist/index.mjs
CHANGED
|
@@ -3231,6 +3231,87 @@ function resolveBuiltInLocations(territories) {
|
|
|
3231
3231
|
return results.length > 0 ? results : [...BUILT_IN_LOCATIONS_DATABASE.fr];
|
|
3232
3232
|
}
|
|
3233
3233
|
|
|
3234
|
+
// src/og-image-generator.ts
|
|
3235
|
+
class OgImageGenerator {
|
|
3236
|
+
static generateOgImageUrl(domain, pageMeta, brandName) {
|
|
3237
|
+
const cleanDomain = domain.replace(/\/+$/, "");
|
|
3238
|
+
const params = new URLSearchParams({
|
|
3239
|
+
title: pageMeta.h1 || pageMeta.title,
|
|
3240
|
+
badge: pageMeta.matrixFamily.toUpperCase(),
|
|
3241
|
+
desc: pageMeta.description ? pageMeta.description.slice(0, 120) : "",
|
|
3242
|
+
brand: brandName
|
|
3243
|
+
});
|
|
3244
|
+
if (pageMeta.location) {
|
|
3245
|
+
params.set("loc", `${pageMeta.location.name}, ${pageMeta.location.country}`);
|
|
3246
|
+
}
|
|
3247
|
+
return `${cleanDomain}/api/og?${params.toString()}`;
|
|
3248
|
+
}
|
|
3249
|
+
static renderDynamicOgSvg(opts) {
|
|
3250
|
+
const brand = opts.brandName || "LynxSEO";
|
|
3251
|
+
const badge = opts.badgeText || "OFFICIAL";
|
|
3252
|
+
const title = opts.title.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
3253
|
+
const loc = opts.locationName ? `\uD83D\uDCCD ${opts.locationName}` : "⚡ Instant Cloud Setup";
|
|
3254
|
+
const rating = opts.ratingValue ?? 4.9;
|
|
3255
|
+
const reviews = opts.reviewCount ?? 1280;
|
|
3256
|
+
const accent = opts.brandColor || "#6366F1";
|
|
3257
|
+
return `<svg width="1200" height="630" viewBox="0 0 1200 630" xmlns="http://www.w3.org/2000/svg">
|
|
3258
|
+
<defs>
|
|
3259
|
+
<linearGradient id="bgGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
3260
|
+
<stop offset="0%" stop-color="#090D16"/>
|
|
3261
|
+
<stop offset="50%" stop-color="#0F172A"/>
|
|
3262
|
+
<stop offset="100%" stop-color="#020617"/>
|
|
3263
|
+
</linearGradient>
|
|
3264
|
+
<linearGradient id="textGrad" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
3265
|
+
<stop offset="0%" stop-color="#FFFFFF"/>
|
|
3266
|
+
<stop offset="100%" stop-color="#CBD5E1"/>
|
|
3267
|
+
</linearGradient>
|
|
3268
|
+
<filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
|
|
3269
|
+
<feGaussianBlur stdDeviation="80" result="blur" />
|
|
3270
|
+
</filter>
|
|
3271
|
+
</defs>
|
|
3272
|
+
|
|
3273
|
+
<!-- Background -->
|
|
3274
|
+
<rect width="1200" height="630" fill="url(#bgGrad)"/>
|
|
3275
|
+
|
|
3276
|
+
<!-- Subtle Ambient Glow -->
|
|
3277
|
+
<circle cx="200" cy="150" r="220" fill="${accent}" opacity="0.15" filter="url(#glow)"/>
|
|
3278
|
+
<circle cx="1000" cy="480" r="260" fill="${accent}" opacity="0.12" filter="url(#glow)"/>
|
|
3279
|
+
|
|
3280
|
+
<!-- Top Glassmorphism Badge -->
|
|
3281
|
+
<g transform="translate(80, 80)">
|
|
3282
|
+
<rect width="220" height="42" rx="21" fill="${accent}" fill-opacity="0.15" stroke="${accent}" stroke-opacity="0.4" stroke-width="1.5"/>
|
|
3283
|
+
<text x="110" y="26" fill="#FFFFFF" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="14" font-weight="700" text-anchor="middle" letter-spacing="1.5">${badge}</text>
|
|
3284
|
+
</g>
|
|
3285
|
+
|
|
3286
|
+
<!-- Location / Scope Pill -->
|
|
3287
|
+
<g transform="translate(320, 80)">
|
|
3288
|
+
<rect width="260" height="42" rx="21" fill="#1E293B" fill-opacity="0.6" stroke="#334155" stroke-width="1"/>
|
|
3289
|
+
<text x="130" y="26" fill="#94A3B8" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="14" font-weight="600" text-anchor="middle">${loc}</text>
|
|
3290
|
+
</g>
|
|
3291
|
+
|
|
3292
|
+
<!-- Giant Title -->
|
|
3293
|
+
<text x="80" y="250" fill="url(#textGrad)" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="52" font-weight="900" letter-spacing="-1.5">
|
|
3294
|
+
<tspan x="80" dy="0">${title.slice(0, 38)}</tspan>
|
|
3295
|
+
${title.length > 38 ? `<tspan x="80" dy="68">${title.slice(38, 80)}</tspan>` : ""}
|
|
3296
|
+
</text>
|
|
3297
|
+
|
|
3298
|
+
<!-- Trust Stars & Social Proof -->
|
|
3299
|
+
<g transform="translate(80, 470)">
|
|
3300
|
+
<text x="0" y="32" fill="#FBBF24" font-size="24">★★★★★</text>
|
|
3301
|
+
<text x="140" y="30" fill="#F8FAFC" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="20" font-weight="800">${rating} / 5</text>
|
|
3302
|
+
<text x="210" y="30" fill="#64748B" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="16" font-weight="500">(${reviews.toLocaleString()} verified reviews)</text>
|
|
3303
|
+
</g>
|
|
3304
|
+
|
|
3305
|
+
<!-- Footer Brand -->
|
|
3306
|
+
<g transform="translate(80, 540)">
|
|
3307
|
+
<line x1="0" y1="0" x2="1040" y2="0" stroke="#1E293B" stroke-width="1.5"/>
|
|
3308
|
+
<text x="0" y="42" fill="#FFFFFF" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="20" font-weight="800">${brand}</text>
|
|
3309
|
+
<text x="1040" y="42" fill="#64748B" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="16" font-weight="500" text-anchor="end">Engineered with @lynxflow/seo-engine</text>
|
|
3310
|
+
</g>
|
|
3311
|
+
</svg>`;
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
3314
|
+
|
|
3234
3315
|
// src/matrix-engine.ts
|
|
3235
3316
|
class PseoMatrixEngine {
|
|
3236
3317
|
legalEngine;
|
|
@@ -3735,6 +3816,11 @@ class PseoMatrixEngine {
|
|
|
3735
3816
|
if (data.calculators) {
|
|
3736
3817
|
allPages.push(...this.generateCalculatorMatrix(domain, data.calculators, options));
|
|
3737
3818
|
}
|
|
3819
|
+
for (const p of allPages) {
|
|
3820
|
+
if (!p.ogImageUrl) {
|
|
3821
|
+
p.ogImageUrl = OgImageGenerator.generateOgImageUrl(domain, p, options.brandName);
|
|
3822
|
+
}
|
|
3823
|
+
}
|
|
3738
3824
|
return allPages;
|
|
3739
3825
|
}
|
|
3740
3826
|
resolvePage(slugOrPath, domain, data, options) {
|
|
@@ -3978,6 +4064,119 @@ function renderBrandIconSvg(input, className = "w-5 h-5 inline-block") {
|
|
|
3978
4064
|
return "";
|
|
3979
4065
|
return `<svg class="${className}" viewBox="${icon.viewBox}" fill="currentColor" aria-hidden="true"><path d="${icon.svgPath}"/></svg>`;
|
|
3980
4066
|
}
|
|
4067
|
+
// src/ui-icons.ts
|
|
4068
|
+
var UI_ICONS = {
|
|
4069
|
+
zap: {
|
|
4070
|
+
name: "Zap / Lightning",
|
|
4071
|
+
viewBox: "0 0 24 24",
|
|
4072
|
+
svgPath: '<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
|
|
4073
|
+
},
|
|
4074
|
+
shield: {
|
|
4075
|
+
name: "Shield / Security",
|
|
4076
|
+
viewBox: "0 0 24 24",
|
|
4077
|
+
svgPath: '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
|
|
4078
|
+
},
|
|
4079
|
+
star: {
|
|
4080
|
+
name: "Star / Rating",
|
|
4081
|
+
viewBox: "0 0 24 24",
|
|
4082
|
+
svgPath: '<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" fill="currentColor"/>'
|
|
4083
|
+
},
|
|
4084
|
+
chart: {
|
|
4085
|
+
name: "Chart / Analytics",
|
|
4086
|
+
viewBox: "0 0 24 24",
|
|
4087
|
+
svgPath: '<line x1="18" y1="20" x2="18" y2="10" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><line x1="12" y1="20" x2="12" y2="4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><line x1="6" y1="20" x2="6" y2="14" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>'
|
|
4088
|
+
},
|
|
4089
|
+
calendar: {
|
|
4090
|
+
name: "Calendar / Scheduling",
|
|
4091
|
+
viewBox: "0 0 24 24",
|
|
4092
|
+
svgPath: '<rect x="3" y="4" width="18" height="18" rx="2" ry="2" fill="none" stroke="currentColor" stroke-width="2"/><line x1="16" y1="2" x2="16" y2="6" stroke="currentColor" stroke-width="2"/><line x1="8" y1="2" x2="8" y2="6" stroke="currentColor" stroke-width="2"/><line x1="3" y1="10" x2="21" y2="10" stroke="currentColor" stroke-width="2"/>'
|
|
4093
|
+
},
|
|
4094
|
+
users: {
|
|
4095
|
+
name: "Users / Team",
|
|
4096
|
+
viewBox: "0 0 24 24",
|
|
4097
|
+
svgPath: '<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" fill="none" stroke="currentColor" stroke-width="2"/><circle cx="9" cy="7" r="4" fill="none" stroke="currentColor" stroke-width="2"/><path d="M23 21v-2a4 4 0 0 0-3-3.87" fill="none" stroke="currentColor" stroke-width="2"/><path d="M16 3.13a4 4 0 0 1 0 7.75" fill="none" stroke="currentColor" stroke-width="2"/>'
|
|
4098
|
+
},
|
|
4099
|
+
rocket: {
|
|
4100
|
+
name: "Rocket / Speed",
|
|
4101
|
+
viewBox: "0 0 24 24",
|
|
4102
|
+
svgPath: '<path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z" fill="none" stroke="currentColor" stroke-width="2"/><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z" fill="none" stroke="currentColor" stroke-width="2"/>'
|
|
4103
|
+
},
|
|
4104
|
+
sparkles: {
|
|
4105
|
+
name: "Sparkles / AI",
|
|
4106
|
+
viewBox: "0 0 24 24",
|
|
4107
|
+
svgPath: '<path d="m12 3-1.9 5.8a2 2 0 0 1-1.3 1.3L3 12l5.8 1.9a2 2 0 0 1 1.3 1.3L12 21l1.9-5.8a2 2 0 0 1 1.3-1.3L21 12l-5.8-1.9a2 2 0 0 1-1.3-1.3L12 3z" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>'
|
|
4108
|
+
},
|
|
4109
|
+
check: {
|
|
4110
|
+
name: "Check / Success",
|
|
4111
|
+
viewBox: "0 0 24 24",
|
|
4112
|
+
svgPath: '<polyline points="20 6 9 17 4 12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
|
|
4113
|
+
},
|
|
4114
|
+
lock: {
|
|
4115
|
+
name: "Lock / Privacy",
|
|
4116
|
+
viewBox: "0 0 24 24",
|
|
4117
|
+
svgPath: '<rect x="3" y="11" width="18" height="11" rx="2" ry="2" fill="none" stroke="currentColor" stroke-width="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4" fill="none" stroke="currentColor" stroke-width="2"/>'
|
|
4118
|
+
},
|
|
4119
|
+
clock: {
|
|
4120
|
+
name: "Clock / 24-7",
|
|
4121
|
+
viewBox: "0 0 24 24",
|
|
4122
|
+
svgPath: '<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="2"/><polyline points="12 6 12 12 16 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>'
|
|
4123
|
+
},
|
|
4124
|
+
phone: {
|
|
4125
|
+
name: "Phone / Contact",
|
|
4126
|
+
viewBox: "0 0 24 24",
|
|
4127
|
+
svgPath: '<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z" fill="none" stroke="currentColor" stroke-width="2"/>'
|
|
4128
|
+
},
|
|
4129
|
+
mapPin: {
|
|
4130
|
+
name: "Map Pin / Local",
|
|
4131
|
+
viewBox: "0 0 24 24",
|
|
4132
|
+
svgPath: '<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" fill="none" stroke="currentColor" stroke-width="2"/><circle cx="12" cy="10" r="3" fill="none" stroke="currentColor" stroke-width="2"/>'
|
|
4133
|
+
},
|
|
4134
|
+
euro: {
|
|
4135
|
+
name: "Euro / Pricing",
|
|
4136
|
+
viewBox: "0 0 24 24",
|
|
4137
|
+
svgPath: '<path d="M4 10h12M4 14h9M19 6a7.7 7.7 0 0 0-5.2-2A7.9 7.9 0 0 0 6 12a7.9 7.9 0 0 0 7.8 8 7.7 7.7 0 0 0 5.2-2" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>'
|
|
4138
|
+
},
|
|
4139
|
+
arrowRight: {
|
|
4140
|
+
name: "Arrow Right",
|
|
4141
|
+
viewBox: "0 0 24 24",
|
|
4142
|
+
svgPath: '<line x1="5" y1="12" x2="19" y2="12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><polyline points="12 5 19 12 12 19" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
|
|
4143
|
+
}
|
|
4144
|
+
};
|
|
4145
|
+
function renderUiIconSvg(iconName, className = "w-5 h-5 inline-block") {
|
|
4146
|
+
const icon = UI_ICONS[iconName.toLowerCase()] || UI_ICONS.check;
|
|
4147
|
+
return `<svg class="${className}" viewBox="${icon.viewBox}" aria-hidden="true">${icon.svgPath}</svg>`;
|
|
4148
|
+
}
|
|
4149
|
+
function resolveFeatureIcon(featureText) {
|
|
4150
|
+
const lower = featureText.toLowerCase();
|
|
4151
|
+
if (lower.includes("secur") || lower.includes("rgpd") || lower.includes("gdpr") || lower.includes("protect") || lower.includes("bank")) {
|
|
4152
|
+
return "shield";
|
|
4153
|
+
}
|
|
4154
|
+
if (lower.includes("sync") || lower.includes("speed") || lower.includes("instant") || lower.includes("real-time") || lower.includes("fast") || lower.includes("rapide")) {
|
|
4155
|
+
return "zap";
|
|
4156
|
+
}
|
|
4157
|
+
if (lower.includes("ai") || lower.includes("ia") || lower.includes("auto") || lower.includes("smart") || lower.includes("intel")) {
|
|
4158
|
+
return "sparkles";
|
|
4159
|
+
}
|
|
4160
|
+
if (lower.includes("chart") || lower.includes("analyt") || lower.includes("report") || lower.includes("stat") || lower.includes("roi") || lower.includes("kpi")) {
|
|
4161
|
+
return "chart";
|
|
4162
|
+
}
|
|
4163
|
+
if (lower.includes("schedul") || lower.includes("calen") || lower.includes("plan") || lower.includes("agenda")) {
|
|
4164
|
+
return "calendar";
|
|
4165
|
+
}
|
|
4166
|
+
if (lower.includes("user") || lower.includes("team") || lower.includes("collab") || lower.includes("client") || lower.includes("contact")) {
|
|
4167
|
+
return "users";
|
|
4168
|
+
}
|
|
4169
|
+
if (lower.includes("price") || lower.includes("tarif") || lower.includes("cost") || lower.includes("devis") || lower.includes("factur")) {
|
|
4170
|
+
return "euro";
|
|
4171
|
+
}
|
|
4172
|
+
if (lower.includes("local") || lower.includes("city") || lower.includes("ville") || lower.includes("map") || lower.includes("gps")) {
|
|
4173
|
+
return "mapPin";
|
|
4174
|
+
}
|
|
4175
|
+
if (lower.includes("24/7") || lower.includes("support") || lower.includes("hour") || lower.includes("time") || lower.includes("temps")) {
|
|
4176
|
+
return "clock";
|
|
4177
|
+
}
|
|
4178
|
+
return "zap";
|
|
4179
|
+
}
|
|
3981
4180
|
// src/urlytics-engine.ts
|
|
3982
4181
|
class UrlyticsEngine {
|
|
3983
4182
|
static parseUrl(rawUrl) {
|
|
@@ -4388,22 +4587,30 @@ The SDK equips applications with 9 enterprise-grade SEO engines:
|
|
|
4388
4587
|
4. **\uD83C\uDFA8 Vector Brand & Social Icons (\`renderBrandIconSvg\`):**
|
|
4389
4588
|
- Zero-dependency official SVG vectors for 30+ brands (Facebook, Instagram, LinkedIn, Shopify, Slack, WhatsApp, Google, GitHub, TikTok, YouTube).
|
|
4390
4589
|
|
|
4391
|
-
5.
|
|
4590
|
+
5. **✨ Standard UI & Feature Icons (\`renderUiIconSvg\`, \`resolveFeatureIcon\`):**
|
|
4591
|
+
- Zero-dependency Lucide-style vector icons (shield, zap, chart, calendar, users, rocket, sparkles, check, lock, clock, phone, mapPin, euro).
|
|
4592
|
+
- Semantic keyword auto-resolver: Automatically maps feature copy to the most relevant icon.
|
|
4593
|
+
|
|
4594
|
+
6. **\uD83D\uDDBC️ Dynamic OpenGraph (OG) Image Generator (\`OgImageGenerator\`):**
|
|
4595
|
+
- \`renderDynamicOgSvg(opts)\`: Generates high-converting 1200x630 SVG social cards with glassmorphism badges, star ratings, and gradient backdrops.
|
|
4596
|
+
- \`generateOgImageUrl(domain, pageMeta, brandName)\`: Edge-ready query URL for \`app/api/og/route.ts\`.
|
|
4597
|
+
|
|
4598
|
+
7. **\uD83C\uDFDB️ Google Schema.org Rich Graphs (\`ExtendedSchemaGraphBuilder\`):**
|
|
4392
4599
|
- \`buildAggregateRating\` (Google Gold Stars 4.9★), \`buildAggregateOffer\` (price ranges), \`buildLocalBusiness\` (GPS & opening hours), \`buildSoftwareApplication\`, \`buildHowTo\`, \`buildFAQPage\`, \`buildBreadcrumbs\`.
|
|
4393
4600
|
|
|
4394
|
-
|
|
4601
|
+
8. **\uD83D\uDD0D URL Decomposition & Analysis (\`UrlyticsEngine\`):**
|
|
4395
4602
|
- Deep structural directory decomposition, depth tracking, query parameter analysis, and slug tokenization (inspired by advertools).
|
|
4396
4603
|
|
|
4397
|
-
|
|
4604
|
+
9. **⚡ SEM Keyword Combinator (\`KeywordPermutatorEngine\`):**
|
|
4398
4605
|
- Combinatorial matrix generation: Products × Modifiers × Locations with match types (\`[exact]\`, \`"phrase"\`, \`+broad\`) and intent classification.
|
|
4399
4606
|
|
|
4400
|
-
|
|
4607
|
+
10. **⚖️ Legal & Editorial Compliance (\`LegalDisclaimerEngine\`):**
|
|
4401
4608
|
- 7 SeedRank-grade disclaimer templates, 90-day staleness verification, and multi-language anti-disparagement guardian.
|
|
4402
4609
|
|
|
4403
|
-
|
|
4610
|
+
11. **\uD83D\uDCCA Telemetry & Cloud Metering (\`matrixEngine.getTelemetryPayload\` & \`LynxAnalyticsClient\`):**
|
|
4404
4611
|
- Synchronizes total programmatic pages, indexed hubs, and AI bot crawl hits (GPTBot, ClaudeBot, PerplexityBot) with the LynxSEO Studio dashboard.
|
|
4405
4612
|
|
|
4406
|
-
|
|
4613
|
+
12. **\uD83E\uDDF9 URL Slug Engine & Schema.org Graphs:**
|
|
4407
4614
|
- \`cleanSeoSlug(text, { language })\`: Strips accents, stop words across 10+ languages, and non-alphanumeric noise.
|
|
4408
4615
|
- Outputs Google-validated Schema.org JSON-LD graphs with 0 syntax errors.
|
|
4409
4616
|
`.trim();
|
|
@@ -6552,8 +6759,10 @@ var LynxSeo = {
|
|
|
6552
6759
|
var src_default = LynxSeo;
|
|
6553
6760
|
export {
|
|
6554
6761
|
validateSeoSlug,
|
|
6762
|
+
resolveFeatureIcon,
|
|
6555
6763
|
resolveBuiltInLocations,
|
|
6556
6764
|
resolveBrandIcon,
|
|
6765
|
+
renderUiIconSvg,
|
|
6557
6766
|
renderBrandIconSvg,
|
|
6558
6767
|
getSeoAgentPrompt,
|
|
6559
6768
|
src_default as default,
|
|
@@ -6561,6 +6770,7 @@ export {
|
|
|
6561
6770
|
cleanSeoSlug,
|
|
6562
6771
|
YoastParityEngine,
|
|
6563
6772
|
UrlyticsEngine,
|
|
6773
|
+
UI_ICONS,
|
|
6564
6774
|
TokenQuotaManager,
|
|
6565
6775
|
TechnicalRulesAuditor,
|
|
6566
6776
|
TeamRbacEngine,
|
|
@@ -6580,6 +6790,7 @@ export {
|
|
|
6580
6790
|
RankMathParityEngine,
|
|
6581
6791
|
PseoMatrixEngine,
|
|
6582
6792
|
PSEO_AGENT_SYSTEM_PROMPT,
|
|
6793
|
+
OgImageGenerator,
|
|
6583
6794
|
NGramDensityAnalyzer,
|
|
6584
6795
|
McpSeoServerHub,
|
|
6585
6796
|
MasterMarketingEngine,
|
package/dist/matrix-engine.d.ts
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🖼️ Dynamic OpenGraph (OG) & Social Card Image Generator
|
|
3
|
+
* Generates high-converting 1200x630 SVG social preview cards and URL queries
|
|
4
|
+
* with zero heavy headless browser dependencies (Puppeteer/Playwright free).
|
|
5
|
+
*/
|
|
6
|
+
import { GeneratedPageMeta } from "./matrix-engine";
|
|
7
|
+
export interface OgImageOptions {
|
|
8
|
+
brandName: string;
|
|
9
|
+
title: string;
|
|
10
|
+
badgeText?: string;
|
|
11
|
+
subtitle?: string;
|
|
12
|
+
locationName?: string;
|
|
13
|
+
ratingValue?: number;
|
|
14
|
+
reviewCount?: number;
|
|
15
|
+
brandColor?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare class OgImageGenerator {
|
|
18
|
+
/**
|
|
19
|
+
* Generates a fully formatted URL for dynamic Edge / API OG image generation.
|
|
20
|
+
* e.g. /api/og?title=...&badge=...&city=...
|
|
21
|
+
*/
|
|
22
|
+
static generateOgImageUrl(domain: string, pageMeta: GeneratedPageMeta, brandName: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Generates an ultra-crisp 1200x630 pure SVG image string for social previews.
|
|
25
|
+
*/
|
|
26
|
+
static renderDynamicOgSvg(opts: OgImageOptions): string;
|
|
27
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🎨 Universal Zero-Dependency UI & Feature SVG Icons
|
|
3
|
+
* Provides crisp, modern Lucide-style vector icons for features, trust badges, and UI components.
|
|
4
|
+
* Includes automatic semantic keyword-to-icon detection for feature descriptions.
|
|
5
|
+
*/
|
|
6
|
+
export interface UiIcon {
|
|
7
|
+
name: string;
|
|
8
|
+
viewBox: string;
|
|
9
|
+
svgPath: string;
|
|
10
|
+
}
|
|
11
|
+
export declare const UI_ICONS: Record<string, UiIcon>;
|
|
12
|
+
/**
|
|
13
|
+
* Returns raw inline SVG markup for a standard UI icon.
|
|
14
|
+
*/
|
|
15
|
+
export declare function renderUiIconSvg(iconName: string, className?: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Semantically resolves the most relevant UI icon based on feature text keywords.
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolveFeatureIcon(featureText: string): string;
|
package/package.json
CHANGED
package/src/engine.test.ts
CHANGED
|
@@ -8,6 +8,8 @@ import { UrlyticsEngine } from "./urlytics-engine";
|
|
|
8
8
|
import { KeywordPermutatorEngine } from "./keyword-permutator";
|
|
9
9
|
import { renderBrandIconSvg } from "./brand-icons";
|
|
10
10
|
import { I18nDetector } from "./i18n-detector";
|
|
11
|
+
import { renderUiIconSvg, resolveFeatureIcon } from "./ui-icons";
|
|
12
|
+
import { OgImageGenerator } from "./og-image-generator";
|
|
11
13
|
|
|
12
14
|
describe("Multilingual SEO Slug Engine (10+ Languages)", () => {
|
|
13
15
|
it("English: Strips stop words and years", () => {
|
|
@@ -265,4 +267,43 @@ describe("Audited Reference Engines (Advertools, Santifer, Seonaut)", () => {
|
|
|
265
267
|
expect(telemetry.brandName).toBe("Acme");
|
|
266
268
|
expect(telemetry.servicesCount).toBe(1);
|
|
267
269
|
});
|
|
270
|
+
|
|
271
|
+
it("UI Icons: Renders standard Lucide-style SVG and resolves semantic feature icons", () => {
|
|
272
|
+
const shieldSvg = renderUiIconSvg("shield");
|
|
273
|
+
expect(shieldSvg).toContain("<svg");
|
|
274
|
+
expect(shieldSvg).toContain("viewBox");
|
|
275
|
+
|
|
276
|
+
expect(resolveFeatureIcon("Bank-grade RGPD Security")).toBe("shield");
|
|
277
|
+
expect(resolveFeatureIcon("Real-time automated sync")).toBe("zap");
|
|
278
|
+
expect(resolveFeatureIcon("Analytics dashboard & KPI tracker")).toBe("chart");
|
|
279
|
+
expect(resolveFeatureIcon("Smart AI content generator")).toBe("sparkles");
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it("OgImageGenerator: Generates dynamic 1200x630 SVG and Edge URL query", () => {
|
|
283
|
+
const ogSvg = OgImageGenerator.renderDynamicOgSvg({
|
|
284
|
+
brandName: "Acme Cloud",
|
|
285
|
+
title: "Autopost Facebook & Instagram in Paris",
|
|
286
|
+
badgeText: "SOCIAL MEDIA",
|
|
287
|
+
locationName: "Paris, France",
|
|
288
|
+
ratingValue: 4.9,
|
|
289
|
+
reviewCount: 1280,
|
|
290
|
+
});
|
|
291
|
+
expect(ogSvg).toContain("<svg width=\"1200\" height=\"630\"");
|
|
292
|
+
expect(ogSvg).toContain("Autopost Facebook");
|
|
293
|
+
expect(ogSvg).toContain("4.9 / 5");
|
|
294
|
+
expect(ogSvg).toContain("Paris, France");
|
|
295
|
+
|
|
296
|
+
const engine = new PseoMatrixEngine();
|
|
297
|
+
const page = engine.resolvePage(
|
|
298
|
+
["autopost-facebook", "paris"],
|
|
299
|
+
"https://acme.com",
|
|
300
|
+
{
|
|
301
|
+
services: [{ slug: "autopost-facebook", name: "Autopost Facebook", category: "Social" }],
|
|
302
|
+
},
|
|
303
|
+
{ brandName: "Acme", countries: ["france"] },
|
|
304
|
+
);
|
|
305
|
+
expect(page?.ogImageUrl).toBeDefined();
|
|
306
|
+
expect(page?.ogImageUrl).toContain("/api/og?");
|
|
307
|
+
expect(page?.ogImageUrl).toContain("brand=Acme");
|
|
308
|
+
});
|
|
268
309
|
});
|
package/src/index.ts
CHANGED
|
@@ -17,6 +17,8 @@ export * from "./matrix-engine";
|
|
|
17
17
|
export * from "./built-in-locations";
|
|
18
18
|
export * from "./i18n-detector";
|
|
19
19
|
export * from "./brand-icons";
|
|
20
|
+
export * from "./ui-icons";
|
|
21
|
+
export * from "./og-image-generator";
|
|
20
22
|
export * from "./urlytics-engine";
|
|
21
23
|
export * from "./keyword-permutator";
|
|
22
24
|
export * from "./llm-prompt";
|
package/src/llm-prompt.ts
CHANGED
|
@@ -275,22 +275,30 @@ The SDK equips applications with 9 enterprise-grade SEO engines:
|
|
|
275
275
|
4. **🎨 Vector Brand & Social Icons (\`renderBrandIconSvg\`):**
|
|
276
276
|
- Zero-dependency official SVG vectors for 30+ brands (Facebook, Instagram, LinkedIn, Shopify, Slack, WhatsApp, Google, GitHub, TikTok, YouTube).
|
|
277
277
|
|
|
278
|
-
5.
|
|
278
|
+
5. **✨ Standard UI & Feature Icons (\`renderUiIconSvg\`, \`resolveFeatureIcon\`):**
|
|
279
|
+
- Zero-dependency Lucide-style vector icons (shield, zap, chart, calendar, users, rocket, sparkles, check, lock, clock, phone, mapPin, euro).
|
|
280
|
+
- Semantic keyword auto-resolver: Automatically maps feature copy to the most relevant icon.
|
|
281
|
+
|
|
282
|
+
6. **🖼️ Dynamic OpenGraph (OG) Image Generator (\`OgImageGenerator\`):**
|
|
283
|
+
- \`renderDynamicOgSvg(opts)\`: Generates high-converting 1200x630 SVG social cards with glassmorphism badges, star ratings, and gradient backdrops.
|
|
284
|
+
- \`generateOgImageUrl(domain, pageMeta, brandName)\`: Edge-ready query URL for \`app/api/og/route.ts\`.
|
|
285
|
+
|
|
286
|
+
7. **🏛️ Google Schema.org Rich Graphs (\`ExtendedSchemaGraphBuilder\`):**
|
|
279
287
|
- \`buildAggregateRating\` (Google Gold Stars 4.9★), \`buildAggregateOffer\` (price ranges), \`buildLocalBusiness\` (GPS & opening hours), \`buildSoftwareApplication\`, \`buildHowTo\`, \`buildFAQPage\`, \`buildBreadcrumbs\`.
|
|
280
288
|
|
|
281
|
-
|
|
289
|
+
8. **🔍 URL Decomposition & Analysis (\`UrlyticsEngine\`):**
|
|
282
290
|
- Deep structural directory decomposition, depth tracking, query parameter analysis, and slug tokenization (inspired by advertools).
|
|
283
291
|
|
|
284
|
-
|
|
292
|
+
9. **⚡ SEM Keyword Combinator (\`KeywordPermutatorEngine\`):**
|
|
285
293
|
- Combinatorial matrix generation: Products × Modifiers × Locations with match types (\`[exact]\`, \`"phrase"\`, \`+broad\`) and intent classification.
|
|
286
294
|
|
|
287
|
-
|
|
295
|
+
10. **⚖️ Legal & Editorial Compliance (\`LegalDisclaimerEngine\`):**
|
|
288
296
|
- 7 SeedRank-grade disclaimer templates, 90-day staleness verification, and multi-language anti-disparagement guardian.
|
|
289
297
|
|
|
290
|
-
|
|
298
|
+
11. **📊 Telemetry & Cloud Metering (\`matrixEngine.getTelemetryPayload\` & \`LynxAnalyticsClient\`):**
|
|
291
299
|
- Synchronizes total programmatic pages, indexed hubs, and AI bot crawl hits (GPTBot, ClaudeBot, PerplexityBot) with the LynxSEO Studio dashboard.
|
|
292
300
|
|
|
293
|
-
|
|
301
|
+
12. **🧹 URL Slug Engine & Schema.org Graphs:**
|
|
294
302
|
- \`cleanSeoSlug(text, { language })\`: Strips accents, stop words across 10+ languages, and non-alphanumeric noise.
|
|
295
303
|
- Outputs Google-validated Schema.org JSON-LD graphs with 0 syntax errors.
|
|
296
304
|
`.trim();
|
package/src/matrix-engine.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { cleanSeoSlug } from "./slug-engine";
|
|
|
14
14
|
import { LegalDisclaimerEngine } from "./legal-disclaimers";
|
|
15
15
|
import { ExtendedSchemaGraphBuilder } from "./extended-schemas";
|
|
16
16
|
import { resolveBuiltInLocations } from "./built-in-locations";
|
|
17
|
+
import { OgImageGenerator } from "./og-image-generator";
|
|
17
18
|
|
|
18
19
|
export type MatrixFamily =
|
|
19
20
|
| "local-geo"
|
|
@@ -141,6 +142,7 @@ export interface GeneratedPageMeta {
|
|
|
141
142
|
robots: "index, follow" | "noindex, follow";
|
|
142
143
|
schemaGraph: SchemaOrgGraph;
|
|
143
144
|
disclaimerText?: string;
|
|
145
|
+
ogImageUrl?: string;
|
|
144
146
|
neighboringLinks?: { name: string; url: string }[];
|
|
145
147
|
service?: PseoService;
|
|
146
148
|
location?: PseoLocation;
|
|
@@ -824,6 +826,12 @@ export class PseoMatrixEngine {
|
|
|
824
826
|
allPages.push(...this.generateCalculatorMatrix(domain, data.calculators, options));
|
|
825
827
|
}
|
|
826
828
|
|
|
829
|
+
for (const p of allPages) {
|
|
830
|
+
if (!p.ogImageUrl) {
|
|
831
|
+
p.ogImageUrl = OgImageGenerator.generateOgImageUrl(domain, p, options.brandName);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
827
835
|
return allPages;
|
|
828
836
|
}
|
|
829
837
|
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🖼️ Dynamic OpenGraph (OG) & Social Card Image Generator
|
|
3
|
+
* Generates high-converting 1200x630 SVG social preview cards and URL queries
|
|
4
|
+
* with zero heavy headless browser dependencies (Puppeteer/Playwright free).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { GeneratedPageMeta } from "./matrix-engine";
|
|
8
|
+
|
|
9
|
+
export interface OgImageOptions {
|
|
10
|
+
brandName: string;
|
|
11
|
+
title: string;
|
|
12
|
+
badgeText?: string;
|
|
13
|
+
subtitle?: string;
|
|
14
|
+
locationName?: string;
|
|
15
|
+
ratingValue?: number;
|
|
16
|
+
reviewCount?: number;
|
|
17
|
+
brandColor?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class OgImageGenerator {
|
|
21
|
+
/**
|
|
22
|
+
* Generates a fully formatted URL for dynamic Edge / API OG image generation.
|
|
23
|
+
* e.g. /api/og?title=...&badge=...&city=...
|
|
24
|
+
*/
|
|
25
|
+
static generateOgImageUrl(domain: string, pageMeta: GeneratedPageMeta, brandName: string): string {
|
|
26
|
+
const cleanDomain = domain.replace(/\/+$/, "");
|
|
27
|
+
const params = new URLSearchParams({
|
|
28
|
+
title: pageMeta.h1 || pageMeta.title,
|
|
29
|
+
badge: pageMeta.matrixFamily.toUpperCase(),
|
|
30
|
+
desc: pageMeta.description ? pageMeta.description.slice(0, 120) : "",
|
|
31
|
+
brand: brandName,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (pageMeta.location) {
|
|
35
|
+
params.set("loc", `${pageMeta.location.name}, ${pageMeta.location.country}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return `${cleanDomain}/api/og?${params.toString()}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Generates an ultra-crisp 1200x630 pure SVG image string for social previews.
|
|
43
|
+
*/
|
|
44
|
+
static renderDynamicOgSvg(opts: OgImageOptions): string {
|
|
45
|
+
const brand = opts.brandName || "LynxSEO";
|
|
46
|
+
const badge = opts.badgeText || "OFFICIAL";
|
|
47
|
+
const title = opts.title.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
48
|
+
const loc = opts.locationName ? `📍 ${opts.locationName}` : "⚡ Instant Cloud Setup";
|
|
49
|
+
const rating = opts.ratingValue ?? 4.9;
|
|
50
|
+
const reviews = opts.reviewCount ?? 1280;
|
|
51
|
+
const accent = opts.brandColor || "#6366F1";
|
|
52
|
+
|
|
53
|
+
return `<svg width="1200" height="630" viewBox="0 0 1200 630" xmlns="http://www.w3.org/2000/svg">
|
|
54
|
+
<defs>
|
|
55
|
+
<linearGradient id="bgGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
56
|
+
<stop offset="0%" stop-color="#090D16"/>
|
|
57
|
+
<stop offset="50%" stop-color="#0F172A"/>
|
|
58
|
+
<stop offset="100%" stop-color="#020617"/>
|
|
59
|
+
</linearGradient>
|
|
60
|
+
<linearGradient id="textGrad" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
61
|
+
<stop offset="0%" stop-color="#FFFFFF"/>
|
|
62
|
+
<stop offset="100%" stop-color="#CBD5E1"/>
|
|
63
|
+
</linearGradient>
|
|
64
|
+
<filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
|
|
65
|
+
<feGaussianBlur stdDeviation="80" result="blur" />
|
|
66
|
+
</filter>
|
|
67
|
+
</defs>
|
|
68
|
+
|
|
69
|
+
<!-- Background -->
|
|
70
|
+
<rect width="1200" height="630" fill="url(#bgGrad)"/>
|
|
71
|
+
|
|
72
|
+
<!-- Subtle Ambient Glow -->
|
|
73
|
+
<circle cx="200" cy="150" r="220" fill="${accent}" opacity="0.15" filter="url(#glow)"/>
|
|
74
|
+
<circle cx="1000" cy="480" r="260" fill="${accent}" opacity="0.12" filter="url(#glow)"/>
|
|
75
|
+
|
|
76
|
+
<!-- Top Glassmorphism Badge -->
|
|
77
|
+
<g transform="translate(80, 80)">
|
|
78
|
+
<rect width="220" height="42" rx="21" fill="${accent}" fill-opacity="0.15" stroke="${accent}" stroke-opacity="0.4" stroke-width="1.5"/>
|
|
79
|
+
<text x="110" y="26" fill="#FFFFFF" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="14" font-weight="700" text-anchor="middle" letter-spacing="1.5">${badge}</text>
|
|
80
|
+
</g>
|
|
81
|
+
|
|
82
|
+
<!-- Location / Scope Pill -->
|
|
83
|
+
<g transform="translate(320, 80)">
|
|
84
|
+
<rect width="260" height="42" rx="21" fill="#1E293B" fill-opacity="0.6" stroke="#334155" stroke-width="1"/>
|
|
85
|
+
<text x="130" y="26" fill="#94A3B8" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="14" font-weight="600" text-anchor="middle">${loc}</text>
|
|
86
|
+
</g>
|
|
87
|
+
|
|
88
|
+
<!-- Giant Title -->
|
|
89
|
+
<text x="80" y="250" fill="url(#textGrad)" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="52" font-weight="900" letter-spacing="-1.5">
|
|
90
|
+
<tspan x="80" dy="0">${title.slice(0, 38)}</tspan>
|
|
91
|
+
${title.length > 38 ? `<tspan x="80" dy="68">${title.slice(38, 80)}</tspan>` : ""}
|
|
92
|
+
</text>
|
|
93
|
+
|
|
94
|
+
<!-- Trust Stars & Social Proof -->
|
|
95
|
+
<g transform="translate(80, 470)">
|
|
96
|
+
<text x="0" y="32" fill="#FBBF24" font-size="24">★★★★★</text>
|
|
97
|
+
<text x="140" y="30" fill="#F8FAFC" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="20" font-weight="800">${rating} / 5</text>
|
|
98
|
+
<text x="210" y="30" fill="#64748B" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="16" font-weight="500">(${reviews.toLocaleString()} verified reviews)</text>
|
|
99
|
+
</g>
|
|
100
|
+
|
|
101
|
+
<!-- Footer Brand -->
|
|
102
|
+
<g transform="translate(80, 540)">
|
|
103
|
+
<line x1="0" y1="0" x2="1040" y2="0" stroke="#1E293B" stroke-width="1.5"/>
|
|
104
|
+
<text x="0" y="42" fill="#FFFFFF" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="20" font-weight="800">${brand}</text>
|
|
105
|
+
<text x="1040" y="42" fill="#64748B" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="16" font-weight="500" text-anchor="end">Engineered with @lynxflow/seo-engine</text>
|
|
106
|
+
</g>
|
|
107
|
+
</svg>`;
|
|
108
|
+
}
|
|
109
|
+
}
|
package/src/ui-icons.ts
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🎨 Universal Zero-Dependency UI & Feature SVG Icons
|
|
3
|
+
* Provides crisp, modern Lucide-style vector icons for features, trust badges, and UI components.
|
|
4
|
+
* Includes automatic semantic keyword-to-icon detection for feature descriptions.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface UiIcon {
|
|
8
|
+
name: string;
|
|
9
|
+
viewBox: string;
|
|
10
|
+
svgPath: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const UI_ICONS: Record<string, UiIcon> = {
|
|
14
|
+
zap: {
|
|
15
|
+
name: "Zap / Lightning",
|
|
16
|
+
viewBox: "0 0 24 24",
|
|
17
|
+
svgPath: '<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>',
|
|
18
|
+
},
|
|
19
|
+
shield: {
|
|
20
|
+
name: "Shield / Security",
|
|
21
|
+
viewBox: "0 0 24 24",
|
|
22
|
+
svgPath: '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>',
|
|
23
|
+
},
|
|
24
|
+
star: {
|
|
25
|
+
name: "Star / Rating",
|
|
26
|
+
viewBox: "0 0 24 24",
|
|
27
|
+
svgPath: '<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" fill="currentColor"/>',
|
|
28
|
+
},
|
|
29
|
+
chart: {
|
|
30
|
+
name: "Chart / Analytics",
|
|
31
|
+
viewBox: "0 0 24 24",
|
|
32
|
+
svgPath: '<line x1="18" y1="20" x2="18" y2="10" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><line x1="12" y1="20" x2="12" y2="4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><line x1="6" y1="20" x2="6" y2="14" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>',
|
|
33
|
+
},
|
|
34
|
+
calendar: {
|
|
35
|
+
name: "Calendar / Scheduling",
|
|
36
|
+
viewBox: "0 0 24 24",
|
|
37
|
+
svgPath: '<rect x="3" y="4" width="18" height="18" rx="2" ry="2" fill="none" stroke="currentColor" stroke-width="2"/><line x1="16" y1="2" x2="16" y2="6" stroke="currentColor" stroke-width="2"/><line x1="8" y1="2" x2="8" y2="6" stroke="currentColor" stroke-width="2"/><line x1="3" y1="10" x2="21" y2="10" stroke="currentColor" stroke-width="2"/>',
|
|
38
|
+
},
|
|
39
|
+
users: {
|
|
40
|
+
name: "Users / Team",
|
|
41
|
+
viewBox: "0 0 24 24",
|
|
42
|
+
svgPath: '<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" fill="none" stroke="currentColor" stroke-width="2"/><circle cx="9" cy="7" r="4" fill="none" stroke="currentColor" stroke-width="2"/><path d="M23 21v-2a4 4 0 0 0-3-3.87" fill="none" stroke="currentColor" stroke-width="2"/><path d="M16 3.13a4 4 0 0 1 0 7.75" fill="none" stroke="currentColor" stroke-width="2"/>',
|
|
43
|
+
},
|
|
44
|
+
rocket: {
|
|
45
|
+
name: "Rocket / Speed",
|
|
46
|
+
viewBox: "0 0 24 24",
|
|
47
|
+
svgPath: '<path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z" fill="none" stroke="currentColor" stroke-width="2"/><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z" fill="none" stroke="currentColor" stroke-width="2"/>',
|
|
48
|
+
},
|
|
49
|
+
sparkles: {
|
|
50
|
+
name: "Sparkles / AI",
|
|
51
|
+
viewBox: "0 0 24 24",
|
|
52
|
+
svgPath: '<path d="m12 3-1.9 5.8a2 2 0 0 1-1.3 1.3L3 12l5.8 1.9a2 2 0 0 1 1.3 1.3L12 21l1.9-5.8a2 2 0 0 1 1.3-1.3L21 12l-5.8-1.9a2 2 0 0 1-1.3-1.3L12 3z" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>',
|
|
53
|
+
},
|
|
54
|
+
check: {
|
|
55
|
+
name: "Check / Success",
|
|
56
|
+
viewBox: "0 0 24 24",
|
|
57
|
+
svgPath: '<polyline points="20 6 9 17 4 12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>',
|
|
58
|
+
},
|
|
59
|
+
lock: {
|
|
60
|
+
name: "Lock / Privacy",
|
|
61
|
+
viewBox: "0 0 24 24",
|
|
62
|
+
svgPath: '<rect x="3" y="11" width="18" height="11" rx="2" ry="2" fill="none" stroke="currentColor" stroke-width="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4" fill="none" stroke="currentColor" stroke-width="2"/>',
|
|
63
|
+
},
|
|
64
|
+
clock: {
|
|
65
|
+
name: "Clock / 24-7",
|
|
66
|
+
viewBox: "0 0 24 24",
|
|
67
|
+
svgPath: '<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="2"/><polyline points="12 6 12 12 16 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>',
|
|
68
|
+
},
|
|
69
|
+
phone: {
|
|
70
|
+
name: "Phone / Contact",
|
|
71
|
+
viewBox: "0 0 24 24",
|
|
72
|
+
svgPath: '<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z" fill="none" stroke="currentColor" stroke-width="2"/>',
|
|
73
|
+
},
|
|
74
|
+
mapPin: {
|
|
75
|
+
name: "Map Pin / Local",
|
|
76
|
+
viewBox: "0 0 24 24",
|
|
77
|
+
svgPath: '<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" fill="none" stroke="currentColor" stroke-width="2"/><circle cx="12" cy="10" r="3" fill="none" stroke="currentColor" stroke-width="2"/>',
|
|
78
|
+
},
|
|
79
|
+
euro: {
|
|
80
|
+
name: "Euro / Pricing",
|
|
81
|
+
viewBox: "0 0 24 24",
|
|
82
|
+
svgPath: '<path d="M4 10h12M4 14h9M19 6a7.7 7.7 0 0 0-5.2-2A7.9 7.9 0 0 0 6 12a7.9 7.9 0 0 0 7.8 8 7.7 7.7 0 0 0 5.2-2" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>',
|
|
83
|
+
},
|
|
84
|
+
arrowRight: {
|
|
85
|
+
name: "Arrow Right",
|
|
86
|
+
viewBox: "0 0 24 24",
|
|
87
|
+
svgPath: '<line x1="5" y1="12" x2="19" y2="12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><polyline points="12 5 19 12 12 19" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>',
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Returns raw inline SVG markup for a standard UI icon.
|
|
93
|
+
*/
|
|
94
|
+
export function renderUiIconSvg(iconName: string, className: string = "w-5 h-5 inline-block"): string {
|
|
95
|
+
const icon = UI_ICONS[iconName.toLowerCase()] || UI_ICONS.check;
|
|
96
|
+
return `<svg class="${className}" viewBox="${icon.viewBox}" aria-hidden="true">${icon.svgPath}</svg>`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Semantically resolves the most relevant UI icon based on feature text keywords.
|
|
101
|
+
*/
|
|
102
|
+
export function resolveFeatureIcon(featureText: string): string {
|
|
103
|
+
const lower = featureText.toLowerCase();
|
|
104
|
+
|
|
105
|
+
if (lower.includes("secur") || lower.includes("rgpd") || lower.includes("gdpr") || lower.includes("protect") || lower.includes("bank")) {
|
|
106
|
+
return "shield";
|
|
107
|
+
}
|
|
108
|
+
if (lower.includes("sync") || lower.includes("speed") || lower.includes("instant") || lower.includes("real-time") || lower.includes("fast") || lower.includes("rapide")) {
|
|
109
|
+
return "zap";
|
|
110
|
+
}
|
|
111
|
+
if (lower.includes("ai") || lower.includes("ia") || lower.includes("auto") || lower.includes("smart") || lower.includes("intel")) {
|
|
112
|
+
return "sparkles";
|
|
113
|
+
}
|
|
114
|
+
if (lower.includes("chart") || lower.includes("analyt") || lower.includes("report") || lower.includes("stat") || lower.includes("roi") || lower.includes("kpi")) {
|
|
115
|
+
return "chart";
|
|
116
|
+
}
|
|
117
|
+
if (lower.includes("schedul") || lower.includes("calen") || lower.includes("plan") || lower.includes("agenda")) {
|
|
118
|
+
return "calendar";
|
|
119
|
+
}
|
|
120
|
+
if (lower.includes("user") || lower.includes("team") || lower.includes("collab") || lower.includes("client") || lower.includes("contact")) {
|
|
121
|
+
return "users";
|
|
122
|
+
}
|
|
123
|
+
if (lower.includes("price") || lower.includes("tarif") || lower.includes("cost") || lower.includes("devis") || lower.includes("factur")) {
|
|
124
|
+
return "euro";
|
|
125
|
+
}
|
|
126
|
+
if (lower.includes("local") || lower.includes("city") || lower.includes("ville") || lower.includes("map") || lower.includes("gps")) {
|
|
127
|
+
return "mapPin";
|
|
128
|
+
}
|
|
129
|
+
if (lower.includes("24/7") || lower.includes("support") || lower.includes("hour") || lower.includes("time") || lower.includes("temps")) {
|
|
130
|
+
return "clock";
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return "zap";
|
|
134
|
+
}
|