@thanh01.pmt/presentation-kit 0.2.15 → 0.2.16

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.
@@ -3375,6 +3375,219 @@ function generateHtmlShell(options) {
3375
3375
  </html>`;
3376
3376
  }
3377
3377
 
3378
+ // src/html-engine/normalizer.ts
3379
+ function normalizeSlideSlots(slide) {
3380
+ if (!slide || typeof slide !== "object") {
3381
+ return { title: "Slide" };
3382
+ }
3383
+ const slots = {
3384
+ ...slide,
3385
+ ...slide.slots || {}
3386
+ };
3387
+ delete slots.slots;
3388
+ slots.title = String(slots.title || slide.title || slide.heading || slide.name || "").trim();
3389
+ slots.tag = String(slots.tag || slide.tag || slide.badge || "").trim();
3390
+ const rawContent = typeof slots.content === "string" && slots.content.trim() ? slots.content.trim() : typeof slide.content === "string" && slide.content.trim() ? slide.content.trim() : "";
3391
+ const layoutId = slide.layoutId || "hero-cover";
3392
+ switch (layoutId) {
3393
+ case "hero-cover": {
3394
+ slots.subtitle = slots.subtitle || slide.subtitle || slots.desc || rawContent || "";
3395
+ slots.author = slots.author || slide.author || "";
3396
+ slots.date = slots.date || slide.date || "";
3397
+ break;
3398
+ }
3399
+ case "split-concept-code": {
3400
+ if (!slots.code) {
3401
+ if (slide.code) {
3402
+ slots.code = slide.code;
3403
+ } else if (rawContent) {
3404
+ const fenceMatch = rawContent.match(/```(?:\w+)?\s*\n?([\s\S]*?)\n?```/);
3405
+ if (fenceMatch) {
3406
+ slots.code = fenceMatch[1].trim();
3407
+ } else if (/Code(?:\s+snippet)?:\s*/i.test(rawContent)) {
3408
+ const parts = rawContent.split(/Code(?:\s+snippet)?:\s*/i);
3409
+ if (parts[1] && parts[1].trim()) {
3410
+ slots.code = parts[1].trim();
3411
+ }
3412
+ }
3413
+ }
3414
+ }
3415
+ if (!Array.isArray(slots.points) || slots.points.length === 0) {
3416
+ if (Array.isArray(slide.points) && slide.points.length > 0) {
3417
+ slots.points = slide.points;
3418
+ } else if (rawContent) {
3419
+ const textOnly = rawContent.replace(/```[\s\S]*?```/g, "").replace(/Code(?:\s+snippet)?:\s*[\s\S]*/i, "").trim();
3420
+ const lines = textOnly.split(/\n|\.\s+/).map((l) => l.replace(/^(?:Concept:\s*|[-*•]|\d+\.)\s*/i, "").trim()).filter((l) => l.length > 3);
3421
+ slots.points = lines.length > 0 ? lines.slice(0, 4) : [textOnly || slots.title || "Core Concept"];
3422
+ }
3423
+ }
3424
+ if (!slots.code || slots.code === "...") {
3425
+ const lang = (slots.language || "code").toLowerCase();
3426
+ if (lang.includes("swift")) {
3427
+ slots.code = `// ${slots.title || "Swift Implementation"}
3428
+ import SwiftUI
3429
+
3430
+ struct PreviewView: View {
3431
+ var body: some View {
3432
+ Text("${slots.title || "Hello, World!"}")
3433
+ .padding()
3434
+ }
3435
+ }`;
3436
+ slots.language = slots.language || "swift";
3437
+ } else if (lang.includes("py")) {
3438
+ slots.code = `# ${slots.title || "Python Implementation"}
3439
+ def execute():
3440
+ print("${slots.title || "Ready"}")
3441
+
3442
+ execute()`;
3443
+ slots.language = slots.language || "python";
3444
+ } else {
3445
+ slots.code = `// ${slots.title || "Code Example"}
3446
+ function main() {
3447
+ console.log("${slots.title || "Executing"}");
3448
+ }
3449
+ main();`;
3450
+ slots.language = slots.language || "typescript";
3451
+ }
3452
+ }
3453
+ break;
3454
+ }
3455
+ case "two-columns-compare": {
3456
+ slots.col1Title = slots.col1Title || slide.col1Title || "Approach A";
3457
+ slots.col2Title = slots.col2Title || slide.col2Title || "Approach B";
3458
+ if (!Array.isArray(slots.col1Items) || slots.col1Items.length === 0) {
3459
+ if (Array.isArray(slide.col1Items) && slide.col1Items.length > 0) {
3460
+ slots.col1Items = slide.col1Items;
3461
+ } else if (rawContent) {
3462
+ const items = rawContent.split(/\n|\.\s+/).map((l) => l.replace(/^(?:[-*•]|\d+\.)\s*/, "").trim()).filter((l) => l.length > 3);
3463
+ const half = Math.max(1, Math.ceil(items.length / 2));
3464
+ slots.col1Items = items.slice(0, half);
3465
+ if (!slots.col2Items || !Array.isArray(slots.col2Items)) {
3466
+ slots.col2Items = items.slice(half);
3467
+ }
3468
+ }
3469
+ }
3470
+ slots.col1Items = Array.isArray(slots.col1Items) && slots.col1Items.length > 0 ? slots.col1Items : ["Primary Characteristics", "Standard Practice"];
3471
+ slots.col2Items = Array.isArray(slots.col2Items) && slots.col2Items.length > 0 ? slots.col2Items : Array.isArray(slide.col2Items) ? slide.col2Items : ["Alternative Paradigm", "Trade-offs & Edge Cases"];
3472
+ break;
3473
+ }
3474
+ case "three-cards-grid": {
3475
+ if (!Array.isArray(slots.cards) || slots.cards.length === 0) {
3476
+ if (Array.isArray(slide.cards) && slide.cards.length > 0) {
3477
+ slots.cards = slide.cards;
3478
+ } else if (rawContent) {
3479
+ const cardTokens = rawContent.split(/Card\s*\d+:\s*|(?:\r?\n){2,}|\.\s+/i).map((c) => c.trim()).filter(Boolean);
3480
+ if (cardTokens.length >= 2) {
3481
+ slots.cards = cardTokens.slice(0, 3).map((txt, idx) => {
3482
+ const colonIdx = txt.indexOf(":");
3483
+ const cardTitle = colonIdx > 0 && colonIdx < 30 ? txt.slice(0, colonIdx).trim() : `Pillar ${idx + 1}`;
3484
+ const cardDesc = colonIdx > 0 && colonIdx < 30 ? txt.slice(colonIdx + 1).trim() : txt;
3485
+ return { badge: `0${idx + 1}`, title: cardTitle, desc: cardDesc };
3486
+ });
3487
+ } else {
3488
+ const lines = rawContent.split(/\n|\.\s+/).map((l) => l.replace(/^(?:[-*•]|\d+\.)\s*/, "").trim()).filter((l) => l.length > 3);
3489
+ slots.cards = lines.slice(0, 3).map((line, idx) => ({
3490
+ badge: `0${idx + 1}`,
3491
+ title: line.split(":")[0]?.slice(0, 30) || `Pillar 0${idx + 1}`,
3492
+ desc: line
3493
+ }));
3494
+ }
3495
+ }
3496
+ }
3497
+ if (!Array.isArray(slots.cards) || slots.cards.length === 0) {
3498
+ slots.cards = [
3499
+ { badge: "01", title: "Foundation", desc: rawContent || "Core fundamentals and design principles" },
3500
+ { badge: "02", title: "Architecture", desc: "Component relationships and data flow patterns" },
3501
+ { badge: "03", title: "Practice", desc: "Real-world application and hands-on synthesis" }
3502
+ ];
3503
+ }
3504
+ break;
3505
+ }
3506
+ case "timeline-steps": {
3507
+ if (!Array.isArray(slots.steps) || slots.steps.length === 0) {
3508
+ if (Array.isArray(slide.steps) && slide.steps.length > 0) {
3509
+ slots.steps = slide.steps;
3510
+ } else if (rawContent) {
3511
+ const stepMatches = rawContent.split(/(?:\d+\.|\bStep\s*\d+:)\s*/i).map((s) => s.trim()).filter(Boolean);
3512
+ if (stepMatches.length > 0) {
3513
+ slots.steps = stepMatches.slice(0, 5).map((txt, idx) => {
3514
+ const periodIdx = txt.indexOf(".");
3515
+ const stepTitle = periodIdx > 0 && periodIdx < 35 ? txt.slice(0, periodIdx).trim() : txt.slice(0, 30);
3516
+ const stepDesc = periodIdx > 0 && periodIdx < 35 ? txt.slice(periodIdx + 1).trim() : txt;
3517
+ return { stepNum: idx + 1, title: stepTitle || `Step ${idx + 1}`, desc: stepDesc || stepTitle };
3518
+ });
3519
+ }
3520
+ }
3521
+ }
3522
+ if (!Array.isArray(slots.steps) || slots.steps.length === 0) {
3523
+ slots.steps = [
3524
+ { stepNum: 1, title: "Analyze", desc: "Deconstruct requirements and constraints" },
3525
+ { stepNum: 2, title: "Implement", desc: rawContent || "Build component logic and structure" },
3526
+ { stepNum: 3, title: "Verify", desc: "Test behavior and validate outcomes" }
3527
+ ];
3528
+ }
3529
+ break;
3530
+ }
3531
+ case "metric-callout": {
3532
+ slots.desc = slots.desc || slide.desc || rawContent || "";
3533
+ slots.metricLabel = slots.metricLabel || slide.metricLabel || "KEY METRIC";
3534
+ if (!slots.metric) {
3535
+ const numMatch = (slots.desc || slots.title).match(/(\d+(?:\.\d+)?%?|O\([^)]+\)|\d+x|\b[A-Z0-9_-]{2,}\b)/i);
3536
+ slots.metric = numMatch ? numMatch[1] : "100%";
3537
+ }
3538
+ break;
3539
+ }
3540
+ case "checkpoint-quiz": {
3541
+ slots.question = slots.question || slide.question || slots.title || "Check Your Understanding";
3542
+ if (!Array.isArray(slots.options) || slots.options.length === 0) {
3543
+ if (Array.isArray(slide.options) && slide.options.length > 0) {
3544
+ slots.options = slide.options;
3545
+ } else if (rawContent) {
3546
+ const items = rawContent.split(/(?:\d+\.|\b[A-D]\))\s*/i).map((l) => l.trim()).filter((l) => l.length > 3);
3547
+ if (items.length >= 2) {
3548
+ slots.options = items.slice(0, 4).map((text, idx) => ({
3549
+ label: String.fromCharCode(65 + idx),
3550
+ text
3551
+ }));
3552
+ }
3553
+ }
3554
+ }
3555
+ if (!Array.isArray(slots.options) || slots.options.length === 0) {
3556
+ slots.options = [
3557
+ { label: "A", text: "Option A (Core Concept)" },
3558
+ { label: "B", text: "Option B (Alternative Approach)" },
3559
+ { label: "C", text: "Option C (Edge Case)" }
3560
+ ];
3561
+ }
3562
+ slots.explanation = slots.explanation || slide.explanation || "";
3563
+ break;
3564
+ }
3565
+ case "summary-takeaways": {
3566
+ if (!Array.isArray(slots.takeaways) || slots.takeaways.length === 0) {
3567
+ if (Array.isArray(slide.takeaways) && slide.takeaways.length > 0) {
3568
+ slots.takeaways = slide.takeaways;
3569
+ } else if (rawContent) {
3570
+ const lines = rawContent.split(/\n|\.\s+/).map((l) => l.replace(/^(?:[-*•]|\d+\.)\s*/, "").trim()).filter((l) => l.length > 3);
3571
+ slots.takeaways = lines.length > 0 ? lines.slice(0, 4) : [rawContent];
3572
+ }
3573
+ }
3574
+ if (!Array.isArray(slots.takeaways) || slots.takeaways.length === 0) {
3575
+ slots.takeaways = [
3576
+ slots.title || "Key lesson principles mastered",
3577
+ "Applied hands-on practice through guided examples",
3578
+ "Ready for independent challenge and synthesis"
3579
+ ];
3580
+ }
3581
+ slots.nextStep = slots.nextStep || slide.nextStep || "Next: Practical Hands-On Lab";
3582
+ break;
3583
+ }
3584
+ }
3585
+ if (rawContent && !slots.rawContent) {
3586
+ slots.rawContent = rawContent;
3587
+ }
3588
+ return slots;
3589
+ }
3590
+
3378
3591
  // src/html-engine/compiler.ts
3379
3592
  function compileHtmlDeck(deckData, options) {
3380
3593
  const theme = options?.themeOverride || resolveHtmlTheme(deckData.theme);
@@ -3382,7 +3595,8 @@ function compileHtmlDeck(deckData, options) {
3382
3595
  const slideCount = Math.max(slides.length, 1);
3383
3596
  const renderedSlides = slides.map((slide, index) => {
3384
3597
  const preset = HTML_SLIDE_PRESETS[slide.layoutId] || HTML_SLIDE_PRESETS["hero-cover"];
3385
- const innerHtml = preset.template(slide.slots || {}, theme);
3598
+ const normalizedSlots = normalizeSlideSlots(slide);
3599
+ const innerHtml = preset.template(normalizedSlots, theme);
3386
3600
  const notesAttr = slide.notes ? ` data-notes="${encodeURIComponent(slide.notes)}"` : "";
3387
3601
  const customClass = slide.customClass ? ` ${slide.customClass}` : "";
3388
3602
  return `
@@ -3416,11 +3630,12 @@ exports.compileHtmlDeck = compileHtmlDeck;
3416
3630
  exports.generateHtmlShell = generateHtmlShell;
3417
3631
  exports.getSlideLayoutPresetById = getSlideLayoutPresetById;
3418
3632
  exports.getSlideLayoutPresetsByCategory = getSlideLayoutPresetsByCategory;
3633
+ exports.normalizeSlideSlots = normalizeSlideSlots;
3419
3634
  exports.removeSlideElementFromMarkdown = removeSlideElementFromMarkdown;
3420
3635
  exports.resolveHtmlTheme = resolveHtmlTheme;
3421
3636
  exports.serializeLayoutToComment = serializeLayoutToComment;
3422
3637
  exports.splitMarkdownSlides = splitMarkdownSlides;
3423
3638
  exports.updateSlideLayoutInMarkdown = updateSlideLayoutInMarkdown;
3424
3639
  exports.updateSlideTextInMarkdown = updateSlideTextInMarkdown;
3425
- //# sourceMappingURL=chunk-SUGKWGJJ.cjs.map
3426
- //# sourceMappingURL=chunk-SUGKWGJJ.cjs.map
3640
+ //# sourceMappingURL=chunk-JNTRKKJX.cjs.map
3641
+ //# sourceMappingURL=chunk-JNTRKKJX.cjs.map