@bendyline/squisq 2.4.4 → 2.6.0

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.
@@ -23,7 +23,7 @@ import {
23
23
  templateRegistry,
24
24
  writeCustomTemplatesToFrontmatter,
25
25
  writeCustomThemesToFrontmatter
26
- } from "./chunk-OIIIHI3Y.js";
26
+ } from "./chunk-GSJEGMKF.js";
27
27
  import {
28
28
  ASCII_TREE_VOCAB,
29
29
  ASCII_VOCAB,
@@ -58,7 +58,7 @@ import {
58
58
  } from "./chunk-BAOV476U.js";
59
59
  import {
60
60
  parseMarkdown
61
- } from "./chunk-YQW6AQBV.js";
61
+ } from "./chunk-6MVWWZHL.js";
62
62
  import {
63
63
  KNOWN_BLOCK_META_KEYS,
64
64
  TEMPLATE_ALIASES,
@@ -66,7 +66,7 @@ import {
66
66
  isContainerTemplate,
67
67
  resolveTemplateName,
68
68
  serializeAnnotation
69
- } from "./chunk-O3LLD7HG.js";
69
+ } from "./chunk-NBKAXPSX.js";
70
70
  import {
71
71
  extractPlainText,
72
72
  getChildren,
@@ -334,6 +334,112 @@ function cssFilterForTreatment(treatment, blur) {
334
334
  return parts.length > 0 ? parts.join(" ") : void 0;
335
335
  }
336
336
 
337
+ // src/doc/coverSlideSettings.ts
338
+ var COVER_SLIDE_TEMPLATE_OPTIONS = Object.freeze([
339
+ {
340
+ id: "cover",
341
+ label: "Hero cover",
342
+ description: "The classic Squisq cover with title, subtitle, and optional hero image."
343
+ },
344
+ {
345
+ id: "title",
346
+ label: "Title",
347
+ description: "A theme-led title card without a full-bleed image."
348
+ },
349
+ {
350
+ id: "sectionHeader",
351
+ label: "Section header",
352
+ description: "A bold section divider using the hero image when one is available."
353
+ },
354
+ {
355
+ id: "imageWithCaption",
356
+ label: "Image with title",
357
+ description: "A full-bleed image with the title and subtitle overlaid.",
358
+ requiresHeroImage: true
359
+ },
360
+ {
361
+ id: "bigText",
362
+ label: "Big text",
363
+ description: "The title in gigantic uppercase type on a clean theme surface \u2014 thumbnail-ready."
364
+ },
365
+ {
366
+ id: "bigTextImage",
367
+ label: "Big text on image",
368
+ description: "The title in gigantic uppercase type over the hero image, with a contrast bloom behind the text.",
369
+ requiresHeroImage: true
370
+ }
371
+ ]);
372
+ var COVER_SLIDE_FRONTMATTER_KEYS = Object.freeze({
373
+ enabled: { canonical: "squisq-cover-slide", legacy: "cover-slide" },
374
+ template: { canonical: "squisq-cover-template", legacy: "cover-template" },
375
+ duration: { canonical: "squisq-cover-duration", legacy: "cover-duration" },
376
+ playback: { canonical: "squisq-cover-playback", legacy: "cover-playback" }
377
+ });
378
+ var DEFAULT_COVER_SLIDE_SETTINGS = Object.freeze({
379
+ enabled: true,
380
+ template: "cover",
381
+ duration: 2,
382
+ playback: "preroll"
383
+ });
384
+ var MAX_COVER_SLIDE_DURATION_SECONDS = 60;
385
+ function readSetting(frontmatter, keys) {
386
+ if (!frontmatter) return void 0;
387
+ return Object.prototype.hasOwnProperty.call(frontmatter, keys.canonical) ? frontmatter[keys.canonical] : frontmatter[keys.legacy];
388
+ }
389
+ function resolveBoolean(value) {
390
+ if (typeof value === "boolean") return value;
391
+ if (typeof value !== "string") return void 0;
392
+ const normalized = value.trim().toLowerCase();
393
+ if (["true", "yes", "on", "show", "visible"].includes(normalized)) return true;
394
+ if (["false", "no", "off", "hide", "hidden"].includes(normalized)) return false;
395
+ return void 0;
396
+ }
397
+ function resolveTemplate(value) {
398
+ if (typeof value !== "string") return void 0;
399
+ const normalized = value.trim().toLowerCase().replace(/[_\s-]+/g, "");
400
+ if (normalized === "cover" || normalized === "hero" || normalized === "managedcover") {
401
+ return "cover";
402
+ }
403
+ if (normalized === "title" || normalized === "titleblock") return "title";
404
+ if (normalized === "sectionheader" || normalized === "section") return "sectionHeader";
405
+ if (normalized === "imagewithcaption" || normalized === "imagetitle" || normalized === "fullbleedimage") {
406
+ return "imageWithCaption";
407
+ }
408
+ if (normalized === "bigtextimage" || normalized === "largetextimage" || normalized === "bigtextonimage") {
409
+ return "bigTextImage";
410
+ }
411
+ if (normalized === "bigtext" || normalized === "largetext") return "bigText";
412
+ return void 0;
413
+ }
414
+ function resolveDuration(value) {
415
+ const duration = typeof value === "number" ? value : typeof value === "string" && value.trim().length > 0 ? Number(value) : Number.NaN;
416
+ if (!Number.isFinite(duration) || duration < 0 || duration > MAX_COVER_SLIDE_DURATION_SECONDS) {
417
+ return void 0;
418
+ }
419
+ return duration;
420
+ }
421
+ function resolvePlayback(value) {
422
+ if (typeof value !== "string") return void 0;
423
+ const normalized = value.trim().toLowerCase().replace(/[_\s]+/g, "-");
424
+ if (["overlay", "over", "concurrent", "play-over"].includes(normalized)) return "overlay";
425
+ if (["preroll", "pre-roll", "delay", "push", "shift"].includes(normalized)) return "preroll";
426
+ return void 0;
427
+ }
428
+ function resolveCoverSlideSettings(frontmatter, overrides = {}) {
429
+ const resolved = {
430
+ enabled: resolveBoolean(readSetting(frontmatter, COVER_SLIDE_FRONTMATTER_KEYS.enabled)) ?? DEFAULT_COVER_SLIDE_SETTINGS.enabled,
431
+ template: resolveTemplate(readSetting(frontmatter, COVER_SLIDE_FRONTMATTER_KEYS.template)) ?? DEFAULT_COVER_SLIDE_SETTINGS.template,
432
+ duration: resolveDuration(readSetting(frontmatter, COVER_SLIDE_FRONTMATTER_KEYS.duration)) ?? DEFAULT_COVER_SLIDE_SETTINGS.duration,
433
+ playback: resolvePlayback(readSetting(frontmatter, COVER_SLIDE_FRONTMATTER_KEYS.playback)) ?? DEFAULT_COVER_SLIDE_SETTINGS.playback
434
+ };
435
+ return {
436
+ enabled: overrides.enabled ?? resolved.enabled,
437
+ template: overrides.template ?? resolved.template,
438
+ duration: resolveDuration(overrides.duration) ?? resolved.duration,
439
+ playback: overrides.playback ?? resolved.playback
440
+ };
441
+ }
442
+
337
443
  // src/doc/docToMarkdown.ts
338
444
  var TRANSITION_PARAM_KEYS = ["transition", "transitionDuration", "transitionDirection"];
339
445
  var DEFAULT_TEMPLATE = "sectionHeader";
@@ -681,6 +787,17 @@ var sectionHeader = (input) => {
681
787
  mediaBackground: !!s.imageSrc
682
788
  };
683
789
  };
790
+ var bigText = (input) => {
791
+ const b = input;
792
+ const media = b.imageSrc ? { type: "image", src: b.imageSrc, alt: b.imageAlt ?? "" } : void 0;
793
+ return {
794
+ kind: "hero",
795
+ variant: media ? "media" : "title",
796
+ slots: { title: b.title, media },
797
+ emphasis: "lead",
798
+ mediaBackground: !!media
799
+ };
800
+ };
684
801
  var content = (input) => {
685
802
  const c = input;
686
803
  return {
@@ -965,6 +1082,7 @@ var layout = (input, ctx) => canvasDraft("layout", ctx, input);
965
1082
  var sectionExtractors = {
966
1083
  title,
967
1084
  sectionHeader,
1085
+ bigText,
968
1086
  content,
969
1087
  statHighlight,
970
1088
  quote,
@@ -1456,6 +1574,10 @@ var PAGE_BASE_CSS = `
1456
1574
  }
1457
1575
  .squisq-page *, .squisq-page *::before, .squisq-page *::after { box-sizing: border-box; }
1458
1576
  .squisq-page img { max-width: 100%; height: auto; }
1577
+ .squisq-page a {
1578
+ color: var(--squisq-page-primary);
1579
+ text-decoration-color: color-mix(in srgb, var(--squisq-page-primary) 40%, transparent);
1580
+ }
1459
1581
 
1460
1582
  /* Page pattern washes (very low intensity) */
1461
1583
  .squisq-page[data-pattern='dots'] {
@@ -1875,11 +1997,34 @@ var PAGE_BASE_CSS = `
1875
1997
  .squisq-page-prose p { margin: 0; }
1876
1998
  .squisq-page-prose p + p { margin-top: 1.25em; }
1877
1999
  .squisq-page-prose > * + * { margin-top: 1.1em; }
1878
- .squisq-page-prose a { color: var(--squisq-page-primary); text-decoration-color: color-mix(in srgb, var(--squisq-page-primary) 40%, transparent); }
1879
2000
  .squisq-page-prose code { font-family: var(--squisq-page-mono-font); font-size: 0.9em; }
1880
2001
  .squisq-page-prose :not(pre) > code { background: color-mix(in srgb, var(--squisq-page-text) 8%, transparent); border-radius: 4px; padding: 0.12em 0.35em; }
1881
2002
  .squisq-page-prose pre { background: color-mix(in srgb, var(--squisq-page-text) 6%, transparent); border-radius: var(--squisq-page-radius); padding: 1em 1.25em; overflow-x: auto; }
1882
2003
  .squisq-page-prose pre code { background: none; border-radius: 0; padding: 0; }
2004
+ .squisq-page .squisq-md-code-frame { position: relative; margin: 1em 0; }
2005
+ .squisq-page .squisq-md-code-frame > pre { margin: 0; padding-right: 5rem; }
2006
+ .squisq-page .squisq-md-code-copy {
2007
+ position: absolute;
2008
+ z-index: 1;
2009
+ top: 0.55rem;
2010
+ right: 0.55rem;
2011
+ min-width: 3.7rem;
2012
+ padding: 0.28rem 0.5rem;
2013
+ border: 1px solid color-mix(in srgb, var(--squisq-page-text) 20%, transparent);
2014
+ border-radius: 5px;
2015
+ background: color-mix(in srgb, var(--squisq-page-bg) 90%, transparent);
2016
+ color: inherit;
2017
+ font: 500 0.72rem/1.2 system-ui, sans-serif;
2018
+ cursor: pointer;
2019
+ opacity: 0.58;
2020
+ transition: opacity 120ms ease, background-color 120ms ease;
2021
+ }
2022
+ .squisq-page .squisq-md-code-frame:hover > .squisq-md-code-copy,
2023
+ .squisq-page .squisq-md-code-copy:focus-visible,
2024
+ .squisq-page .squisq-md-code-copy[data-copy-state='copied'],
2025
+ .squisq-page .squisq-md-code-copy[data-copy-state='failed'] { opacity: 1; }
2026
+ .squisq-page .squisq-md-code-copy:hover { background: var(--squisq-page-bg); }
2027
+ .squisq-page .squisq-md-code-copy:disabled { cursor: wait; }
1883
2028
  .squisq-page-prose blockquote { margin: 0; border-left: 4px solid var(--squisq-page-accent); padding-left: 1.25em; color: var(--squisq-page-text-muted); }
1884
2029
  .squisq-page-prose ul, .squisq-page-prose ol { padding-left: 1.5em; margin: 0; }
1885
2030
  .squisq-page-prose li + li { margin-top: 0.4em; }
@@ -2025,24 +2170,24 @@ function retimeBlocks(blocks, ctx) {
2025
2170
  const range = ctx.ranges.get(block.id);
2026
2171
  const pinned = getPinnedBlockMeta(block);
2027
2172
  const next = { ...block };
2028
- if (range && pinned.duration == null && pinned.startTime == null) {
2029
- next.startTime = ctx.clipStart + range.startSec;
2030
- next.duration = Math.max(0, range.endSec - range.startSec);
2031
- ctx.cursor = Math.max(ctx.cursor, next.startTime + next.duration);
2032
- } else if (range) {
2033
- const pinnedStart = pinned.startTime ?? block.startTime;
2034
- const pinnedDuration = pinned.duration ?? block.duration;
2035
- const narrStart = ctx.clipStart + range.startSec;
2173
+ if (range) {
2174
+ const narrStart = ctx.clipStart + range.startSec + ctx.shift;
2036
2175
  const narrDuration = Math.max(0, range.endSec - range.startSec);
2037
- if (Math.abs(pinnedStart - narrStart) > PIN_CONFLICT_TOLERANCE_SEC || Math.abs(pinnedDuration - narrDuration) > PIN_CONFLICT_TOLERANCE_SEC) {
2176
+ next.startTime = pinned.startTime ?? narrStart;
2177
+ next.duration = pinned.duration ?? narrDuration;
2178
+ const end = next.startTime + next.duration;
2179
+ ctx.shift = end - (ctx.clipStart + range.endSec);
2180
+ const durationConflict = pinned.duration != null && Math.abs(pinned.duration - narrDuration) > PIN_CONFLICT_TOLERANCE_SEC;
2181
+ const startConflict = pinned.startTime != null && Math.abs(pinned.startTime - narrStart) > PIN_CONFLICT_TOLERANCE_SEC;
2182
+ if (durationConflict || startConflict) {
2038
2183
  ctx.diagnostics.push({
2039
2184
  severity: "info",
2040
2185
  code: "narration-pin-conflict",
2041
- message: `Block timing is pinned (duration=/startTime=) but the recorded narration says ~${narrDuration.toFixed(1)}s starting at ~${narrStart.toFixed(1)}s. The pin wins; remove it to follow the narration.`,
2186
+ message: `Block timing is pinned (duration=/startTime=) but the recorded narration says ~${narrDuration.toFixed(1)}s starting at ~${narrStart.toFixed(1)}s. The pin wins and later blocks follow it, while the recorded voice keeps its own schedule \u2014 playback drifts from the take past this block. Remove the pin to follow the narration.`,
2042
2187
  blockId: block.id
2043
2188
  });
2044
2189
  }
2045
- ctx.cursor = Math.max(ctx.cursor, pinnedStart + pinnedDuration);
2190
+ ctx.cursor = Math.max(ctx.cursor, end);
2046
2191
  } else {
2047
2192
  next.startTime = ctx.cursor;
2048
2193
  ctx.cursor += next.duration;
@@ -2068,7 +2213,8 @@ async function applyNarrationTiming(doc, container) {
2068
2213
  clipStart: clip.startAt,
2069
2214
  ranges,
2070
2215
  diagnostics: [],
2071
- cursor: 0
2216
+ cursor: 0,
2217
+ shift: 0
2072
2218
  };
2073
2219
  const blocks = retimeBlocks(doc.blocks, ctx);
2074
2220
  const duration = Math.max(clip.startAt + timing.duration, ctx.cursor);
@@ -3865,6 +4011,11 @@ export {
3865
4011
  getTransitionClass,
3866
4012
  getAnimationProgress,
3867
4013
  cssFilterForTreatment,
4014
+ COVER_SLIDE_TEMPLATE_OPTIONS,
4015
+ COVER_SLIDE_FRONTMATTER_KEYS,
4016
+ DEFAULT_COVER_SLIDE_SETTINGS,
4017
+ MAX_COVER_SLIDE_DURATION_SECONDS,
4018
+ resolveCoverSlideSettings,
3868
4019
  docToMarkdown,
3869
4020
  resolveThemeForDoc,
3870
4021
  isTemplatedPageBlock,
@@ -34,6 +34,7 @@ function isContainerTemplate(name) {
34
34
  var TEMPLATE_TOKEN_NAMES = /* @__PURE__ */ new Set([
35
35
  "title",
36
36
  "sectionHeader",
37
+ "bigText",
37
38
  "content",
38
39
  "statHighlight",
39
40
  "quote",