slidemind 0.3.13 → 0.3.14
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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/AGENTS.md +86 -21
- package/README.md +14 -17
- package/bin/slidemind.mjs +65 -4969
- package/package.json +1 -1
- package/skills/create-slides/SKILL.md +113 -74
- package/skills/search/SKILL.md +25 -7
- package/src/bridge/build-and-push.mjs +108 -15
- package/src/bridge/bundle.mjs +69 -0
- package/src/bridge/capture.mjs +19 -0
- package/src/bridge/client/brands.mjs +622 -0
- package/src/bridge/client/engine-routing.mjs +237 -0
- package/src/bridge/client/engine-search.mjs +310 -0
- package/src/bridge/client/engine-slides.mjs +435 -0
- package/src/bridge/client/pane-ops.mjs +335 -0
- package/src/bridge/client/presentations.mjs +587 -0
- package/src/bridge/client/render.mjs +317 -0
- package/src/bridge/client/transport.mjs +695 -0
- package/src/bridge/client.mjs +80 -3452
- package/src/bridge/config.mjs +1 -1
- package/src/bridge/engine-download.mjs +15 -2
- package/src/builders/build.mjs +11 -804
- package/src/builders/draft-check.mjs +376 -0
- package/src/builders/lint.mjs +38 -2
- package/src/builders/preview.html +799 -0
- package/src/builders/review.mjs +23 -4
- package/src/builders/visual-families.mjs +6 -8
- package/src/cli/common.mjs +179 -0
- package/src/cli/concepts.mjs +310 -0
- package/src/cli/connection.mjs +147 -0
- package/src/cli/deck.mjs +608 -0
- package/src/cli/designs.mjs +113 -0
- package/src/cli/diagnose.mjs +141 -0
- package/src/cli/download-bundle.mjs +144 -0
- package/src/cli/engine-helpers.mjs +429 -0
- package/src/cli/engine-results.mjs +320 -0
- package/src/cli/help.mjs +201 -0
- package/src/cli/install.mjs +257 -0
- package/src/cli/read.mjs +120 -0
- package/src/cli/ready.mjs +805 -0
- package/src/cli/search.mjs +608 -0
- package/src/cli/session.mjs +177 -0
- package/src/cli/setup.mjs +282 -0
- package/src/cli/slide-reuse.mjs +334 -0
- package/src/cli/status.mjs +118 -0
- package/src/cli/workspace.mjs +302 -0
- package/src/installers/codex.mjs +1 -1
- package/src/update-check.mjs +231 -0
package/src/builders/build.mjs
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// Pure Node stdlib — no external dependencies.
|
|
4
4
|
//
|
|
5
5
|
// Usage:
|
|
6
|
-
// slidemind
|
|
7
|
-
//
|
|
6
|
+
// slidemind view-draft <deck> (the CLI entry point; it builds, then captures)
|
|
7
|
+
// node src/builders/build.mjs --deck <deck> [--open] [--fix]
|
|
8
8
|
//
|
|
9
9
|
// Convention:
|
|
10
10
|
// <deck>/
|
|
@@ -286,806 +286,13 @@ function collectSvgAssets(deckDir) {
|
|
|
286
286
|
return map;
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
<!-- Arimo: open-source Arial-metric-compatible font. We alias it to
|
|
297
|
-
"Arial" so master text pinned to Arial gets a font with the same
|
|
298
|
-
horizontal extents PowerPoint/LibreOffice use, instead of macOS
|
|
299
|
-
Chrome's slightly wider system Arial that pushes long strings
|
|
300
|
-
(addresses, single-line headers) past their bounding boxes. -->
|
|
301
|
-
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
302
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
303
|
-
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Arimo:ital,wght@0,400..700;1,400..700&display=swap" />
|
|
304
|
-
<link rel="stylesheet" href="./corporate-design.css" />
|
|
305
|
-
<link rel="stylesheet" href="./layouts.css" />
|
|
306
|
-
<style>
|
|
307
|
-
/* Compatibility aliases for decks/slides authored against the older
|
|
308
|
-
statworx token names. New materialized bundles expose semantic tokens
|
|
309
|
-
(--fg-*, --bg-*, --border-*); existing slide JSX may still refer
|
|
310
|
-
to --sw-*. Keep those declarations valid in the HTML preview. */
|
|
311
|
-
:root {
|
|
312
|
-
--sw-black: var(--fg-1, #000000);
|
|
313
|
-
--sw-white: var(--bg-1, #ffffff);
|
|
314
|
-
--sw-ink: var(--fg-2, #283440);
|
|
315
|
-
--sw-slate: var(--fg-3, #6C7D8C);
|
|
316
|
-
--sw-gray: var(--fg-4, #B6BDCC);
|
|
317
|
-
--sw-fog: var(--bg-2, #EBF0F2);
|
|
318
|
-
--sw-ice-blue: var(--bg-3, #E6E6FF);
|
|
319
|
-
--sw-tech-blue: var(--fg-accent, #0000FF);
|
|
320
|
-
--sw-tech-blue-10: #E6E6FF;
|
|
321
|
-
--sw-tech-blue-20: #CCCCFF;
|
|
322
|
-
--sw-tech-blue-40: #9999FF;
|
|
323
|
-
--sw-tech-blue-80: #3333FF;
|
|
324
|
-
--sw-magenta: #E6007E;
|
|
325
|
-
}
|
|
326
|
-
/* A data-connector div is a directive, not a painted box (the render
|
|
327
|
-
derives the line between its two referenced nodes). Same rule the
|
|
328
|
-
bundle's corporate-design.css carries; repeated here so a preview
|
|
329
|
-
against an older bundle lays out like the render will. */
|
|
330
|
-
[data-connector] { position: absolute; width: 0; height: 0; padding: 0; margin: 0; border-color: transparent; pointer-events: none; }
|
|
331
|
-
[data-sm-connector-overlay] { position: absolute; left: 0; top: 0; width: 1280px; height: 720px; overflow: visible; pointer-events: none; z-index: 2147483000; }
|
|
332
|
-
</style>
|
|
333
|
-
<style>
|
|
334
|
-
/* Substitute Arial with Arimo (a metric clone) so wrap behaviour
|
|
335
|
-
matches PowerPoint's rendering. Local Arial wins only if Arimo
|
|
336
|
-
hasn't loaded yet. */
|
|
337
|
-
@font-face {
|
|
338
|
-
font-family: "Arial";
|
|
339
|
-
src: local("Arimo"), local("Arial");
|
|
340
|
-
font-weight: 400;
|
|
341
|
-
font-style: normal;
|
|
342
|
-
}
|
|
343
|
-
@font-face {
|
|
344
|
-
font-family: "Arial";
|
|
345
|
-
src: local("Arimo Bold"), local("Arial Bold");
|
|
346
|
-
font-weight: 700;
|
|
347
|
-
font-style: normal;
|
|
348
|
-
}
|
|
349
|
-
@font-face {
|
|
350
|
-
font-family: "Arial";
|
|
351
|
-
src: local("Arimo Italic"), local("Arial Italic");
|
|
352
|
-
font-weight: 400;
|
|
353
|
-
font-style: italic;
|
|
354
|
-
}
|
|
355
|
-
@font-face {
|
|
356
|
-
font-family: "Arial";
|
|
357
|
-
src: local("Arimo Bold Italic"), local("Arial Bold Italic");
|
|
358
|
-
font-weight: 700;
|
|
359
|
-
font-style: italic;
|
|
360
|
-
}
|
|
361
|
-
</style>
|
|
362
|
-
<style id="brand-font-faces">__BRAND_FONT_FACES__</style>
|
|
363
|
-
<!-- Chart palette for the <Chart> stand-in (from the bundle's capture engine;
|
|
364
|
-
empty when the bundle carries no chart runtime). -->
|
|
365
|
-
<style id="chart-theme-tokens">__CHART_THEME_TOKENS__</style>
|
|
366
|
-
<style>
|
|
367
|
-
html, body { margin: 0; padding: 0; height: 100%; background: #0a0a0a; overflow: hidden; }
|
|
368
|
-
:root {
|
|
369
|
-
--deck-rail-w: 220px;
|
|
370
|
-
--deck-chrome-h: 62px;
|
|
371
|
-
--deck-chrome-bg: #111113;
|
|
372
|
-
--deck-chrome-bg-2: #161618;
|
|
373
|
-
--deck-chrome-border: #2b2b30;
|
|
374
|
-
--deck-chrome-fg: #ededed;
|
|
375
|
-
--deck-chrome-dim: #9a9aa0;
|
|
376
|
-
}
|
|
377
|
-
.deck {
|
|
378
|
-
position: fixed;
|
|
379
|
-
inset: 0 0 0 var(--deck-rail-w);
|
|
380
|
-
display: flex;
|
|
381
|
-
align-items: center;
|
|
382
|
-
justify-content: center;
|
|
383
|
-
}
|
|
384
|
-
.deck__stage {
|
|
385
|
-
width: 1280px;
|
|
386
|
-
height: 720px;
|
|
387
|
-
transform-origin: center center;
|
|
388
|
-
background: var(--bg-1);
|
|
389
|
-
box-shadow: 0 20px 60px rgba(0,0,0,0.35);
|
|
390
|
-
}
|
|
391
|
-
.deck__nav {
|
|
392
|
-
position: fixed;
|
|
393
|
-
bottom: 16px;
|
|
394
|
-
right: 16px;
|
|
395
|
-
padding: 6px 12px;
|
|
396
|
-
border-radius: 999px;
|
|
397
|
-
color: rgba(255,255,255,0.65);
|
|
398
|
-
background: rgba(255,255,255,0.08);
|
|
399
|
-
backdrop-filter: blur(6px);
|
|
400
|
-
font-family: var(--font-sans, sans-serif);
|
|
401
|
-
font-size: 12px;
|
|
402
|
-
letter-spacing: 0.02em;
|
|
403
|
-
user-select: none;
|
|
404
|
-
}
|
|
405
|
-
.deck__rail {
|
|
406
|
-
position: fixed;
|
|
407
|
-
top: 0;
|
|
408
|
-
left: 0;
|
|
409
|
-
bottom: 0;
|
|
410
|
-
width: var(--deck-rail-w);
|
|
411
|
-
z-index: 2147483590;
|
|
412
|
-
display: flex;
|
|
413
|
-
flex-direction: column;
|
|
414
|
-
background: var(--deck-chrome-bg);
|
|
415
|
-
border-right: 1px solid var(--deck-chrome-border);
|
|
416
|
-
color: var(--deck-chrome-fg);
|
|
417
|
-
font-family: var(--font-sans, sans-serif);
|
|
418
|
-
}
|
|
419
|
-
.deck__thumbs {
|
|
420
|
-
flex: 1 1 auto;
|
|
421
|
-
overflow-y: auto;
|
|
422
|
-
padding: 12px 14px 18px;
|
|
423
|
-
scrollbar-width: none;
|
|
424
|
-
-ms-overflow-style: none;
|
|
425
|
-
}
|
|
426
|
-
.deck__thumbs::-webkit-scrollbar {
|
|
427
|
-
width: 0;
|
|
428
|
-
height: 0;
|
|
429
|
-
display: none;
|
|
430
|
-
}
|
|
431
|
-
.deck__thumb {
|
|
432
|
-
width: 100%;
|
|
433
|
-
margin: 0 0 12px;
|
|
434
|
-
padding: 8px;
|
|
435
|
-
border: 1px solid var(--deck-chrome-border);
|
|
436
|
-
border-radius: 8px;
|
|
437
|
-
background: rgba(255,255,255,0.025);
|
|
438
|
-
color: inherit;
|
|
439
|
-
cursor: pointer;
|
|
440
|
-
text-align: left;
|
|
441
|
-
transition: border-color 120ms ease, background 120ms ease, box-shadow 120ms ease;
|
|
442
|
-
}
|
|
443
|
-
.deck__thumb:hover {
|
|
444
|
-
background: rgba(255,255,255,0.06);
|
|
445
|
-
border-color: #3a3a40;
|
|
446
|
-
}
|
|
447
|
-
.deck__thumb.is-active {
|
|
448
|
-
border-color: rgba(237,237,237,0.7);
|
|
449
|
-
background: rgba(255,255,255,0.085);
|
|
450
|
-
box-shadow: 0 0 0 2px rgba(237,237,237,0.08);
|
|
451
|
-
}
|
|
452
|
-
.deck__thumb-preview {
|
|
453
|
-
width: 176px;
|
|
454
|
-
height: 99px;
|
|
455
|
-
overflow: hidden;
|
|
456
|
-
background: #fff;
|
|
457
|
-
box-shadow: 0 4px 14px rgba(0,0,0,0.28);
|
|
458
|
-
pointer-events: none;
|
|
459
|
-
}
|
|
460
|
-
.deck__thumb-frame {
|
|
461
|
-
display: block;
|
|
462
|
-
width: 176px;
|
|
463
|
-
height: 99px;
|
|
464
|
-
border: 0;
|
|
465
|
-
background: #fff;
|
|
466
|
-
pointer-events: none;
|
|
467
|
-
}
|
|
468
|
-
.deck__thumb-meta {
|
|
469
|
-
display: flex;
|
|
470
|
-
align-items: baseline;
|
|
471
|
-
gap: 7px;
|
|
472
|
-
margin-top: 7px;
|
|
473
|
-
min-width: 0;
|
|
474
|
-
font-size: 11px;
|
|
475
|
-
line-height: 1.25;
|
|
476
|
-
}
|
|
477
|
-
.deck__thumb-number {
|
|
478
|
-
flex: 0 0 auto;
|
|
479
|
-
color: var(--deck-chrome-dim);
|
|
480
|
-
font-variant-numeric: tabular-nums;
|
|
481
|
-
}
|
|
482
|
-
.deck__thumb-name {
|
|
483
|
-
min-width: 0;
|
|
484
|
-
overflow: hidden;
|
|
485
|
-
text-overflow: ellipsis;
|
|
486
|
-
white-space: nowrap;
|
|
487
|
-
}
|
|
488
|
-
.deck__error {
|
|
489
|
-
padding: 32px;
|
|
490
|
-
color: #ff6b6b;
|
|
491
|
-
background: #1a0000;
|
|
492
|
-
font-family: var(--font-mono, monospace);
|
|
493
|
-
font-size: 14px;
|
|
494
|
-
white-space: pre-wrap;
|
|
495
|
-
}
|
|
496
|
-
/* Export mode (?export=1) — used by the PPTX exporter's headless Chrome
|
|
497
|
-
so it captures a clean 1280x720 stage with no letterbox/nav/scaling. */
|
|
498
|
-
body.deck--export { background: #ffffff !important; overflow: hidden; }
|
|
499
|
-
body.deck--export .deck { position: static; display: block; inset: auto; }
|
|
500
|
-
body.deck--export .deck__stage {
|
|
501
|
-
transform: none !important;
|
|
502
|
-
box-shadow: none !important;
|
|
503
|
-
margin: 0;
|
|
504
|
-
position: static;
|
|
505
|
-
}
|
|
506
|
-
body.deck--export .deck__nav,
|
|
507
|
-
body.deck--export .deck__rail { display: none !important; }
|
|
508
|
-
/* Thumbnail mode runs inside the left rail's isolated iframes. It must
|
|
509
|
-
render exactly one slide and no chrome, so per-slide <style> tags do not
|
|
510
|
-
leak into the main review document. */
|
|
511
|
-
body.deck--thumb { background: transparent !important; overflow: hidden; }
|
|
512
|
-
body.deck--thumb .deck { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; }
|
|
513
|
-
body.deck--thumb .deck__stage { box-shadow: none !important; }
|
|
514
|
-
body.deck--thumb .deck__nav,
|
|
515
|
-
body.deck--thumb .deck__rail { display: none !important; }
|
|
516
|
-
/* Print mode (?print=1) — stacks every slide as its own page so the
|
|
517
|
-
browser's Print / Cmd-P renders one slide per page (proofing). */
|
|
518
|
-
body.deck--print {
|
|
519
|
-
background: #ffffff !important;
|
|
520
|
-
overflow: visible !important;
|
|
521
|
-
height: auto !important;
|
|
522
|
-
}
|
|
523
|
-
body.deck--print .deck,
|
|
524
|
-
body.deck--print .deck__nav,
|
|
525
|
-
body.deck--print .deck__rail { display: none !important; }
|
|
526
|
-
.print-deck { background: #ffffff; }
|
|
527
|
-
.print-page {
|
|
528
|
-
width: 1280px;
|
|
529
|
-
height: 720px;
|
|
530
|
-
position: relative;
|
|
531
|
-
overflow: hidden;
|
|
532
|
-
background: var(--bg-1);
|
|
533
|
-
page-break-after: always;
|
|
534
|
-
break-after: page;
|
|
535
|
-
}
|
|
536
|
-
.print-page:last-child {
|
|
537
|
-
page-break-after: auto;
|
|
538
|
-
break-after: auto;
|
|
539
|
-
}
|
|
540
|
-
.print-page__error {
|
|
541
|
-
padding: 32px;
|
|
542
|
-
color: #ff6b6b;
|
|
543
|
-
background: #1a0000;
|
|
544
|
-
font-family: var(--font-mono, monospace);
|
|
545
|
-
font-size: 14px;
|
|
546
|
-
white-space: pre-wrap;
|
|
547
|
-
width: 100%;
|
|
548
|
-
height: 100%;
|
|
549
|
-
box-sizing: border-box;
|
|
550
|
-
}
|
|
551
|
-
@media print {
|
|
552
|
-
@page { size: 1280px 720px; margin: 0; }
|
|
553
|
-
html, body { background: #ffffff !important; }
|
|
554
|
-
}
|
|
555
|
-
</style>
|
|
556
|
-
</head>
|
|
557
|
-
<body>
|
|
558
|
-
<aside class="deck__rail" id="slide-rail" aria-label="Slide thumbnails">
|
|
559
|
-
<div class="deck__thumbs" id="slide-thumbs"></div>
|
|
560
|
-
</aside>
|
|
561
|
-
<div class="deck">
|
|
562
|
-
<div class="deck__stage" id="stage"></div>
|
|
563
|
-
</div>
|
|
564
|
-
<div class="deck__nav" id="nav">loading…</div>
|
|
565
|
-
|
|
566
|
-
<!-- Layout sources (inlined from skills/create-slides/layouts/*.jsx).
|
|
567
|
-
Each one is evaluated once and exposed under its binding name
|
|
568
|
-
(SlideCover, SlideAgendaBlack, ...) so slides can reference it. -->
|
|
569
|
-
__LAYOUTS__
|
|
570
|
-
|
|
571
|
-
<!-- Slide sources (inlined from this deck's slides/ folder, filename order). -->
|
|
572
|
-
__SLIDES__
|
|
573
|
-
|
|
574
|
-
<!-- Runtime: React + Babel-standalone from CDN. -->
|
|
575
|
-
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
|
|
576
|
-
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
|
|
577
|
-
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
|
578
|
-
<!-- The <Chart> stand-in runtime: the add-in's own vendored scripts, inlined
|
|
579
|
-
from the bundle's capture engine (this CLI ships no chart source). They
|
|
580
|
-
install window.SlidemindChart, which the bootstrap below binds as Chart
|
|
581
|
-
before any slide is evaluated. -->
|
|
582
|
-
__CHART_PREVIEW_SCRIPTS__
|
|
583
|
-
<!-- The exporter's connector geometry (connector-derive), inlined from the
|
|
584
|
-
bundle's capture engine: the overlay below derives every data-connector
|
|
585
|
-
route and arrowhead exactly as the .pptx export does. -->
|
|
586
|
-
__CONNECTOR_PREVIEW_SCRIPT__
|
|
587
|
-
<script>window.__SLIDE_EDITOR_OVERRIDES__ = __OVERRIDES__;</script>
|
|
588
|
-
<script>window.SVG_ASSETS = __SVG_ASSETS__;</script>
|
|
589
|
-
<script>
|
|
590
|
-
__RUNTIME_APPLIER__
|
|
591
|
-
</script>
|
|
592
|
-
|
|
593
|
-
<script>
|
|
594
|
-
(function () {
|
|
595
|
-
// Map kebab-case filename stem -> PascalCase layout binding name.
|
|
596
|
-
function layoutBindingName(stem) {
|
|
597
|
-
return "Slide" + stem.split("-").map(function (p) {
|
|
598
|
-
return p ? p[0].toUpperCase() + p.slice(1) : "";
|
|
599
|
-
}).join("");
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
// Stamp every intrinsic JSX element with data-sm-loc="<prefix>:<line>:<col>"
|
|
603
|
-
// so the preview-review overlay (review-overlay.js) can resolve a click back
|
|
604
|
-
// to the exact slide source location. Runs as a Babel plugin BEFORE the react
|
|
605
|
-
// preset (plugins run before presets) so it sees JSX nodes, not createElement
|
|
606
|
-
// calls. Inert without the overlay. Mirrors the add-in's instrument-source-loc.
|
|
607
|
-
function makeSourceLocPlugin(locPrefix) {
|
|
608
|
-
return function (babel) {
|
|
609
|
-
var t = babel.types;
|
|
610
|
-
return {
|
|
611
|
-
visitor: {
|
|
612
|
-
JSXOpeningElement: function (path) {
|
|
613
|
-
var node = path.node;
|
|
614
|
-
var nameNode = node.name;
|
|
615
|
-
// Intrinsic elements only: a lowercase JSXIdentifier. Skip
|
|
616
|
-
// components (<SlideCover>), member/namespaced names, fragments.
|
|
617
|
-
// <Chart> is the one component that opts in (as in the add-in's
|
|
618
|
-
// instrument-source-loc): it forwards the attribute onto its
|
|
619
|
-
// root div, so a review pin on a chart resolves to its tag.
|
|
620
|
-
if (!nameNode || nameNode.type !== "JSXIdentifier") return;
|
|
621
|
-
if (!/^[a-z]/.test(nameNode.name) && nameNode.name !== "Chart") return;
|
|
622
|
-
for (var i = 0; i < node.attributes.length; i++) {
|
|
623
|
-
var a = node.attributes[i];
|
|
624
|
-
if (a.type === "JSXAttribute" && a.name &&
|
|
625
|
-
a.name.name === "data-sm-loc") return;
|
|
626
|
-
}
|
|
627
|
-
var start = node.loc && node.loc.start;
|
|
628
|
-
if (!start) return;
|
|
629
|
-
var value = locPrefix + ":" + start.line + ":" + (start.column + 1);
|
|
630
|
-
node.attributes.push(
|
|
631
|
-
t.jsxAttribute(t.jsxIdentifier("data-sm-loc"),
|
|
632
|
-
t.stringLiteral(value))
|
|
633
|
-
);
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
};
|
|
637
|
-
};
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
function evalJsx(src, filename, extra) {
|
|
641
|
-
// The inliner wraps each source as "\\n" + src + "\\n" inside the <script>
|
|
642
|
-
// block; strip that one leading newline so Babel's line numbers (and thus
|
|
643
|
-
// data-sm-loc) align with the author's pristine workspace file.
|
|
644
|
-
if (src.charAt(0) === "\\n") src = src.slice(1);
|
|
645
|
-
var result = Babel.transform(src, {
|
|
646
|
-
presets: [["react", { runtime: "classic" }]],
|
|
647
|
-
plugins: [makeSourceLocPlugin(filename)],
|
|
648
|
-
sourceType: "module",
|
|
649
|
-
filename: filename
|
|
650
|
-
});
|
|
651
|
-
var body = result.code.replace(
|
|
652
|
-
/export\\s+default\\s+/g,
|
|
653
|
-
"__module.default = "
|
|
654
|
-
);
|
|
655
|
-
var names = ["__module", "React"].concat(Object.keys(extra || {}));
|
|
656
|
-
var values = [{}, React].concat(Object.keys(extra || {}).map(function (k) {
|
|
657
|
-
return extra[k];
|
|
658
|
-
}));
|
|
659
|
-
var factory = new Function(
|
|
660
|
-
names.join(","),
|
|
661
|
-
body + "\\nreturn __module.default;"
|
|
662
|
-
);
|
|
663
|
-
return factory.apply(null, values);
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
// Preview parity: replace <img src="*.svg"> whose file we inlined into
|
|
667
|
-
// window.SVG_ASSETS with the SVG markup itself, carrying the <img>'s box
|
|
668
|
-
// and color onto a wrapper so fill="currentColor" resolves — the same
|
|
669
|
-
// tint the .pptx renderer applies. Re-runs on every React render via a
|
|
670
|
-
// MutationObserver so navigating slides keeps icons inlined.
|
|
671
|
-
function smInlineSvgAssets() {
|
|
672
|
-
var ASSETS = (typeof window !== "undefined" && window.SVG_ASSETS) || null;
|
|
673
|
-
if (!ASSETS || !Object.keys(ASSETS).length) return;
|
|
674
|
-
|
|
675
|
-
function lookup(src) {
|
|
676
|
-
if (!src) return null;
|
|
677
|
-
var s = src.split("?")[0].split("#")[0];
|
|
678
|
-
var alt = s.indexOf("./") === 0 ? s.slice(2) : s;
|
|
679
|
-
var base = s.split("/").pop();
|
|
680
|
-
return ASSETS[s] || ASSETS[alt] || ASSETS[base] || null;
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
function inlineWithin(root) {
|
|
684
|
-
if (!root || !root.querySelectorAll) return;
|
|
685
|
-
var imgs = root.querySelectorAll("img");
|
|
686
|
-
for (var i = 0; i < imgs.length; i++) {
|
|
687
|
-
var img = imgs[i];
|
|
688
|
-
if (img.getAttribute("data-sm-inlined") === "1") continue;
|
|
689
|
-
var markup = lookup(img.getAttribute("src"));
|
|
690
|
-
if (!markup) continue;
|
|
691
|
-
var span = document.createElement("span");
|
|
692
|
-
span.setAttribute("data-sm-inlined", "1");
|
|
693
|
-
if (img.className) span.className = img.className;
|
|
694
|
-
var styleAttr = img.getAttribute("style");
|
|
695
|
-
if (styleAttr) span.setAttribute("style", styleAttr);
|
|
696
|
-
// Pull the laid-out box + colour even when they came from CSS, not
|
|
697
|
-
// an inline style, so sizing holds and currentColor resolves.
|
|
698
|
-
var cs = window.getComputedStyle ? window.getComputedStyle(img) : null;
|
|
699
|
-
var hasW = !!span.style.width;
|
|
700
|
-
var hasH = !!span.style.height;
|
|
701
|
-
if (cs) {
|
|
702
|
-
if (!hasW && cs.width && cs.width !== "auto" && cs.width !== "0px") {
|
|
703
|
-
span.style.width = cs.width;
|
|
704
|
-
hasW = true;
|
|
705
|
-
}
|
|
706
|
-
if (!hasH && cs.height && cs.height !== "auto" && cs.height !== "0px") {
|
|
707
|
-
span.style.height = cs.height;
|
|
708
|
-
hasH = true;
|
|
709
|
-
}
|
|
710
|
-
if (!span.style.color && cs.color) span.style.color = cs.color;
|
|
711
|
-
}
|
|
712
|
-
if (!span.style.display || span.style.display === "inline") {
|
|
713
|
-
span.style.display = "inline-flex";
|
|
714
|
-
}
|
|
715
|
-
if (!span.style.alignItems) span.style.alignItems = "center";
|
|
716
|
-
if (!span.style.justifyContent) span.style.justifyContent = "center";
|
|
717
|
-
span.innerHTML = markup;
|
|
718
|
-
var svg = span.querySelector("svg");
|
|
719
|
-
if (svg) {
|
|
720
|
-
svg.style.display = "block";
|
|
721
|
-
if (hasW || hasH) {
|
|
722
|
-
svg.style.width = "100%";
|
|
723
|
-
svg.style.height = "100%";
|
|
724
|
-
svg.removeAttribute("width");
|
|
725
|
-
svg.removeAttribute("height");
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
if (img.parentNode) img.parentNode.replaceChild(span, img);
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
var scheduled = false;
|
|
733
|
-
var raf =
|
|
734
|
-
window.requestAnimationFrame ||
|
|
735
|
-
function (cb) { return setTimeout(cb, 16); };
|
|
736
|
-
function schedule() {
|
|
737
|
-
if (scheduled) return;
|
|
738
|
-
scheduled = true;
|
|
739
|
-
raf(function () {
|
|
740
|
-
scheduled = false;
|
|
741
|
-
inlineWithin(document.body);
|
|
742
|
-
});
|
|
743
|
-
}
|
|
744
|
-
if (typeof MutationObserver !== "undefined") {
|
|
745
|
-
new MutationObserver(schedule).observe(document.body, {
|
|
746
|
-
childList: true,
|
|
747
|
-
subtree: true,
|
|
748
|
-
});
|
|
749
|
-
}
|
|
750
|
-
schedule();
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
var LAYOUTS = {};
|
|
754
|
-
document.querySelectorAll('script[data-kind="layout"]').forEach(function (s) {
|
|
755
|
-
var name = s.dataset.binding || layoutBindingName(s.dataset.name);
|
|
756
|
-
try {
|
|
757
|
-
LAYOUTS[name] = evalJsx(
|
|
758
|
-
s.textContent,
|
|
759
|
-
"layouts/" + s.dataset.name + ".jsx",
|
|
760
|
-
{}
|
|
761
|
-
);
|
|
762
|
-
window[name] = LAYOUTS[name];
|
|
763
|
-
} catch (err) {
|
|
764
|
-
console.error("Layout " + name + " failed to load:", err);
|
|
765
|
-
throw err;
|
|
766
|
-
}
|
|
767
|
-
});
|
|
768
|
-
|
|
769
|
-
// The Chart binding slide jsx is compiled against — installed by the
|
|
770
|
-
// bundle's chart-preview scripts above; without them a chart renders
|
|
771
|
-
// nothing, exactly as the add-in's page behaves.
|
|
772
|
-
var Chart = window.SlidemindChart
|
|
773
|
-
? window.SlidemindChart.createComponent(React)
|
|
774
|
-
: function () { return null; };
|
|
775
|
-
var slideScope = Object.assign({ Chart: Chart }, LAYOUTS);
|
|
776
|
-
|
|
777
|
-
var slides = [];
|
|
778
|
-
document.querySelectorAll('script[data-kind="slide"]').forEach(function (s) {
|
|
779
|
-
var idx = parseInt(s.dataset.index, 10);
|
|
780
|
-
var name = s.dataset.name;
|
|
781
|
-
try {
|
|
782
|
-
var Component = evalJsx(
|
|
783
|
-
s.textContent,
|
|
784
|
-
"slides/" + name + ".jsx",
|
|
785
|
-
slideScope
|
|
786
|
-
);
|
|
787
|
-
slides[idx] = { name: name, Component: Component };
|
|
788
|
-
} catch (err) {
|
|
789
|
-
console.error("Slide " + name + " failed to load:", err);
|
|
790
|
-
slides[idx] = { name: name, error: err };
|
|
791
|
-
}
|
|
792
|
-
});
|
|
793
|
-
|
|
794
|
-
var stage = document.getElementById("stage");
|
|
795
|
-
var nav = document.getElementById("nav");
|
|
796
|
-
var rail = document.getElementById("slide-rail");
|
|
797
|
-
var thumbs = document.getElementById("slide-thumbs");
|
|
798
|
-
var thumbButtons = [];
|
|
799
|
-
var params = new URLSearchParams(location.search);
|
|
800
|
-
var exportMode = params.get("export") === "1";
|
|
801
|
-
var printMode = params.get("print") === "1";
|
|
802
|
-
var thumbMode = params.get("thumb") === "1";
|
|
803
|
-
if (exportMode) document.body.classList.add("deck--export");
|
|
804
|
-
if (thumbMode) document.body.classList.add("deck--thumb");
|
|
805
|
-
|
|
806
|
-
smInlineSvgAssets();
|
|
807
|
-
|
|
808
|
-
// Connector overlay — the preview's stand-in for the native connectors
|
|
809
|
-
// the render derives from every data-connector directive (the capture
|
|
810
|
-
// engine injects an equivalent SVG before its screenshot). Straight,
|
|
811
|
-
// elbow and arc routes, side pins, arrowheads, dash and colour follow
|
|
812
|
-
// the same rules as the exporter's connector-derive; the exporter's
|
|
813
|
-
// measured output at view-draft / in the .pptx stays the truth.
|
|
814
|
-
function smDrawConnectors(container) {
|
|
815
|
-
if (!container) return;
|
|
816
|
-
var old = container.querySelector(":scope > svg[data-sm-connector-overlay]");
|
|
817
|
-
if (old) old.remove();
|
|
818
|
-
var dirs = container.querySelectorAll("[data-connector]");
|
|
819
|
-
if (!dirs.length) return;
|
|
820
|
-
var derive = window.SlidemindConnectorDerive;
|
|
821
|
-
if (!derive || typeof derive.resolveConnector !== "function") {
|
|
822
|
-
console.warn("connector overlay skipped: the bundle's capture engine exports no connector geometry (re-fetch the bundle)");
|
|
823
|
-
return;
|
|
824
|
-
}
|
|
825
|
-
var cRect = container.getBoundingClientRect();
|
|
826
|
-
var scale = cRect.width / 1280 || 1;
|
|
827
|
-
var NS = "http://www.w3.org/2000/svg";
|
|
828
|
-
var svg = document.createElementNS(NS, "svg");
|
|
829
|
-
svg.setAttribute("data-sm-connector-overlay", "1");
|
|
830
|
-
svg.setAttribute("viewBox", "0 0 1280 720");
|
|
831
|
-
var defs = document.createElementNS(NS, "defs");
|
|
832
|
-
svg.appendChild(defs);
|
|
833
|
-
var rectOf = function (el) {
|
|
834
|
-
var r = el.getBoundingClientRect();
|
|
835
|
-
return { x: (r.left - cRect.left) / scale, y: (r.top - cRect.top) / scale, w: r.width / scale, h: r.height / scale };
|
|
836
|
-
};
|
|
837
|
-
var resolveRef = function (ref) {
|
|
838
|
-
if (!ref) return null;
|
|
839
|
-
try { return container.querySelector(ref.charAt(0) === "#" ? ref : "#" + ref); } catch (e) { return null; }
|
|
840
|
-
};
|
|
841
|
-
var sideOrNull = function (raw) {
|
|
842
|
-
var s = (raw || "").toLowerCase();
|
|
843
|
-
return (s === "top" || s === "bottom" || s === "left" || s === "right") ? s : null;
|
|
844
|
-
};
|
|
845
|
-
// Computed CSS colour ("rgb(a)(...)") → { hex, alpha } as the serialiser reports it.
|
|
846
|
-
var colorOf = function (css) {
|
|
847
|
-
var m = /rgba?\(([^)]+)\)/.exec(css || "");
|
|
848
|
-
if (!m) return { hex: "000000", alpha: 1 };
|
|
849
|
-
var p = m[1].split(",").map(function (v) { return parseFloat(v); });
|
|
850
|
-
var hex = p.slice(0, 3).map(function (v) {
|
|
851
|
-
var h = Math.max(0, Math.min(255, Math.round(v))).toString(16);
|
|
852
|
-
return h.length < 2 ? "0" + h : h;
|
|
853
|
-
}).join("").toUpperCase();
|
|
854
|
-
return { hex: hex, alpha: p.length > 3 && isFinite(p[3]) ? p[3] : 1 };
|
|
855
|
-
};
|
|
856
|
-
dirs.forEach(function (el, idx) {
|
|
857
|
-
var fromEl = resolveRef(el.getAttribute("data-from"));
|
|
858
|
-
var toEl = resolveRef(el.getAttribute("data-to"));
|
|
859
|
-
var route = (el.getAttribute("data-route") || "straight").toLowerCase();
|
|
860
|
-
var centerEl = route === "arc" ? resolveRef(el.getAttribute("data-center")) : null;
|
|
861
|
-
var arrowRaw = (el.getAttribute("data-arrow") || "none").toLowerCase();
|
|
862
|
-
var cs = getComputedStyle(el);
|
|
863
|
-
var stroke = colorOf(cs.color);
|
|
864
|
-
// The same payload the serialiser hands the exporter; resolveConnector
|
|
865
|
-
// derives endpoints, route, bends, arc, and arrowhead size from it.
|
|
866
|
-
var r = derive.resolveConnector({
|
|
867
|
-
from: el.getAttribute("data-from") || "",
|
|
868
|
-
to: el.getAttribute("data-to") || "",
|
|
869
|
-
arrow: (arrowRaw === "start" || arrowRaw === "end" || arrowRaw === "both") ? arrowRaw : "none",
|
|
870
|
-
route: route,
|
|
871
|
-
fromSide: sideOrNull(el.getAttribute("data-from-side")),
|
|
872
|
-
toSide: sideOrNull(el.getAttribute("data-to-side")),
|
|
873
|
-
center: el.getAttribute("data-center") || null,
|
|
874
|
-
fromRect: fromEl ? rectOf(fromEl) : null,
|
|
875
|
-
toRect: toEl ? rectOf(toEl) : null,
|
|
876
|
-
centerRect: centerEl ? rectOf(centerEl) : null,
|
|
877
|
-
strokeHex: stroke.hex,
|
|
878
|
-
strokeAlpha: stroke.alpha,
|
|
879
|
-
strokeWidthPx: parseFloat(cs.borderTopWidth) || 0,
|
|
880
|
-
dashed: /dashed|dotted/.test(cs.borderTopStyle || ""),
|
|
881
|
-
missing: [],
|
|
882
|
-
});
|
|
883
|
-
if (!r) return;
|
|
884
|
-
var color = "#" + r.strokeHex;
|
|
885
|
-
var d;
|
|
886
|
-
if (r.arc) {
|
|
887
|
-
var large = Math.abs(r.arc.sweepDeg) > 180 ? 1 : 0;
|
|
888
|
-
var sweep = r.arc.sweepDeg > 0 ? 1 : 0;
|
|
889
|
-
d = "M" + r.start.x + " " + r.start.y + " A" + r.arc.r + " " + r.arc.r + " 0 " + large + " " + sweep + " " + r.end.x + " " + r.end.y;
|
|
890
|
-
} else {
|
|
891
|
-
d = r.points.map(function (p, i) { return (i ? "L" : "M") + p.x + " " + p.y; }).join(" ");
|
|
892
|
-
}
|
|
893
|
-
var path = document.createElementNS(NS, "path");
|
|
894
|
-
path.setAttribute("d", d);
|
|
895
|
-
path.setAttribute("fill", "none");
|
|
896
|
-
path.setAttribute("stroke", color);
|
|
897
|
-
path.setAttribute("stroke-opacity", String(r.strokeAlpha));
|
|
898
|
-
path.setAttribute("stroke-width", String(r.strokeWidthPx));
|
|
899
|
-
if (r.dashed) {
|
|
900
|
-
var dash = Math.max(4, r.strokeWidthPx * 3);
|
|
901
|
-
path.setAttribute("stroke-dasharray", dash + " " + dash);
|
|
902
|
-
}
|
|
903
|
-
if (r.arrowStart || r.arrowEnd) {
|
|
904
|
-
// Marker sized to the head PowerPoint renders (r.arrowheadPx).
|
|
905
|
-
var markerId = "sm-conn-arrow-" + idx;
|
|
906
|
-
var marker = document.createElementNS(NS, "marker");
|
|
907
|
-
marker.setAttribute("id", markerId);
|
|
908
|
-
marker.setAttribute("viewBox", "0 0 10 10");
|
|
909
|
-
marker.setAttribute("refX", "9");
|
|
910
|
-
marker.setAttribute("refY", "5");
|
|
911
|
-
marker.setAttribute("markerWidth", String(r.arrowheadPx));
|
|
912
|
-
marker.setAttribute("markerHeight", String(r.arrowheadPx));
|
|
913
|
-
marker.setAttribute("orient", "auto-start-reverse");
|
|
914
|
-
marker.setAttribute("markerUnits", "userSpaceOnUse");
|
|
915
|
-
var tri = document.createElementNS(NS, "path");
|
|
916
|
-
tri.setAttribute("d", "M0,0 L10,5 L0,10 z");
|
|
917
|
-
tri.setAttribute("fill", color);
|
|
918
|
-
tri.setAttribute("fill-opacity", String(r.strokeAlpha));
|
|
919
|
-
marker.appendChild(tri);
|
|
920
|
-
defs.appendChild(marker);
|
|
921
|
-
if (r.arrowStart) path.setAttribute("marker-start", "url(#" + markerId + ")");
|
|
922
|
-
if (r.arrowEnd) path.setAttribute("marker-end", "url(#" + markerId + ")");
|
|
923
|
-
}
|
|
924
|
-
svg.appendChild(path);
|
|
925
|
-
});
|
|
926
|
-
if (svg.childNodes.length > 1) container.appendChild(svg);
|
|
927
|
-
}
|
|
928
|
-
function smScheduleConnectors(container) {
|
|
929
|
-
requestAnimationFrame(function () { requestAnimationFrame(function () { smDrawConnectors(container); }); });
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
if (printMode) {
|
|
933
|
-
document.body.classList.add("deck--print");
|
|
934
|
-
var printDeck = document.createElement("div");
|
|
935
|
-
printDeck.className = "print-deck";
|
|
936
|
-
document.body.appendChild(printDeck);
|
|
937
|
-
slides.forEach(function (slide) {
|
|
938
|
-
var page = document.createElement("div");
|
|
939
|
-
page.className = "print-page";
|
|
940
|
-
printDeck.appendChild(page);
|
|
941
|
-
if (slide.error) {
|
|
942
|
-
var errEl = document.createElement("div");
|
|
943
|
-
errEl.className = "print-page__error";
|
|
944
|
-
errEl.textContent = slide.name + ".jsx failed:\\n" + slide.error.message;
|
|
945
|
-
page.appendChild(errEl);
|
|
946
|
-
return;
|
|
947
|
-
}
|
|
948
|
-
ReactDOM.createRoot(page).render(
|
|
949
|
-
React.createElement(slide.Component, { assetsPath: "./assets" })
|
|
950
|
-
);
|
|
951
|
-
smScheduleConnectors(page);
|
|
952
|
-
});
|
|
953
|
-
return;
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
var startIndex = parseInt(params.get("slide") || "0", 10);
|
|
957
|
-
if (isNaN(startIndex) || startIndex < 0) startIndex = 0;
|
|
958
|
-
if (startIndex >= slides.length) startIndex = slides.length - 1;
|
|
959
|
-
var current = startIndex;
|
|
960
|
-
var root = ReactDOM.createRoot(stage);
|
|
961
|
-
|
|
962
|
-
function buildThumbs() {
|
|
963
|
-
if (!thumbs || printMode || exportMode || thumbMode) return;
|
|
964
|
-
thumbs.innerHTML = "";
|
|
965
|
-
thumbButtons = [];
|
|
966
|
-
slides.forEach(function (slide, idx) {
|
|
967
|
-
var btn = document.createElement("button");
|
|
968
|
-
btn.type = "button";
|
|
969
|
-
btn.className = "deck__thumb";
|
|
970
|
-
btn.setAttribute("aria-label", "Go to slide " + (idx + 1) + ": " + slide.name);
|
|
971
|
-
|
|
972
|
-
var preview = document.createElement("div");
|
|
973
|
-
preview.className = "deck__thumb-preview";
|
|
974
|
-
var frame = document.createElement("iframe");
|
|
975
|
-
frame.className = "deck__thumb-frame";
|
|
976
|
-
frame.loading = "lazy";
|
|
977
|
-
frame.setAttribute("aria-hidden", "true");
|
|
978
|
-
frame.tabIndex = -1;
|
|
979
|
-
frame.src = location.pathname + "?slide=" + idx + "&thumb=1";
|
|
980
|
-
preview.appendChild(frame);
|
|
981
|
-
|
|
982
|
-
var meta = document.createElement("div");
|
|
983
|
-
meta.className = "deck__thumb-meta";
|
|
984
|
-
var num = document.createElement("span");
|
|
985
|
-
num.className = "deck__thumb-number";
|
|
986
|
-
num.textContent = String(idx + 1).padStart(2, "0");
|
|
987
|
-
var name = document.createElement("span");
|
|
988
|
-
name.className = "deck__thumb-name";
|
|
989
|
-
name.textContent = slide.name;
|
|
990
|
-
meta.appendChild(num);
|
|
991
|
-
meta.appendChild(name);
|
|
992
|
-
|
|
993
|
-
btn.appendChild(preview);
|
|
994
|
-
btn.appendChild(meta);
|
|
995
|
-
btn.addEventListener("click", function () {
|
|
996
|
-
setCurrent(idx);
|
|
997
|
-
stage.focus && stage.focus();
|
|
998
|
-
});
|
|
999
|
-
thumbs.appendChild(btn);
|
|
1000
|
-
thumbButtons[idx] = btn;
|
|
1001
|
-
});
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
function updateThumbs() {
|
|
1005
|
-
thumbButtons.forEach(function (btn, idx) {
|
|
1006
|
-
if (!btn) return;
|
|
1007
|
-
var active = idx === current;
|
|
1008
|
-
btn.classList.toggle("is-active", active);
|
|
1009
|
-
btn.setAttribute("aria-current", active ? "true" : "false");
|
|
1010
|
-
if (active && thumbs) {
|
|
1011
|
-
var top = btn.offsetTop;
|
|
1012
|
-
var bottom = top + btn.offsetHeight;
|
|
1013
|
-
if (top < thumbs.scrollTop) {
|
|
1014
|
-
thumbs.scrollTop = Math.max(0, top - 12);
|
|
1015
|
-
} else if (bottom > thumbs.scrollTop + thumbs.clientHeight) {
|
|
1016
|
-
thumbs.scrollTop = bottom - thumbs.clientHeight + 12;
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
});
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
function render() {
|
|
1023
|
-
var slide = slides[current];
|
|
1024
|
-
if (!slide) return;
|
|
1025
|
-
nav.textContent =
|
|
1026
|
-
(current + 1) + " / " + slides.length + " · " + slide.name;
|
|
1027
|
-
if (slide.error) {
|
|
1028
|
-
root.render(
|
|
1029
|
-
React.createElement(
|
|
1030
|
-
"div",
|
|
1031
|
-
{ className: "deck__error" },
|
|
1032
|
-
slide.name + ".jsx failed:\\n" + slide.error.message
|
|
1033
|
-
)
|
|
1034
|
-
);
|
|
1035
|
-
updateThumbs();
|
|
1036
|
-
return;
|
|
1037
|
-
}
|
|
1038
|
-
root.render(
|
|
1039
|
-
React.createElement(slide.Component, { assetsPath: "./assets" })
|
|
1040
|
-
);
|
|
1041
|
-
smScheduleConnectors(stage);
|
|
1042
|
-
updateThumbs();
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
function setCurrent(index) {
|
|
1046
|
-
current = Math.max(0, Math.min(index, slides.length - 1));
|
|
1047
|
-
render();
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
function fit() {
|
|
1051
|
-
if (exportMode) {
|
|
1052
|
-
stage.style.transform = "none";
|
|
1053
|
-
return;
|
|
1054
|
-
}
|
|
1055
|
-
var railW = rail && getComputedStyle(rail).display !== "none" ? rail.offsetWidth : 0;
|
|
1056
|
-
var sx = Math.max(1, window.innerWidth - railW) / 1280;
|
|
1057
|
-
var sy = window.innerHeight / 720;
|
|
1058
|
-
var s = Math.min(sx, sy) * (thumbMode ? 1 : 0.95);
|
|
1059
|
-
stage.style.transform = "scale(" + s + ")";
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
window.addEventListener("resize", fit);
|
|
1063
|
-
buildThumbs();
|
|
1064
|
-
fit();
|
|
1065
|
-
render();
|
|
1066
|
-
|
|
1067
|
-
document.addEventListener("keydown", function (e) {
|
|
1068
|
-
var k = e.key;
|
|
1069
|
-
if (k === "ArrowRight" || k === "PageDown" || k === " ") {
|
|
1070
|
-
setCurrent(current + 1);
|
|
1071
|
-
e.preventDefault();
|
|
1072
|
-
} else if (k === "ArrowLeft" || k === "PageUp") {
|
|
1073
|
-
setCurrent(current - 1);
|
|
1074
|
-
e.preventDefault();
|
|
1075
|
-
} else if (k === "Home") {
|
|
1076
|
-
setCurrent(0);
|
|
1077
|
-
} else if (k === "End") {
|
|
1078
|
-
setCurrent(slides.length - 1);
|
|
1079
|
-
} else if (k === "f" || k === "F") {
|
|
1080
|
-
if (document.fullscreenElement) document.exitFullscreen();
|
|
1081
|
-
else document.documentElement.requestFullscreen();
|
|
1082
|
-
}
|
|
1083
|
-
});
|
|
1084
|
-
})();
|
|
1085
|
-
</script>
|
|
1086
|
-
</body>
|
|
1087
|
-
</html>
|
|
1088
|
-
`;
|
|
289
|
+
// The preview page. Kept as a real .html file (syntax-highlighted, diffable)
|
|
290
|
+
// rather than an 800-line template string; the __PLACEHOLDER__ slots are
|
|
291
|
+
// filled by buildDeck below. Function replacer: a string replacement would
|
|
292
|
+
// expand `$&`-style patterns in the substituted value.
|
|
293
|
+
const HTML_TEMPLATE = fs
|
|
294
|
+
.readFileSync(path.join(__dirname, "preview.html"), "utf8")
|
|
295
|
+
.replace("__FAVICON_LINK__", () => FAVICON_LINK);
|
|
1089
296
|
|
|
1090
297
|
export async function buildDeck(deckDir, opts = {}) {
|
|
1091
298
|
const skill = resolveSkillForDeck(deckDir);
|
|
@@ -1316,8 +523,8 @@ function parseArgs(argv) {
|
|
|
1316
523
|
args.fix = true;
|
|
1317
524
|
} else if (a === "-h" || a === "--help") {
|
|
1318
525
|
process.stdout.write(
|
|
1319
|
-
"Usage:
|
|
1320
|
-
"
|
|
526
|
+
"Usage: node src/builders/build.mjs --deck <deck> [--open] [--fix]\n" +
|
|
527
|
+
" (the CLI entry point is `slidemind view-draft <deck> [--fix]`)\n" +
|
|
1321
528
|
" --fix apply the deterministic lint autofixes to slides/*.jsx before building\n" +
|
|
1322
529
|
" (pin inline-svg px size from its viewBox; letter-spacing em→px where the\n" +
|
|
1323
530
|
" block's px font-size makes the conversion exact)\n",
|