@thanh01.pmt/presentation-kit 0.2.13 → 0.2.15

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.
@@ -1282,9 +1282,9 @@ var HTML_SLIDE_PRESETS = {
1282
1282
 
1283
1283
  // src/html-engine/shell.ts
1284
1284
  function generateHtmlShell(options) {
1285
- const { title, theme, slidesHtml, slideCount, customHeadTags = "" } = options;
1285
+ const { title, theme, slidesHtml, slideCount, customHeadTags = "", deckId = "" } = options;
1286
1286
  return `<!DOCTYPE html>
1287
- <html lang="en" class="${theme.scheme}">
1287
+ <html lang="en" class="${theme.scheme}"${deckId ? ` data-deck-id="${deckId}"` : ""}>
1288
1288
  <head>
1289
1289
  <meta charset="UTF-8">
1290
1290
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
@@ -1298,6 +1298,7 @@ function generateHtmlShell(options) {
1298
1298
  <!-- Core Presentation Styling & 1920x1080 Scaled Tailwind Configuration -->
1299
1299
  <script src="https://cdn.tailwindcss.com"></script>
1300
1300
  <script>
1301
+ window.tailwind = window.tailwind || {};
1301
1302
  tailwind.config = {
1302
1303
  darkMode: 'class',
1303
1304
  theme: {
@@ -1421,6 +1422,7 @@ function generateHtmlShell(options) {
1421
1422
  left: 50%;
1422
1423
  width: 1920px;
1423
1424
  height: 1080px;
1425
+ transform: translate(-50%, -50%);
1424
1426
  transform-origin: center center;
1425
1427
  background-color: var(--bg);
1426
1428
  overflow: hidden;
@@ -2239,7 +2241,7 @@ function generateHtmlShell(options) {
2239
2241
  <!-- WYSIWYG Inline Edit UI (Level 1) -->
2240
2242
  <div class="edit-hotzone" id="edit-hotzone"></div>
2241
2243
  <button class="edit-toggle" id="btn-edit-toggle" title="Toggle Inline Edit Mode (E)">
2242
- <span>\u270F\uFE0F</span>
2244
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z"/></svg>
2243
2245
  <span id="edit-toggle-label">Edit Mode (E)</span>
2244
2246
  </button>
2245
2247
 
@@ -2254,7 +2256,9 @@ function generateHtmlShell(options) {
2254
2256
  <button type="button" class="mode-tab active" id="tab-mode-unified">Unified</button>
2255
2257
  <button type="button" class="mode-tab" id="tab-mode-independent">Independent</button>
2256
2258
  </div>
2257
- <button type="button" class="scale-panel-close" id="btn-scale-panel-close" title="Close Panel">\u2715</button>
2259
+ <button type="button" class="scale-panel-close" id="btn-scale-panel-close" title="Close Panel" style="display: flex; align-items: center; justify-content: center;">
2260
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
2261
+ </button>
2258
2262
  </div>
2259
2263
 
2260
2264
  <!-- Unified Mode Content -->
@@ -2304,7 +2308,10 @@ function generateHtmlShell(options) {
2304
2308
  <input type="range" id="input-scale-spacing" min="70" max="150" value="100" class="scale-range-slider" />
2305
2309
  </div>
2306
2310
  <div class="scale-independent-footer">
2307
- <button type="button" class="btn-reset-scales" id="btn-reset-scales">\u21BA Reset All</button>
2311
+ <button type="button" class="btn-reset-scales" id="btn-reset-scales" style="display: flex; align-items: center; gap: 4px;">
2312
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
2313
+ <span>Reset All</span>
2314
+ </button>
2308
2315
  </div>
2309
2316
  </div>
2310
2317
  </div>
@@ -2317,13 +2324,22 @@ function generateHtmlShell(options) {
2317
2324
  <button class="edit-bar-btn" id="btn-format-underline" title="Underline (Cmd+U)"><u>U</u></button>
2318
2325
  <div class="edit-bar-divider"></div>
2319
2326
  <button class="edit-bar-btn" id="btn-editbar-scale" title="Tune Element Scales (Unified / Independent)">
2320
- <span>\u{1F4D0}</span>
2327
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 3 3 21"/><path d="m14 4 6 6"/><path d="m4 14 6 6"/></svg>
2321
2328
  <span>Scale</span>
2322
2329
  </button>
2323
2330
  <div class="edit-bar-divider"></div>
2324
- <button class="edit-bar-btn" id="btn-reset-edits" title="Restore original content">\u21BA Reset</button>
2325
- <button class="edit-bar-btn save-btn" id="btn-save-html" title="Save and download clean HTML file">\u{1F4BE} Save HTML</button>
2326
- <button class="edit-bar-btn" id="btn-done-edit" title="Done editing (E)">\u2715 Done</button>
2331
+ <button class="edit-bar-btn" id="btn-reset-edits" title="Restore original content">
2332
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
2333
+ <span>Reset</span>
2334
+ </button>
2335
+ <button class="edit-bar-btn save-btn" id="btn-save-html" title="Save and download clean HTML file">
2336
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
2337
+ <span>Save HTML</span>
2338
+ </button>
2339
+ <button class="edit-bar-btn" id="btn-done-edit" title="Done editing (E)">
2340
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>
2341
+ <span>Done</span>
2342
+ </button>
2327
2343
  </div>
2328
2344
 
2329
2345
  <!-- Presenter Drawer (Quick In-Page Notes & Timer) -->
@@ -2332,15 +2348,18 @@ function generateHtmlShell(options) {
2332
2348
  <div class="presenter-meta-group">
2333
2349
  <span style="font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: #a1a1aa;">Presenter Console</span>
2334
2350
  <div class="presenter-timer-badge">
2335
- <span>\u23F1</span>
2351
+ <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
2336
2352
  <span id="presenter-timer-text">00:00</span>
2337
2353
  </div>
2338
2354
  </div>
2339
2355
  <div style="display: flex; align-items: center; gap: 8px;">
2340
- <button type="button" class="btn-reset-scales" id="btn-open-dual-presenter" title="Open separate synced presenter console window for multi-monitor display">
2341
- <span>Launch 2nd Window \u2197</span>
2356
+ <button type="button" class="btn-reset-scales" id="btn-open-dual-presenter" title="Open separate synced presenter console window for multi-monitor display" style="display: flex; align-items: center; gap: 4px;">
2357
+ <span>Launch 2nd Window</span>
2358
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M7 17 17 7"/><path d="M7 7h10v10"/></svg>
2359
+ </button>
2360
+ <button type="button" class="scale-panel-close" id="btn-close-presenter-drawer" title="Close Drawer (P)" style="display: flex; align-items: center; justify-content: center;">
2361
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
2342
2362
  </button>
2343
- <button type="button" class="scale-panel-close" id="btn-close-presenter-drawer" title="Close Drawer (P)">\u2715</button>
2344
2363
  </div>
2345
2364
  </div>
2346
2365
  <div class="presenter-notes-box" id="presenter-notes-text">
@@ -2360,11 +2379,14 @@ function generateHtmlShell(options) {
2360
2379
  <span style="user-select: none;">In k\xE8m ghi ch\xFA gi\u1EA3ng vi\xEAn (Handout / Teacher Notes)</span>
2361
2380
  </label>
2362
2381
  <p style="font-size: 12px; color: #a1a1aa; line-height: 1.5; margin: 0;">
2363
- \u{1F4A1} Ch\u1ECDn trang in Kh\u1ED5 ngang (Landscape), B\u1EADt "Background graphics" trong h\u1ED9p tho\u1EA1i in c\u1EE7a tr\xECnh duy\u1EC7t \u0111\u1EC3 m\xE0u s\u1EAFc hi\u1EC3n th\u1ECB \u0111\xFAng 100%.
2382
+ L\u01B0u \xFD: Ch\u1ECDn trang in Kh\u1ED5 ngang (Landscape), B\u1EADt &quot;Background graphics&quot; trong h\u1ED9p tho\u1EA1i in c\u1EE7a tr\xECnh duy\u1EC7t \u0111\u1EC3 m\xE0u s\u1EAFc hi\u1EC3n th\u1ECB \u0111\xFAng 100%.
2364
2383
  </p>
2365
2384
  <div class="print-dialog-actions">
2366
2385
  <button type="button" class="btn-reset-scales" id="btn-cancel-print">H\u1EE7y</button>
2367
- <button type="button" class="edit-bar-btn save-btn" id="btn-start-print" style="padding: 6px 16px;">\u{1F5A8}\uFE0F In ngay (Print)</button>
2386
+ <button type="button" class="edit-bar-btn save-btn" id="btn-start-print" style="padding: 6px 16px; display: flex; align-items: center; gap: 6px;">
2387
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 6 2 18 2 18 9"></polyline><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"></path><rect x="6" y="14" width="12" height="8"></rect></svg>
2388
+ <span>In ngay (Print)</span>
2389
+ </button>
2368
2390
  </div>
2369
2391
  </div>
2370
2392
  </div>
@@ -2388,7 +2410,7 @@ function generateHtmlShell(options) {
2388
2410
  <button class="control-btn" id="btn-controls-print" title="Print &amp; Export PDF (Cmd+P)">
2389
2411
  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 6 2 18 2 18 9"></polyline><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"></path><rect x="6" y="14" width="12" height="8"></rect></svg>
2390
2412
  </button>
2391
- <button class="control-btn" id="btn-controls-scale" title="Tune Scale (\u{1F4D0})">
2413
+ <button class="control-btn" id="btn-controls-scale" title="Tune Scale">
2392
2414
  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 3 3 21"/><path d="m14 4 6 6"/><path d="m4 14 6 6"/></svg>
2393
2415
  </button>
2394
2416
  <button class="control-btn" id="btn-fullscreen" title="Toggle Fullscreen (F)">
@@ -2401,10 +2423,21 @@ function generateHtmlShell(options) {
2401
2423
  const TOTAL_SLIDES = ${slideCount};
2402
2424
  let currentSlide = 0;
2403
2425
  let isEditMode = false;
2404
- const STORAGE_KEY = 'deck_edit_' + encodeURIComponent(document.title.trim() || 'default');
2405
- const originalCanvasHtml = document.getElementById('canvas').innerHTML;
2406
-
2407
2426
  const canvas = document.getElementById('canvas');
2427
+ const originalCanvasHtml = canvas ? canvas.innerHTML : '';
2428
+
2429
+ function computeDeckContentHash(str) {
2430
+ let h = 0;
2431
+ for (let i = 0; i < str.length; i++) {
2432
+ h = (Math.imul(31, h) + str.charCodeAt(i)) | 0;
2433
+ }
2434
+ return Math.abs(h).toString(36);
2435
+ }
2436
+
2437
+ const deckIdAttr = document.documentElement.getAttribute('data-deck-id');
2438
+ const contentHash = computeDeckContentHash(originalCanvasHtml);
2439
+ const deckKeyPrefix = deckIdAttr ? encodeURIComponent(deckIdAttr) : (encodeURIComponent(document.title.trim() || 'deck') + '_' + contentHash);
2440
+ const STORAGE_KEY = 'deck_edit_' + deckKeyPrefix;
2408
2441
  const slides = document.querySelectorAll('.slide-page');
2409
2442
  const progressBar = document.getElementById('progress-bar');
2410
2443
  const counter = document.getElementById('slide-counter');
@@ -2464,7 +2497,7 @@ function generateHtmlShell(options) {
2464
2497
  let timerInterval = null;
2465
2498
  let isTimerRunning = false;
2466
2499
 
2467
- const SCALE_STORAGE_KEY = 'deck_scale_' + encodeURIComponent(document.title.trim() || 'default');
2500
+ const SCALE_STORAGE_KEY = 'deck_scale_' + deckKeyPrefix;
2468
2501
  let scaleState = {
2469
2502
  mode: 'unified', // 'unified' | 'independent'
2470
2503
  factor: 1.0,
@@ -2488,8 +2521,9 @@ function generateHtmlShell(options) {
2488
2521
  // Auto-scale 1920x1080 canvas to fit window
2489
2522
  function resizeCanvas() {
2490
2523
  if (isDeckMode) return;
2491
- const vw = window.innerWidth;
2492
- const vh = window.innerHeight;
2524
+ const vw = window.innerWidth || document.documentElement.clientWidth || 1920;
2525
+ const vh = window.innerHeight || document.documentElement.clientHeight || 1080;
2526
+ if (vw <= 0 || vh <= 0) return;
2493
2527
  const scaleX = vw / 1920;
2494
2528
  const scaleY = vh / 1080;
2495
2529
  const scale = Math.min(scaleX, scaleY);
@@ -2497,7 +2531,10 @@ function generateHtmlShell(options) {
2497
2531
  }
2498
2532
 
2499
2533
  window.addEventListener('resize', resizeCanvas);
2534
+ window.addEventListener('load', resizeCanvas);
2535
+ document.addEventListener('DOMContentLoaded', resizeCanvas);
2500
2536
  resizeCanvas();
2537
+ requestAnimationFrame(resizeCanvas);
2501
2538
 
2502
2539
  function getSlideNotes(slideIndex) {
2503
2540
  if (slideIndex < 0 || slideIndex >= slides.length) return '';
@@ -2670,8 +2707,7 @@ function generateHtmlShell(options) {
2670
2707
  if (!presenterNotesText) return;
2671
2708
  const notes = getSlideNotes(currentSlide);
2672
2709
  if (notes && notes.trim()) {
2673
- presenterNotesText.innerHTML = notes.replace(/
2674
- /g, '<br/>');
2710
+ presenterNotesText.innerHTML = notes.split('\\n').join('<br/>');
2675
2711
  } else {
2676
2712
  presenterNotesText.innerHTML = '<span style="opacity: 0.5; font-style: italic;">No speaker notes for Slide ' + (currentSlide + 1) + '.</span>';
2677
2713
  }
@@ -2688,7 +2724,7 @@ function generateHtmlShell(options) {
2688
2724
 
2689
2725
  function openPresenterConsole() {
2690
2726
  startTimer();
2691
- const popupName = 'PresenterConsole_' + encodeURIComponent(document.title.replace(/W/g, ''));
2727
+ const popupName = 'PresenterConsole_' + encodeURIComponent(document.title.replace(/[^a-zA-Z0-9_]/g, ''));
2692
2728
  const win = window.open('', popupName, 'width=1100,height=720,menubar=no,toolbar=no,location=no,status=no');
2693
2729
  if (!win) {
2694
2730
  togglePresenterDrawer();
@@ -2699,151 +2735,84 @@ function generateHtmlShell(options) {
2699
2735
  const currentNotes = getSlideNotes(currentSlide);
2700
2736
  const curSlideTitle = 'Slide ' + (currentSlide + 1) + ' / ' + TOTAL_SLIDES;
2701
2737
 
2702
- const consoleHtml = '<!DOCTYPE html>
2703
- <html>
2704
- <head>
2705
- ' +
2706
- '<meta charset="UTF-8"><title>Presenter Console \u2014 ' + (document.title || 'Slides') + '</title>
2707
- ' +
2708
- '<style>
2709
- ' +
2710
- '* { box-sizing: border-box; margin: 0; padding: 0; }
2711
- ' +
2712
- 'body { background: #0c0d14; color: #f4f4f5; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; display: flex; flex-direction: column; height: 100vh; overflow: hidden; user-select: none; }
2713
- ' +
2714
- '.top-bar { display: flex; align-items: center; justify-content: space-between; padding: 12px 24px; background: #141522; border-bottom: 1px solid rgba(255,255,255,0.12); }
2715
- ' +
2716
- '.timer-badge { font-family: monospace; font-size: 20px; font-weight: 700; color: #38bdf8; background: rgba(56,189,248,0.15); border: 1px solid rgba(56,189,248,0.3); padding: 4px 14px; border-radius: 8px; display: flex; align-items: center; gap: 8px; }
2717
- ' +
2718
- '.grid-container { flex: 1; display: grid; grid-template-columns: 1fr 1fr; gap: 20px; padding: 20px 24px; overflow: hidden; }
2719
- ' +
2720
- '.panel-card { background: #181926; border: 1px solid rgba(255,255,255,0.1); border-radius: 12px; display: flex; flex-direction: column; overflow: hidden; }
2721
- ' +
2722
- '.card-header { padding: 10px 16px; background: rgba(255,255,255,0.04); border-bottom: 1px solid rgba(255,255,255,0.08); font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: #a1a1aa; display: flex; justify-content: space-between; }
2723
- ' +
2724
- '.notes-content { flex: 1; padding: 20px; font-size: 19px; line-height: 1.7; overflow-y: auto; color: #f1f5f9; user-select: text; white-space: pre-wrap; }
2725
- ' +
2726
- '.preview-box { flex: 1; display: flex; align-items: center; justify-content: center; background: #000; overflow: hidden; padding: 16px; }
2727
- ' +
2728
- '.btn { background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.15); color: #fff; padding: 6px 14px; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 600; }
2729
- ' +
2730
- '.btn:hover { background: rgba(255,255,255,0.2); }
2731
- ' +
2732
- '.btn-nav { font-size: 16px; padding: 10px 24px; border-radius: 8px; }
2733
- ' +
2734
- '.bottom-nav { display: flex; align-items: center; justify-content: space-between; padding: 14px 24px; background: #141522; border-top: 1px solid rgba(255,255,255,0.12); }
2735
- ' +
2736
- '</style>
2737
- </head>
2738
- <body>
2739
- ' +
2740
- '<div class="top-bar">
2741
- ' +
2742
- ' <div style="font-weight: 700; font-size: 16px;">' + (document.title || 'Slide Deck') + '</div>
2743
- ' +
2744
- ' <div style="display: flex; align-items: center; gap: 12px;">
2745
- ' +
2746
- ' <div class="timer-badge">\u23F1 <span id="timer-val">00:00</span></div>
2747
- ' +
2748
- ' <button class="btn" id="btn-toggle-t">Pause</button>
2749
- ' +
2750
- ' <button class="btn" id="btn-reset-t">Reset</button>
2751
- ' +
2752
- ' </div>
2753
- ' +
2754
- '</div>
2755
- ' +
2756
- '<div class="grid-container">
2757
- ' +
2758
- ' <div class="panel-card">
2759
- ' +
2760
- ' <div class="card-header"><span>CURRENT SLIDE</span><span id="slide-num">' + curSlideTitle + '</span></div>
2761
- ' +
2762
- ' <div class="preview-box"><div style="font-size: 22px; color: #94a3b8; text-align: center;">Active Slide On Screen<br/><span style="font-size: 14px; color: #38bdf8;">(Broadcasting Synced)</span></div></div>
2763
- ' +
2764
- ' <div class="card-header" style="border-top: 1px solid rgba(255,255,255,0.08);"><span>NEXT SLIDE PREVIEW</span></div>
2765
- ' +
2766
- ' <div style="padding: 12px 16px; font-size: 14px; color: #94a3b8; background: rgba(0,0,0,0.3); min-height: 80px;" id="next-preview-text">Upcoming: Slide ' + (currentSlide + 2) + '</div>
2767
- ' +
2768
- ' </div>
2769
- ' +
2770
- ' <div class="panel-card">
2771
- ' +
2772
- ' <div class="card-header"><span>SPEAKER NOTES / TALK TRACK</span><div><button class="btn" id="font-dn" style="padding: 2px 8px;">A-</button> <button class="btn" id="font-up" style="padding: 2px 8px;">A+</button></div></div>
2773
- ' +
2774
- ' <div class="notes-content" id="speaker-notes">' + (currentNotes ? currentNotes : '<span style="opacity: 0.4;">No speaker notes for this slide.</span>') + '</div>
2775
- ' +
2776
- ' </div>
2777
- ' +
2778
- '</div>
2779
- ' +
2780
- '<div class="bottom-nav">
2781
- ' +
2782
- ' <button class="btn btn-nav" id="btn-prev-p">\u2039 Previous (\u2190)</button>
2783
- ' +
2784
- ' <span style="font-size: 14px; color: #a1a1aa;">Use Arrow Keys or Space to advance</span>
2785
- ' +
2786
- ' <button class="btn btn-nav" id="btn-next-p" style="background: #0284c7; border-color: #0284c7;">Next (\u2192 / Space) \u203A</button>
2787
- ' +
2788
- '</div>
2789
- ' +
2790
- '<script>
2791
- ' +
2792
- ' const bc = new BroadcastChannel("' + SYNC_CHANNEL_NAME + '");
2793
- ' +
2794
- ' let curSecs = ' + timerSeconds + ';
2795
- ' +
2796
- ' function sendCmd(action, idx) { bc.postMessage({ type: "COMMAND", action: action, index: idx }); }
2797
- ' +
2798
- ' document.getElementById("btn-prev-p").onclick = () => sendCmd("PREV");
2799
- ' +
2800
- ' document.getElementById("btn-next-p").onclick = () => sendCmd("NEXT");
2801
- ' +
2802
- ' document.getElementById("btn-reset-t").onclick = () => sendCmd("RESET_TIMER");
2803
- ' +
2804
- ' document.getElementById("btn-toggle-t").onclick = () => sendCmd("TOGGLE_TIMER");
2805
- ' +
2806
- ' let curFontSize = 19;
2807
- ' +
2808
- ' document.getElementById("font-up").onclick = () => { curFontSize += 2; document.getElementById("speaker-notes").style.fontSize = curFontSize + "px"; };
2809
- ' +
2810
- ' document.getElementById("font-dn").onclick = () => { curFontSize = Math.max(12, curFontSize - 2); document.getElementById("speaker-notes").style.fontSize = curFontSize + "px"; };
2811
- ' +
2812
- ' window.onkeydown = (e) => {
2813
- ' +
2814
- ' if (["ArrowRight", "Space", "PageDown"].includes(e.code)) { e.preventDefault(); sendCmd("NEXT"); }
2815
- ' +
2816
- ' else if (["ArrowLeft", "PageUp"].includes(e.code)) { e.preventDefault(); sendCmd("PREV"); }
2817
- ' +
2818
- ' };
2819
- ' +
2820
- ' bc.onmessage = (e) => {
2821
- ' +
2822
- ' if (!e.data || e.data.type !== "STATE_UPDATE") return;
2823
- ' +
2824
- ' document.getElementById("slide-num").textContent = "Slide " + (e.data.currentSlide + 1) + " / " + e.data.totalSlides;
2825
- ' +
2826
- ' document.getElementById("speaker-notes").textContent = e.data.notes || "No speaker notes for this slide.";
2827
- ' +
2828
- ' document.getElementById("next-preview-text").textContent = e.data.nextNotes ? "Next Notes: " + e.data.nextNotes : "Next: Slide " + (e.data.currentSlide + 2);
2829
- ' +
2830
- ' curSecs = e.data.timerSeconds || 0;
2831
- ' +
2832
- ' const m = String(Math.floor(curSecs / 60)).padStart(2, "0");
2833
- ' +
2834
- ' const s = String(curSecs % 60).padStart(2, "0");
2835
- ' +
2836
- ' document.getElementById("timer-val").textContent = m + ":" + s;
2837
- ' +
2838
- ' document.getElementById("btn-toggle-t").textContent = e.data.timerRunning ? "Pause" : "Start";
2839
- ' +
2840
- ' };
2841
- ' +
2842
- ' bc.postMessage({ type: "REQUEST_STATE" });
2843
- ' +
2844
- '<' + '/script>
2845
- </body>
2846
- </html>';
2738
+ const consoleHtml = [
2739
+ '<!DOCTYPE html>',
2740
+ '<html>',
2741
+ '<head>',
2742
+ '<meta charset="UTF-8"><title>Presenter Console \u2014 ' + (document.title || 'Slides') + '</title>',
2743
+ '<style>',
2744
+ '* { box-sizing: border-box; margin: 0; padding: 0; }',
2745
+ 'body { background: #0c0d14; color: #f4f4f5; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; display: flex; flex-direction: column; height: 100vh; overflow: hidden; user-select: none; }',
2746
+ '.top-bar { display: flex; align-items: center; justify-content: space-between; padding: 12px 24px; background: #141522; border-bottom: 1px solid rgba(255,255,255,0.12); }',
2747
+ '.timer-badge { font-family: monospace; font-size: 20px; font-weight: 700; color: #38bdf8; background: rgba(56,189,248,0.15); border: 1px solid rgba(56,189,248,0.3); padding: 4px 14px; border-radius: 8px; display: flex; align-items: center; gap: 8px; }',
2748
+ '.grid-container { flex: 1; display: grid; grid-template-columns: 1fr 1fr; gap: 20px; padding: 20px 24px; overflow: hidden; }',
2749
+ '.panel-card { background: #181926; border: 1px solid rgba(255,255,255,0.1); border-radius: 12px; display: flex; flex-direction: column; overflow: hidden; }',
2750
+ '.card-header { padding: 10px 16px; background: rgba(255,255,255,0.04); border-bottom: 1px solid rgba(255,255,255,0.08); font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: #a1a1aa; display: flex; justify-content: space-between; }',
2751
+ '.notes-content { flex: 1; padding: 20px; font-size: 19px; line-height: 1.7; overflow-y: auto; color: #f1f5f9; user-select: text; white-space: pre-wrap; }',
2752
+ '.preview-box { flex: 1; display: flex; align-items: center; justify-content: center; background: #000; overflow: hidden; padding: 16px; }',
2753
+ '.btn { background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.15); color: #fff; padding: 6px 14px; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 600; }',
2754
+ '.btn:hover { background: rgba(255,255,255,0.2); }',
2755
+ '.btn-nav { font-size: 16px; padding: 10px 24px; border-radius: 8px; }',
2756
+ '.bottom-nav { display: flex; align-items: center; justify-content: space-between; padding: 14px 24px; background: #141522; border-top: 1px solid rgba(255,255,255,0.12); }',
2757
+ '</style>',
2758
+ '</head>',
2759
+ '<body>',
2760
+ '<div class="top-bar">',
2761
+ ' <div style="font-weight: 700; font-size: 16px;">' + (document.title || 'Slide Deck') + '</div>',
2762
+ ' <div style="display: flex; align-items: center; gap: 12px;">',
2763
+ ' <div class="timer-badge">\u23F1 <span id="timer-val">00:00</span></div>',
2764
+ ' <button class="btn" id="btn-toggle-t">Pause</button>',
2765
+ ' <button class="btn" id="btn-reset-t">Reset</button>',
2766
+ ' </div>',
2767
+ '</div>',
2768
+ '<div class="grid-container">',
2769
+ ' <div class="panel-card">',
2770
+ ' <div class="card-header"><span>CURRENT SLIDE</span><span id="slide-num">' + curSlideTitle + '</span></div>',
2771
+ ' <div class="preview-box"><div style="font-size: 22px; color: #94a3b8; text-align: center;">Active Slide On Screen<br/><span style="font-size: 14px; color: #38bdf8;">(Broadcasting Synced)</span></div></div>',
2772
+ ' <div class="card-header" style="border-top: 1px solid rgba(255,255,255,0.08);"><span>NEXT SLIDE PREVIEW</span></div>',
2773
+ ' <div style="padding: 12px 16px; font-size: 14px; color: #94a3b8; background: rgba(0,0,0,0.3); min-height: 80px;" id="next-preview-text">Upcoming: Slide ' + (currentSlide + 2) + '</div>',
2774
+ ' </div>',
2775
+ ' <div class="panel-card">',
2776
+ ' <div class="card-header"><span>SPEAKER NOTES / TALK TRACK</span><div><button class="btn" id="font-dn" style="padding: 2px 8px;">A-</button> <button class="btn" id="font-up" style="padding: 2px 8px;">A+</button></div></div>',
2777
+ ' <div class="notes-content" id="speaker-notes">' + (currentNotes ? currentNotes : '<span style="opacity: 0.4;">No speaker notes for this slide.</span>') + '</div>',
2778
+ ' </div>',
2779
+ '</div>',
2780
+ '<div class="bottom-nav">',
2781
+ ' <button class="btn btn-nav" id="btn-prev-p">\u2039 Previous (\u2190)</button>',
2782
+ ' <span style="font-size: 14px; color: #a1a1aa;">Use Arrow Keys or Space to advance</span>',
2783
+ ' <button class="btn btn-nav" id="btn-next-p" style="background: #0284c7; border-color: #0284c7;">Next (\u2192 / Space) \u203A</button>',
2784
+ '</div>',
2785
+ '<script>',
2786
+ ' const bc = new BroadcastChannel("' + SYNC_CHANNEL_NAME + '");',
2787
+ ' let curSecs = ' + timerSeconds + ';',
2788
+ ' function sendCmd(action, idx) { bc.postMessage({ type: "COMMAND", action: action, index: idx }); }',
2789
+ ' document.getElementById("btn-prev-p").onclick = () => sendCmd("PREV");',
2790
+ ' document.getElementById("btn-next-p").onclick = () => sendCmd("NEXT");',
2791
+ ' document.getElementById("btn-reset-t").onclick = () => sendCmd("RESET_TIMER");',
2792
+ ' document.getElementById("btn-toggle-t").onclick = () => sendCmd("TOGGLE_TIMER");',
2793
+ ' let curFontSize = 19;',
2794
+ ' document.getElementById("font-up").onclick = () => { curFontSize += 2; document.getElementById("speaker-notes").style.fontSize = curFontSize + "px"; };',
2795
+ ' document.getElementById("font-dn").onclick = () => { curFontSize = Math.max(12, curFontSize - 2); document.getElementById("speaker-notes").style.fontSize = curFontSize + "px"; };',
2796
+ ' window.onkeydown = (e) => {',
2797
+ ' if (["ArrowRight", "Space", "PageDown"].includes(e.code)) { e.preventDefault(); sendCmd("NEXT"); }',
2798
+ ' else if (["ArrowLeft", "PageUp"].includes(e.code)) { e.preventDefault(); sendCmd("PREV"); }',
2799
+ ' };',
2800
+ ' bc.onmessage = (e) => {',
2801
+ ' if (!e.data || e.data.type !== "STATE_UPDATE") return;',
2802
+ ' document.getElementById("slide-num").textContent = "Slide " + (e.data.currentSlide + 1) + " / " + e.data.totalSlides;',
2803
+ ' document.getElementById("speaker-notes").textContent = e.data.notes || "No speaker notes for this slide.";',
2804
+ ' document.getElementById("next-preview-text").textContent = e.data.nextNotes ? "Next Notes: " + e.data.nextNotes : "Next: Slide " + (e.data.currentSlide + 2);',
2805
+ ' curSecs = e.data.timerSeconds || 0;',
2806
+ ' const m = String(Math.floor(curSecs / 60)).padStart(2, "0");',
2807
+ ' const s = String(curSecs % 60).padStart(2, "0");',
2808
+ ' document.getElementById("timer-val").textContent = m + ":" + s;',
2809
+ ' document.getElementById("btn-toggle-t").textContent = e.data.timerRunning ? "Pause" : "Start";',
2810
+ ' };',
2811
+ ' bc.postMessage({ type: "REQUEST_STATE" });',
2812
+ '<' + '/script>',
2813
+ '</body>',
2814
+ '</html>'
2815
+ ].join('\\n');
2847
2816
 
2848
2817
  win.document.open();
2849
2818
  win.document.write(consoleHtml);
@@ -2859,8 +2828,7 @@ function generateHtmlShell(options) {
2859
2828
  const card = document.createElement('div');
2860
2829
  card.className = 'deck-notes-card';
2861
2830
  card.setAttribute('data-for-slide', String(idx));
2862
- card.innerHTML = '<div class="deck-notes-card-header"><span>\u{1F4DD}</span><span>Presenter Notes (Slide ' + (idx + 1) + ')</span></div><div>' + notes.replace(/
2863
- /g, '<br/>') + '</div>';
2831
+ card.innerHTML = '<div class="deck-notes-card-header"><span>\u{1F4DD}</span><span>Presenter Notes (Slide ' + (idx + 1) + ')</span></div><div>' + notes.split('\\n').join('<br/>') + '</div>';
2864
2832
  slide.after(card);
2865
2833
  }
2866
2834
  });
@@ -3200,8 +3168,7 @@ function generateHtmlShell(options) {
3200
3168
  if (scalePanel) scalePanel.style.display = 'none';
3201
3169
 
3202
3170
  // Take snapshot of full HTML
3203
- let cleanHtml = '<!DOCTYPE html>
3204
- ' + document.documentElement.outerHTML;
3171
+ let cleanHtml = '<!DOCTYPE html>' + '\\n' + document.documentElement.outerHTML;
3205
3172
 
3206
3173
  // Clean any temporary runtime inline styles or attributes
3207
3174
  cleanHtml = cleanHtml
@@ -3211,28 +3178,20 @@ function generateHtmlShell(options) {
3211
3178
  .replace(/class="edit-toggle[^"]*"/g, 'class="edit-toggle"');
3212
3179
 
3213
3180
  // Embed current scale settings into :root style definition so exported HTML is permanently styled with chosen scales
3214
- const scaleVarsReplacement =
3215
- '--scale-mode: ' + scaleState.mode + ';
3216
- ' +
3217
- ' --scale-factor: ' + scaleState.factor + ';
3218
- ' +
3219
- ' --scale-headings: ' + scaleState.headings + ';
3220
- ' +
3221
- ' --scale-body: ' + scaleState.body + ';
3222
- ' +
3223
- ' --scale-code: ' + scaleState.code + ';
3224
- ' +
3225
- ' --scale-spacing: ' + scaleState.spacing + ';
3226
- ' +
3227
- ' --scale-headings-eff: ' + (scaleState.mode === 'unified' ? scaleState.factor : scaleState.headings) + ';
3228
- ' +
3229
- ' --scale-body-eff: ' + (scaleState.mode === 'unified' ? scaleState.factor : scaleState.body) + ';
3230
- ' +
3231
- ' --scale-code-eff: ' + (scaleState.mode === 'unified' ? scaleState.factor : scaleState.code) + ';
3232
- ' +
3233
- ' --scale-spacing-eff: ' + (scaleState.mode === 'unified' ? scaleState.factor : scaleState.spacing) + ';';
3234
-
3235
- cleanHtml = cleanHtml.replace(/--scale-mode:s*unified;[sS]*?--scale-spacing-eff:s*1.0;/, scaleVarsReplacement);
3181
+ const scaleVarsReplacement = [
3182
+ '--scale-mode: ' + scaleState.mode + ';',
3183
+ ' --scale-factor: ' + scaleState.factor + ';',
3184
+ ' --scale-headings: ' + scaleState.headings + ';',
3185
+ ' --scale-body: ' + scaleState.body + ';',
3186
+ ' --scale-code: ' + scaleState.code + ';',
3187
+ ' --scale-spacing: ' + scaleState.spacing + ';',
3188
+ ' --scale-headings-eff: ' + (scaleState.mode === 'unified' ? scaleState.factor : scaleState.headings) + ';',
3189
+ ' --scale-body-eff: ' + (scaleState.mode === 'unified' ? scaleState.factor : scaleState.body) + ';',
3190
+ ' --scale-code-eff: ' + (scaleState.mode === 'unified' ? scaleState.factor : scaleState.code) + ';',
3191
+ ' --scale-spacing-eff: ' + (scaleState.mode === 'unified' ? scaleState.factor : scaleState.spacing) + ';'
3192
+ ].join('\\n');
3193
+
3194
+ cleanHtml = cleanHtml.replace(/--scale-mode:\\s*[^;]+;[\\s\\S]*?--scale-spacing-eff:\\s*[^;]+;/, scaleVarsReplacement);
3236
3195
 
3237
3196
  // Create Blob and trigger download
3238
3197
  const blob = new Blob([cleanHtml], { type: 'text/html;charset=utf-8' });
@@ -3401,6 +3360,8 @@ function generateHtmlShell(options) {
3401
3360
  applyScales();
3402
3361
  } else if (e.data.type === 'TOGGLE_SCALE_PANEL') {
3403
3362
  toggleScalePanel();
3363
+ } else if (e.data.type === 'RESIZE') {
3364
+ resizeCanvas();
3404
3365
  }
3405
3366
  });
3406
3367
 
@@ -3437,7 +3398,8 @@ function compileHtmlDeck(deckData, options) {
3437
3398
  theme,
3438
3399
  slidesHtml,
3439
3400
  slideCount,
3440
- customHeadTags: options?.customHeadTags
3401
+ customHeadTags: options?.customHeadTags,
3402
+ deckId: deckData?.id || options?.deckId
3441
3403
  });
3442
3404
  return {
3443
3405
  html: fullHtml,
@@ -3460,5 +3422,5 @@ exports.serializeLayoutToComment = serializeLayoutToComment;
3460
3422
  exports.splitMarkdownSlides = splitMarkdownSlides;
3461
3423
  exports.updateSlideLayoutInMarkdown = updateSlideLayoutInMarkdown;
3462
3424
  exports.updateSlideTextInMarkdown = updateSlideTextInMarkdown;
3463
- //# sourceMappingURL=chunk-53GLUCYC.cjs.map
3464
- //# sourceMappingURL=chunk-53GLUCYC.cjs.map
3425
+ //# sourceMappingURL=chunk-SUGKWGJJ.cjs.map
3426
+ //# sourceMappingURL=chunk-SUGKWGJJ.cjs.map