@thanh01.pmt/presentation-kit 0.2.10 → 0.2.12

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.
@@ -1,7 +1,7 @@
1
1
  import React$1 from 'react';
2
- import { B as BuiltInTheme, c as SlideTheme, e as SlideRenderResult, b as SlideLayoutData, L as LayoutBoxConfig } from '../types-C1121XLt.cjs';
3
- export { M as MermaidBlock, f as SlideRenderAsyncResult, d as SlideRenderOptions, T as ThemeTokens } from '../types-C1121XLt.cjs';
4
- export { l as ClickCssOptions, k as ClickPostprocessResult, C as ClickPreprocessResult, d as defineTheme, i as extractMaxClicks, j as generateClickCss, h as postprocessClicks, p as preprocessClicks, r as renderSlide, a as renderSlideAsync, e as resolveTheme } from '../click-css-BP9oqy_1.cjs';
2
+ import { B as BuiltInTheme, h as SlideTheme, j as SlideRenderResult, f as HtmlDeckData, b as SlideLayoutData, L as LayoutBoxConfig } from '../types-8AVYoEUn.cjs';
3
+ export { M as MermaidBlock, k as SlideRenderAsyncResult, i as SlideRenderOptions, T as ThemeTokens } from '../types-8AVYoEUn.cjs';
4
+ export { l as ClickCssOptions, k as ClickPostprocessResult, C as ClickPreprocessResult, d as defineTheme, i as extractMaxClicks, j as generateClickCss, h as postprocessClicks, p as preprocessClicks, r as renderSlide, a as renderSlideAsync, e as resolveTheme } from '../click-css-Ch_jyWaw.cjs';
5
5
 
6
6
  /** View mode for slide display */
7
7
  type SlideViewMode = 'presentation' | 'deck';
@@ -80,6 +80,10 @@ interface DeckViewProps {
80
80
  slideCount: number;
81
81
  /** Container CSS class */
82
82
  className?: string;
83
+ /** Presenter notes per slide (optional) */
84
+ notes?: string[][];
85
+ /** Whether to render speaker notes below each slide in deck mode */
86
+ showNotes?: boolean;
83
87
  }
84
88
  /** Props for ThemeSwitcher */
85
89
  interface ThemeSwitcherProps {
@@ -171,6 +175,22 @@ interface SlideActions {
171
175
  */
172
176
  declare function SlideViewer({ markdown, initialTheme, initialMode, className, showThemeSwitcher, showNavigation, enableKeyboard, enableClicks, fullscreen, showNotes, showDesignMode, themeOptions, onThemeChange, onSlideChange, onModeChange, onRenderComplete, onSaveMarkdown, }: SlideViewerProps): React$1.JSX.Element;
173
177
 
178
+ interface HtmlSlideViewerProps {
179
+ /** Pre-compiled HTML string. If provided, used directly */
180
+ htmlContent?: string;
181
+ /** Structured deck data. If provided without htmlContent, compiled automatically */
182
+ deckData?: HtmlDeckData;
183
+ /** Deck title displayed on header / download file name */
184
+ title?: string;
185
+ /** Extra CSS classes for wrapper */
186
+ className?: string;
187
+ /** Callback fired when active slide index changes */
188
+ onSlideChange?: (current: number, total: number) => void;
189
+ /** Show top control bar */
190
+ showTopBar?: boolean;
191
+ }
192
+ declare const HtmlSlideViewer: React$1.FC<HtmlSlideViewerProps>;
193
+
174
194
  /**
175
195
  * PresentationView — Renders a single slide at a time.
176
196
  *
@@ -205,7 +225,7 @@ declare function PresentationView({ html, css, themeCss, layoutCss, currentIndex
205
225
  * />
206
226
  * ```
207
227
  */
208
- declare function DeckView({ html, css, themeCss, layoutCss, slideCount, className, }: DeckViewProps): React$1.JSX.Element;
228
+ declare function DeckView({ html, css, themeCss, layoutCss, slideCount, className, notes, showNotes, }: DeckViewProps): React$1.JSX.Element;
209
229
 
210
230
  /**
211
231
  * ThemeSwitcher — Renders a row of theme toggle buttons.
@@ -321,4 +341,4 @@ interface UseSlideStateReturn {
321
341
  */
322
342
  declare function useSlideState(options: UseSlideStateOptions): UseSlideStateReturn;
323
343
 
324
- export { BuiltInTheme, DeckView, type DeckViewProps, NavigationBar, type NavigationBarProps, PresentationView, type PresentationViewProps, PresenterNotesDrawer, type PresenterNotesDrawerProps, type SlideActions, SlideDesignOverlay, type SlideDesignOverlayProps, SlideRenderResult, type SlideState, SlideTheme, type SlideViewMode, SlideViewer, type SlideViewerProps, type ThemeOption, ThemeSwitcher, type ThemeSwitcherProps, type UseSlideStateOptions, type UseSlideStateReturn, useSlideState };
344
+ export { BuiltInTheme, DeckView, type DeckViewProps, HtmlSlideViewer, type HtmlSlideViewerProps, NavigationBar, type NavigationBarProps, PresentationView, type PresentationViewProps, PresenterNotesDrawer, type PresenterNotesDrawerProps, type SlideActions, SlideDesignOverlay, type SlideDesignOverlayProps, SlideRenderResult, type SlideState, SlideTheme, type SlideViewMode, SlideViewer, type SlideViewerProps, type ThemeOption, ThemeSwitcher, type ThemeSwitcherProps, type UseSlideStateOptions, type UseSlideStateReturn, useSlideState };
@@ -1,7 +1,7 @@
1
1
  import React$1 from 'react';
2
- import { B as BuiltInTheme, c as SlideTheme, e as SlideRenderResult, b as SlideLayoutData, L as LayoutBoxConfig } from '../types-C1121XLt.js';
3
- export { M as MermaidBlock, f as SlideRenderAsyncResult, d as SlideRenderOptions, T as ThemeTokens } from '../types-C1121XLt.js';
4
- export { l as ClickCssOptions, k as ClickPostprocessResult, C as ClickPreprocessResult, d as defineTheme, i as extractMaxClicks, j as generateClickCss, h as postprocessClicks, p as preprocessClicks, r as renderSlide, a as renderSlideAsync, e as resolveTheme } from '../click-css-BbhqdtGr.js';
2
+ import { B as BuiltInTheme, h as SlideTheme, j as SlideRenderResult, f as HtmlDeckData, b as SlideLayoutData, L as LayoutBoxConfig } from '../types-8AVYoEUn.js';
3
+ export { M as MermaidBlock, k as SlideRenderAsyncResult, i as SlideRenderOptions, T as ThemeTokens } from '../types-8AVYoEUn.js';
4
+ export { l as ClickCssOptions, k as ClickPostprocessResult, C as ClickPreprocessResult, d as defineTheme, i as extractMaxClicks, j as generateClickCss, h as postprocessClicks, p as preprocessClicks, r as renderSlide, a as renderSlideAsync, e as resolveTheme } from '../click-css-CktIYP3H.js';
5
5
 
6
6
  /** View mode for slide display */
7
7
  type SlideViewMode = 'presentation' | 'deck';
@@ -80,6 +80,10 @@ interface DeckViewProps {
80
80
  slideCount: number;
81
81
  /** Container CSS class */
82
82
  className?: string;
83
+ /** Presenter notes per slide (optional) */
84
+ notes?: string[][];
85
+ /** Whether to render speaker notes below each slide in deck mode */
86
+ showNotes?: boolean;
83
87
  }
84
88
  /** Props for ThemeSwitcher */
85
89
  interface ThemeSwitcherProps {
@@ -171,6 +175,22 @@ interface SlideActions {
171
175
  */
172
176
  declare function SlideViewer({ markdown, initialTheme, initialMode, className, showThemeSwitcher, showNavigation, enableKeyboard, enableClicks, fullscreen, showNotes, showDesignMode, themeOptions, onThemeChange, onSlideChange, onModeChange, onRenderComplete, onSaveMarkdown, }: SlideViewerProps): React$1.JSX.Element;
173
177
 
178
+ interface HtmlSlideViewerProps {
179
+ /** Pre-compiled HTML string. If provided, used directly */
180
+ htmlContent?: string;
181
+ /** Structured deck data. If provided without htmlContent, compiled automatically */
182
+ deckData?: HtmlDeckData;
183
+ /** Deck title displayed on header / download file name */
184
+ title?: string;
185
+ /** Extra CSS classes for wrapper */
186
+ className?: string;
187
+ /** Callback fired when active slide index changes */
188
+ onSlideChange?: (current: number, total: number) => void;
189
+ /** Show top control bar */
190
+ showTopBar?: boolean;
191
+ }
192
+ declare const HtmlSlideViewer: React$1.FC<HtmlSlideViewerProps>;
193
+
174
194
  /**
175
195
  * PresentationView — Renders a single slide at a time.
176
196
  *
@@ -205,7 +225,7 @@ declare function PresentationView({ html, css, themeCss, layoutCss, currentIndex
205
225
  * />
206
226
  * ```
207
227
  */
208
- declare function DeckView({ html, css, themeCss, layoutCss, slideCount, className, }: DeckViewProps): React$1.JSX.Element;
228
+ declare function DeckView({ html, css, themeCss, layoutCss, slideCount, className, notes, showNotes, }: DeckViewProps): React$1.JSX.Element;
209
229
 
210
230
  /**
211
231
  * ThemeSwitcher — Renders a row of theme toggle buttons.
@@ -321,4 +341,4 @@ interface UseSlideStateReturn {
321
341
  */
322
342
  declare function useSlideState(options: UseSlideStateOptions): UseSlideStateReturn;
323
343
 
324
- export { BuiltInTheme, DeckView, type DeckViewProps, NavigationBar, type NavigationBarProps, PresentationView, type PresentationViewProps, PresenterNotesDrawer, type PresenterNotesDrawerProps, type SlideActions, SlideDesignOverlay, type SlideDesignOverlayProps, SlideRenderResult, type SlideState, SlideTheme, type SlideViewMode, SlideViewer, type SlideViewerProps, type ThemeOption, ThemeSwitcher, type ThemeSwitcherProps, type UseSlideStateOptions, type UseSlideStateReturn, useSlideState };
344
+ export { BuiltInTheme, DeckView, type DeckViewProps, HtmlSlideViewer, type HtmlSlideViewerProps, NavigationBar, type NavigationBarProps, PresentationView, type PresentationViewProps, PresenterNotesDrawer, type PresenterNotesDrawerProps, type SlideActions, SlideDesignOverlay, type SlideDesignOverlayProps, SlideRenderResult, type SlideState, SlideTheme, type SlideViewMode, SlideViewer, type SlideViewerProps, type ThemeOption, ThemeSwitcher, type ThemeSwitcherProps, type UseSlideStateOptions, type UseSlideStateReturn, useSlideState };
@@ -1,7 +1,7 @@
1
1
  import { renderSlide, renderMermaidDiagrams } from '../chunk-NYLDRVHZ.js';
2
2
  export { defineTheme, extractMaxClicks, generateClickCss, postprocessClicks, preprocessClicks, renderSlide, renderSlideAsync, resolveTheme } from '../chunk-NYLDRVHZ.js';
3
- import { SLIDE_LAYOUT_PRESETS, updateSlideLayoutInMarkdown } from '../chunk-V6X7D44H.js';
4
- import { useState, useRef, useMemo, useEffect, useCallback } from 'react';
3
+ import { SLIDE_LAYOUT_PRESETS, updateSlideLayoutInMarkdown, compileHtmlDeck } from '../chunk-3VGV5KYZ.js';
4
+ import React2, { useState, useRef, useMemo, useEffect, useCallback } from 'react';
5
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
6
 
7
7
  function useSlideState(options) {
@@ -410,8 +410,45 @@ function DeckView({
410
410
  themeCss,
411
411
  layoutCss,
412
412
  slideCount,
413
- className
413
+ className,
414
+ notes,
415
+ showNotes = false
414
416
  }) {
417
+ const hostRef = React2.useRef(null);
418
+ React2.useEffect(() => {
419
+ if (!hostRef.current) return;
420
+ hostRef.current.querySelectorAll(".deck-slide-note-item").forEach((el) => el.remove());
421
+ if (!showNotes || !notes || notes.length === 0) return;
422
+ const svgs = hostRef.current.querySelectorAll("svg[data-marpit-svg], svg");
423
+ svgs.forEach((svg, idx) => {
424
+ const slideNotes = notes[idx];
425
+ if (slideNotes && slideNotes.length > 0) {
426
+ const noteBox = document.createElement("div");
427
+ noteBox.className = "deck-slide-note-item";
428
+ noteBox.style.cssText = `
429
+ width: 100%;
430
+ max-width: 960px;
431
+ margin: -16px auto 32px auto;
432
+ background: rgba(24, 24, 32, 0.85);
433
+ border: 1px solid rgba(255, 255, 255, 0.12);
434
+ border-left: 4px solid #0284c7;
435
+ border-radius: 8px;
436
+ padding: 14px 18px;
437
+ color: #e4e4e7;
438
+ font-size: 14px;
439
+ line-height: 1.6;
440
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
441
+ `;
442
+ noteBox.innerHTML = `
443
+ <div style="font-weight: 700; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; color: #38bdf8; margin-bottom: 4px; display: flex; align-items: center; gap: 6px;">
444
+ <span>\u{1F4DD}</span><span>Presenter Notes (Slide ${idx + 1})</span>
445
+ </div>
446
+ <div style="white-space: pre-wrap; font-size: 13px; line-height: 1.6; color: inherit;">${slideNotes.join("\n")}</div>
447
+ `;
448
+ svg.after(noteBox);
449
+ }
450
+ });
451
+ }, [html, notes, showNotes]);
415
452
  const deckCss = `
416
453
  .slide-deck-host {
417
454
  display: flex !important;
@@ -451,6 +488,46 @@ function DeckView({
451
488
  transform: translateY(-2px);
452
489
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2) !important;
453
490
  }
491
+
492
+ @media print {
493
+ @page {
494
+ size: landscape;
495
+ margin: 0;
496
+ }
497
+ body {
498
+ background: #ffffff !important;
499
+ color: #000000 !important;
500
+ }
501
+ .slide-deck-host {
502
+ padding: 0 !important;
503
+ }
504
+ .slide-deck-host #marp-container svg[data-marpit-svg],
505
+ .slide-deck-host #marp-container > svg,
506
+ .slide-deck-host > div > svg {
507
+ width: 100vw !important;
508
+ max-width: none !important;
509
+ height: 56.25vw !important;
510
+ page-break-inside: avoid !important;
511
+ break-inside: avoid !important;
512
+ page-break-after: always !important;
513
+ break-after: page !important;
514
+ margin: 0 auto !important;
515
+ box-shadow: none !important;
516
+ border-radius: 0 !important;
517
+ }
518
+ .deck-slide-note-item {
519
+ page-break-after: always !important;
520
+ break-after: page !important;
521
+ background: #f8fafc !important;
522
+ border: 1px solid #cbd5e1 !important;
523
+ border-left: 4px solid #2563eb !important;
524
+ color: #1e293b !important;
525
+ padding: 12px 18px !important;
526
+ margin: 12px auto 32px auto !important;
527
+ width: 90vw !important;
528
+ font-size: 11pt !important;
529
+ }
530
+ }
454
531
  `;
455
532
  return /* @__PURE__ */ jsxs("div", { className, style: { width: "100%" }, children: [
456
533
  /* @__PURE__ */ jsx("style", { children: css }),
@@ -460,6 +537,7 @@ function DeckView({
460
537
  /* @__PURE__ */ jsx(
461
538
  "div",
462
539
  {
540
+ ref: hostRef,
463
541
  className: "slide-deck-host",
464
542
  dangerouslySetInnerHTML: { __html: html }
465
543
  }
@@ -1219,6 +1297,13 @@ function SlideViewer({
1219
1297
  setIsFullscreen((prev) => !prev);
1220
1298
  }
1221
1299
  break;
1300
+ case "p":
1301
+ case "P":
1302
+ if (showNotes) {
1303
+ e.preventDefault();
1304
+ setShowNotesDrawer((prev) => !prev);
1305
+ }
1306
+ break;
1222
1307
  }
1223
1308
  };
1224
1309
  window.addEventListener("keydown", handleKey);
@@ -1440,45 +1525,62 @@ function SlideViewer({
1440
1525
  transition: "width 0.2s ease"
1441
1526
  } }) })
1442
1527
  ] }),
1443
- /* @__PURE__ */ jsx("div", { style: { position: "relative", width: "100%" }, children: mode === "presentation" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1444
- /* @__PURE__ */ jsx(
1445
- PresentationView,
1446
- {
1447
- html: result.html,
1448
- css: result.css,
1449
- themeCss: result.themeCss,
1450
- layoutCss: result.layoutCss,
1451
- currentIndex,
1452
- slideCount
1453
- }
1454
- ),
1455
- isDesignMode && /* @__PURE__ */ jsx(
1456
- SlideDesignOverlay,
1457
- {
1458
- slideIndex: currentIndex,
1459
- layout: liveLayout ?? result.layouts?.[currentIndex] ?? null,
1460
- onLayoutChange: setLiveLayout,
1461
- onSave: (layout) => {
1462
- setLiveLayout(layout);
1463
- if (onSaveMarkdown) {
1464
- const updated = updateSlideLayoutInMarkdown(markdown, currentIndex, layout);
1465
- onSaveMarkdown(updated);
1466
- }
1467
- },
1468
- onClose: () => setIsDesignMode(false),
1469
- isActive: isDesignMode
1470
- }
1471
- )
1472
- ] }) : /* @__PURE__ */ jsx(
1473
- DeckView,
1528
+ /* @__PURE__ */ jsx(
1529
+ "div",
1474
1530
  {
1475
- html: result.html,
1476
- css: result.css,
1477
- themeCss: result.themeCss,
1478
- layoutCss: result.layoutCss,
1479
- slideCount
1531
+ style: { position: "relative", width: "100%" },
1532
+ onClick: (e) => {
1533
+ if (mode !== "presentation" || isDesignMode) return;
1534
+ if (e.target.closest("button, a, input, select, textarea, [contenteditable], .slide-design-overlay")) return;
1535
+ if (enableClicks && currentClick < maxClicks) {
1536
+ nextClick();
1537
+ } else {
1538
+ nextSlide();
1539
+ }
1540
+ },
1541
+ children: mode === "presentation" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1542
+ /* @__PURE__ */ jsx(
1543
+ PresentationView,
1544
+ {
1545
+ html: result.html,
1546
+ css: result.css,
1547
+ themeCss: result.themeCss,
1548
+ layoutCss: result.layoutCss,
1549
+ currentIndex,
1550
+ slideCount
1551
+ }
1552
+ ),
1553
+ isDesignMode && /* @__PURE__ */ jsx(
1554
+ SlideDesignOverlay,
1555
+ {
1556
+ slideIndex: currentIndex,
1557
+ layout: liveLayout ?? result.layouts?.[currentIndex] ?? null,
1558
+ onLayoutChange: setLiveLayout,
1559
+ onSave: (layout) => {
1560
+ setLiveLayout(layout);
1561
+ if (onSaveMarkdown) {
1562
+ const updated = updateSlideLayoutInMarkdown(markdown, currentIndex, layout);
1563
+ onSaveMarkdown(updated);
1564
+ }
1565
+ },
1566
+ onClose: () => setIsDesignMode(false),
1567
+ isActive: isDesignMode
1568
+ }
1569
+ )
1570
+ ] }) : /* @__PURE__ */ jsx(
1571
+ DeckView,
1572
+ {
1573
+ html: result.html,
1574
+ css: result.css,
1575
+ themeCss: result.themeCss,
1576
+ layoutCss: result.layoutCss,
1577
+ slideCount,
1578
+ notes: result.notes,
1579
+ showNotes: showNotesDrawer
1580
+ }
1581
+ )
1480
1582
  }
1481
- ) }),
1583
+ ),
1482
1584
  showNotes && /* @__PURE__ */ jsx(
1483
1585
  PresenterNotesDrawer,
1484
1586
  {
@@ -1494,7 +1596,161 @@ function SlideViewer({
1494
1596
  }
1495
1597
  );
1496
1598
  }
1599
+ var HtmlSlideViewer = ({
1600
+ htmlContent: initialHtml,
1601
+ deckData,
1602
+ title: propTitle,
1603
+ className = "",
1604
+ onSlideChange,
1605
+ showTopBar = true
1606
+ }) => {
1607
+ const iframeRef = useRef(null);
1608
+ const containerRef = useRef(null);
1609
+ const [currentSlide, setCurrentSlide] = useState(0);
1610
+ const [totalSlides, setTotalSlides] = useState(deckData?.slides?.length || 1);
1611
+ const [isFullscreen, setIsFullscreen] = useState(false);
1612
+ const fullHtml = useMemo(() => {
1613
+ if (initialHtml) return initialHtml;
1614
+ if (deckData) {
1615
+ return compileHtmlDeck(deckData).html;
1616
+ }
1617
+ return "";
1618
+ }, [initialHtml, deckData]);
1619
+ const deckTitle = propTitle || deckData?.title || "Presentation";
1620
+ useEffect(() => {
1621
+ const handleMessage = (e) => {
1622
+ if (!e.data || typeof e.data !== "object") return;
1623
+ if (e.data.type === "PRESENTATION_SLIDE_CHANGE") {
1624
+ const cur = Number(e.data.currentIndex) || 0;
1625
+ const tot = Number(e.data.totalSlides) || 1;
1626
+ setCurrentSlide(cur);
1627
+ setTotalSlides(tot);
1628
+ if (onSlideChange) {
1629
+ onSlideChange(cur, tot);
1630
+ }
1631
+ }
1632
+ };
1633
+ window.addEventListener("message", handleMessage);
1634
+ return () => window.removeEventListener("message", handleMessage);
1635
+ }, [onSlideChange]);
1636
+ useEffect(() => {
1637
+ const onFsChange = () => {
1638
+ setIsFullscreen(Boolean(document.fullscreenElement));
1639
+ };
1640
+ document.addEventListener("fullscreenchange", onFsChange);
1641
+ return () => document.removeEventListener("fullscreenchange", onFsChange);
1642
+ }, []);
1643
+ const sendIframeMessage = useCallback((msg) => {
1644
+ if (iframeRef.current?.contentWindow) {
1645
+ iframeRef.current.contentWindow.postMessage(msg, "*");
1646
+ }
1647
+ }, []);
1648
+ const handleNext = () => sendIframeMessage({ type: "NEXT_SLIDE" });
1649
+ const handlePrev = () => sendIframeMessage({ type: "PREV_SLIDE" });
1650
+ const handleToggleFullscreen = () => {
1651
+ if (!containerRef.current) return;
1652
+ if (!document.fullscreenElement) {
1653
+ containerRef.current.requestFullscreen().catch(() => {
1654
+ });
1655
+ } else {
1656
+ document.exitFullscreen().catch(() => {
1657
+ });
1658
+ }
1659
+ };
1660
+ const handleDownloadHtml = () => {
1661
+ if (!fullHtml) return;
1662
+ const blob = new Blob([fullHtml], { type: "text/html;charset=utf-8" });
1663
+ const url = URL.createObjectURL(blob);
1664
+ const a = document.createElement("a");
1665
+ a.href = url;
1666
+ a.download = `${deckTitle.toLowerCase().replace(/[^a-z0-9]/g, "-") || "slide-deck"}.html`;
1667
+ document.body.appendChild(a);
1668
+ a.click();
1669
+ document.body.removeChild(a);
1670
+ URL.revokeObjectURL(url);
1671
+ };
1672
+ return /* @__PURE__ */ jsxs(
1673
+ "div",
1674
+ {
1675
+ ref: containerRef,
1676
+ className: `relative flex flex-col w-full h-full bg-neutral-950 text-neutral-100 rounded-xl overflow-hidden shadow-2xl ${className}`,
1677
+ style: { minHeight: "480px" },
1678
+ children: [
1679
+ showTopBar && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-2 bg-neutral-900/90 backdrop-blur border-b border-neutral-800 text-xs select-none z-20", children: [
1680
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 font-medium truncate max-w-md", children: [
1681
+ /* @__PURE__ */ jsx("span", { className: "w-2 h-2 rounded-full bg-blue-500 inline-block" }),
1682
+ /* @__PURE__ */ jsx("span", { className: "truncate text-neutral-200", children: deckTitle })
1683
+ ] }),
1684
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1685
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 bg-neutral-800/80 px-2 py-1 rounded border border-neutral-700 font-mono text-[11px]", children: [
1686
+ /* @__PURE__ */ jsx(
1687
+ "button",
1688
+ {
1689
+ onClick: handlePrev,
1690
+ disabled: currentSlide <= 0,
1691
+ className: "hover:text-white disabled:opacity-40 p-0.5",
1692
+ title: "Previous Slide",
1693
+ children: "\u25C0"
1694
+ }
1695
+ ),
1696
+ /* @__PURE__ */ jsxs("span", { className: "px-1 font-semibold text-neutral-200", children: [
1697
+ String(currentSlide + 1).padStart(2, "0"),
1698
+ " / ",
1699
+ String(totalSlides).padStart(2, "0")
1700
+ ] }),
1701
+ /* @__PURE__ */ jsx(
1702
+ "button",
1703
+ {
1704
+ onClick: handleNext,
1705
+ disabled: currentSlide >= totalSlides - 1,
1706
+ className: "hover:text-white disabled:opacity-40 p-0.5",
1707
+ title: "Next Slide",
1708
+ children: "\u25B6"
1709
+ }
1710
+ )
1711
+ ] }),
1712
+ /* @__PURE__ */ jsxs(
1713
+ "button",
1714
+ {
1715
+ onClick: handleDownloadHtml,
1716
+ className: "flex items-center gap-1 px-2.5 py-1 rounded bg-neutral-800 hover:bg-neutral-700 text-neutral-300 hover:text-white border border-neutral-700 transition-colors",
1717
+ title: "Download standalone offline HTML file",
1718
+ children: [
1719
+ /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1720
+ /* @__PURE__ */ jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
1721
+ /* @__PURE__ */ jsx("polyline", { points: "7 10 12 15 17 10" }),
1722
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "15", x2: "12", y2: "3" })
1723
+ ] }),
1724
+ /* @__PURE__ */ jsx("span", { children: "Export HTML" })
1725
+ ]
1726
+ }
1727
+ ),
1728
+ /* @__PURE__ */ jsx(
1729
+ "button",
1730
+ {
1731
+ onClick: handleToggleFullscreen,
1732
+ className: "p-1 rounded bg-neutral-800 hover:bg-neutral-700 text-neutral-300 hover:text-white border border-neutral-700 transition-colors",
1733
+ title: "Toggle Fullscreen (F)",
1734
+ children: /* @__PURE__ */ jsx("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: isFullscreen ? /* @__PURE__ */ jsx("path", { d: "M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3" }) : /* @__PURE__ */ jsx("path", { d: "M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3" }) })
1735
+ }
1736
+ )
1737
+ ] })
1738
+ ] }),
1739
+ /* @__PURE__ */ jsx("div", { className: "relative flex-1 w-full h-full min-h-0 bg-black", children: fullHtml ? /* @__PURE__ */ jsx(
1740
+ "iframe",
1741
+ {
1742
+ ref: iframeRef,
1743
+ srcDoc: fullHtml,
1744
+ title: deckTitle,
1745
+ sandbox: "allow-scripts allow-same-origin allow-popups allow-forms",
1746
+ className: "w-full h-full border-0 block"
1747
+ }
1748
+ ) : /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center text-neutral-500 text-sm", children: "No slide deck content loaded." }) })
1749
+ ]
1750
+ }
1751
+ );
1752
+ };
1497
1753
 
1498
- export { DeckView, NavigationBar, PresentationView, PresenterNotesDrawer, SlideDesignOverlay, SlideViewer, ThemeSwitcher, useSlideState };
1754
+ export { DeckView, HtmlSlideViewer, NavigationBar, PresentationView, PresenterNotesDrawer, SlideDesignOverlay, SlideViewer, ThemeSwitcher, useSlideState };
1499
1755
  //# sourceMappingURL=index.js.map
1500
1756
  //# sourceMappingURL=index.js.map