@juspay/neurolink
Version:
Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio
406 lines • 13.3 kB
JavaScript
/**
* Slide Type Inference Utility
*
* Automatically infers the best slide type and bullet style based on:
* 1. Title keywords (e.g., "Agenda", "Summary", "Key Takeaways")
* 2. Content patterns (e.g., numbered items, checkmarks in text)
* 3. AI response structure
*
* This helps ensure consistent slide rendering when AI doesn't explicitly
* specify a slide type, or when we want to normalize AI responses.
*/
// ============================================================================
// KEYWORD-BASED SLIDE TYPE INFERENCE
// ============================================================================
/**
* Keywords that suggest specific slide types
* Ordered by specificity - more specific patterns first
*/
const TITLE_KEYWORD_PATTERNS = [
// Agenda / Table of Contents - numbered list
{
patterns: [
/\bagenda\b/i,
/\btable\s+of\s+contents\b/i,
/\boutline\b/i,
/\boverview\b/i,
/\bwhat\s+we('ll)?\s+cover\b/i,
/\btoday('s)?\s+topics?\b/i,
/\bsession\s+outline\b/i,
],
slideType: "agenda",
bulletStyle: "number",
},
// Conclusion / Summary - checkmark
{
patterns: [
/\bconclusion\b/i,
/\bsummary\b/i,
/\bkey\s+takeaways?\b/i,
/\btakeaways?\b/i,
/\brecap\b/i,
/\bin\s+summary\b/i,
/\bwhat\s+we('ve)?\s+learned\b/i,
/\bmain\s+points?\b/i,
/\bkey\s+points?\b/i,
/\bhighlights?\b/i,
/\bachievements?\b/i,
/\baccomplishments?\b/i,
],
slideType: "conclusion",
bulletStyle: "checkmark",
},
// Closing / Thank You - checkmark
{
patterns: [
/\bthank\s+you\b/i,
/\bthanks?\b/i,
/\bquestions?\b\??/i,
/\bq\s*&\s*a\b/i,
/\bcontact(\s+us)?\b/i,
/\bnext\s+steps?\b/i,
/\baction\s+items?\b/i,
/\blet('s)?\s+connect\b/i,
/\bget\s+(in\s+)?touch\b/i,
],
slideType: "closing",
bulletStyle: "checkmark",
},
// Comparison - arrow bullets
{
patterns: [
/\bcomparison\b/i,
/\bcompare\b/i,
/\bvs\.?\b/i,
/\bversus\b/i,
/\bbefore\s+(and|&|vs\.?)\s+after\b/i,
/\bpros?\s+(and|&|vs\.?)\s+cons?\b/i,
/\badvantages?\s+(and|&|vs\.?)\s+disadvantages?\b/i,
/\bbenefits?\s+(and|&|vs\.?)\s+risks?\b/i,
/\bold\s+vs\.?\s+new\b/i,
],
slideType: "comparison",
bulletStyle: "arrow",
},
// Process / Steps - numbered
{
patterns: [
/\bprocess\b/i,
/\bsteps?\b/i,
/\bstep[\s-]+by[\s-]+step\b/i,
/\bhow\s+to\b/i,
/\bworkflow\b/i,
/\bprocedure\b/i,
/\bmethodology\b/i,
/\d+\s+steps?\s+to\b/i,
/\bimplementation\s+steps?\b/i,
/\bgetting\s+started\b/i,
],
slideType: "numbered-list",
bulletStyle: "number",
},
// Features / Benefits - disc (but could be checkmark for benefits)
{
patterns: [
/\bfeatures?\b/i,
/\bcapabilities?\b/i,
/\bwhat\s+(we|it)\s+offers?\b/i,
/\bour\s+offerings?\b/i,
],
slideType: "features",
bulletStyle: "disc",
},
{
patterns: [
/\bbenefits?\b/i,
/\badvantages?\b/i,
/\bwhy\s+choose\b/i,
/\breasons?\s+to\b/i,
/\bvalue\s+proposition\b/i,
],
slideType: "content",
bulletStyle: "checkmark",
},
// Goals / Objectives - checkmark
{
patterns: [
/\bgoals?\b/i,
/\bobjectives?\b/i,
/\btargets?\b/i,
/\baims?\b/i,
/\bour\s+mission\b/i,
/\bwhat\s+we\s+aim\s+for\b/i,
],
slideType: "content",
bulletStyle: "checkmark",
},
// Challenges / Risks - arrow
{
patterns: [
/\bchallenges?\b/i,
/\brisks?\b/i,
/\bobstacles?\b/i,
/\bbarriers?\b/i,
/\bconcerns?\b/i,
/\bissues?\b/i,
/\bproblems?\b/i,
],
slideType: "content",
bulletStyle: "arrow",
},
// Requirements / Checklist - checkmark
{
patterns: [
/\brequirements?\b/i,
/\bchecklist\b/i,
/\bprerequisites?\b/i,
/\bwhat\s+you\s+need\b/i,
/\bmust\s+haves?\b/i,
/\bessentials?\b/i,
],
slideType: "content",
bulletStyle: "checkmark",
},
];
/**
* Infer slide type and bullet style from title text
*/
export function inferFromTitle(title) {
const cleanTitle = title.trim();
for (const { patterns, slideType, bulletStyle } of TITLE_KEYWORD_PATTERNS) {
for (const pattern of patterns) {
if (pattern.test(cleanTitle)) {
return { slideType, bulletStyle };
}
}
}
return { slideType: null, bulletStyle: null };
}
// ============================================================================
// CONTENT-BASED INFERENCE
// ============================================================================
/**
* Content patterns that suggest specific bullet styles
*/
const CONTENT_PATTERNS = [
// Numbered content (1., 2., Step 1, etc.)
{ pattern: /^\d+[.)]\s+/m, bulletStyle: "number" },
{ pattern: /^step\s+\d+/im, bulletStyle: "number" },
// Checkmark content (✓, ✔, [x], etc.)
{ pattern: /^[✓✔☑]\s*/m, bulletStyle: "checkmark" },
{ pattern: /^\[x\]\s*/im, bulletStyle: "checkmark" },
{ pattern: /^done:/im, bulletStyle: "checkmark" },
{ pattern: /^completed:/im, bulletStyle: "checkmark" },
// Arrow content (→, ->, =>)
{ pattern: /^[→➜➡]\s*/m, bulletStyle: "arrow" },
{ pattern: /^->\s*/m, bulletStyle: "arrow" },
{ pattern: /^=>\s*/m, bulletStyle: "arrow" },
];
/**
* Infer bullet style from content text patterns
*/
export function inferBulletStyleFromContent(bullets) {
if (!bullets || bullets.length === 0) {
return null;
}
// Check each bullet for patterns
for (const bullet of bullets) {
for (const { pattern, bulletStyle } of CONTENT_PATTERNS) {
if (pattern.test(bullet.text)) {
return bulletStyle;
}
}
}
return null;
}
// ============================================================================
// SLIDE TYPE NORMALIZATION
// ============================================================================
/**
* Get the appropriate bullet style for a slide type
* This is the single source of truth for type → style mapping
*/
export function getBulletStyleForSlideType(slideType) {
switch (slideType) {
// Numbered slides
case "agenda":
case "numbered-list":
return "number";
// Checkmark slides
case "conclusion":
case "closing":
case "thank-you":
return "checkmark";
// Arrow slides
case "comparison":
return "arrow";
// No bullet slides
case "features":
case "icons":
case "quote":
case "statistics":
case "title":
case "section-header":
return "none";
// Default disc for content slides
case "content":
case "bullets":
case "two-column":
case "three-column":
case "split-content":
default:
return "disc";
}
}
/**
* Normalize slide type and apply appropriate bullet style
*
* This function:
* 1. Tries to infer slide type from title keywords
* 2. Applies appropriate bullet style based on slide type
* 3. Can be used to enhance AI responses
*/
export function normalizeSlideWithInference(title, currentType, content) {
// First, try to infer from title
const { slideType: inferredType, bulletStyle: inferredStyle } = inferFromTitle(title);
if (inferredType) {
return {
type: inferredType,
bulletStyle: inferredStyle || getBulletStyleForSlideType(inferredType),
wasInferred: true,
};
}
// If content has patterns, use those for bullet style
const contentStyle = content.bullets
? inferBulletStyleFromContent(content.bullets)
: null;
if (contentStyle) {
return {
type: currentType,
bulletStyle: contentStyle,
wasInferred: true,
};
}
// Default: use the current type's default style
return {
type: currentType,
bulletStyle: getBulletStyleForSlideType(currentType),
wasInferred: false,
};
}
/**
* Apply inferred bullet style to all bullets in content
* Returns a new content object with bullet styles applied
*/
export function applyBulletStyleToContent(content, bulletStyle) {
// Check if any bullets exist (main content or columns)
const hasAnyBullets = (content.bullets?.length ?? 0) > 0 ||
(content.leftColumn?.bullets?.length ?? 0) > 0 ||
(content.rightColumn?.bullets?.length ?? 0) > 0 ||
(content.centerColumn?.bullets?.length ?? 0) > 0;
if (!hasAnyBullets) {
return content;
}
return {
...content,
bullets: content.bullets?.map((bullet) => ({
...bullet,
// Only set bulletStyle if not already specified by AI
bulletStyle: bullet.bulletStyle || bulletStyle,
})),
// Also apply to column bullets if present
leftColumn: content.leftColumn
? {
...content.leftColumn,
bullets: content.leftColumn.bullets?.map((b) => ({
...b,
bulletStyle: b.bulletStyle || bulletStyle,
})),
}
: undefined,
rightColumn: content.rightColumn
? {
...content.rightColumn,
bullets: content.rightColumn.bullets?.map((b) => ({
...b,
bulletStyle: b.bulletStyle || bulletStyle,
})),
}
: undefined,
centerColumn: content.centerColumn
? {
...content.centerColumn,
bullets: content.centerColumn.bullets?.map((b) => ({
...b,
bulletStyle: b.bulletStyle || bulletStyle,
})),
}
: undefined,
};
}
// ============================================================================
// SLIDE TYPE DESCRIPTIONS (for AI guidance)
// ============================================================================
/**
* Human-readable descriptions for when to use each slide type
* Can be used in AI prompts for better guidance
*/
export const SLIDE_TYPE_GUIDANCE = {
// Content types with specific bullet styles
agenda: {
use: "For table of contents, outline, overview, or 'what we'll cover' slides",
bulletStyle: "number",
example: "Agenda, Outline, Today's Topics, What We'll Cover",
},
conclusion: {
use: "For summary, key takeaways, recap, or main points slides",
bulletStyle: "checkmark",
example: "Conclusion, Summary, Key Takeaways, What We Learned",
},
closing: {
use: "For thank you, Q&A, contact, or next steps slides",
bulletStyle: "checkmark",
example: "Thank You, Questions?, Contact Us, Next Steps",
},
"numbered-list": {
use: "For step-by-step processes, how-to guides, or ranked lists",
bulletStyle: "number",
example: "5 Steps to Success, How To Get Started, Implementation Process",
},
comparison: {
use: "For before/after, pros/cons, or side-by-side comparisons",
bulletStyle: "arrow",
example: "Before vs After, Pros and Cons, Old vs New",
},
content: {
use: "For general content with standard bullet points",
bulletStyle: "disc",
example: "Features, Details, Information, Background",
},
bullets: {
use: "For enhanced bullet points with optional icons",
bulletStyle: "disc",
example: "Key Points, Details, Highlights",
},
};
/**
* Generate AI guidance text for slide types
*/
export function getSlideTypeGuidanceForAI() {
const lines = [
"SLIDE TYPE SELECTION GUIDE:",
"",
"Choose the slide type based on the title/content:",
"",
];
for (const [type, info] of Object.entries(SLIDE_TYPE_GUIDANCE)) {
lines.push(`• "${type}": ${info.use}`);
lines.push(` → Uses: ${info.bulletStyle} bullets`);
lines.push(` → Examples: ${info.example}`);
lines.push("");
}
lines.push("If the AI specifies a bulletStyle in the content, that takes priority.");
lines.push("Otherwise, the slide type determines the bullet style automatically.");
return lines.join("\n");
}
//# sourceMappingURL=slideTypeInference.js.map