@bendyline/squisq-formats 2.6.15 → 2.6.17

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.
@@ -0,0 +1,15 @@
1
+ // src/pdf/upng.ts
2
+ import UPNG from "@pdf-lib/upng";
3
+ function isUpngEncoder(value) {
4
+ return typeof value === "object" && value !== null && "encode" in value && typeof value.encode === "function";
5
+ }
6
+ function resolveUpngEncoder(value) {
7
+ if (isUpngEncoder(value)) return value;
8
+ if (typeof value !== "object" || value === null || !("default" in value)) return null;
9
+ return isUpngEncoder(value.default) ? value.default : null;
10
+ }
11
+ var upngEncoder = resolveUpngEncoder(UPNG);
12
+
13
+ export {
14
+ upngEncoder
15
+ };
@@ -1,10 +1,14 @@
1
1
  import {
2
2
  convert,
3
3
  defaultRegistry
4
- } from "./chunk-VE2ADQ5T.js";
4
+ } from "./chunk-SZDCCL6O.js";
5
5
  import {
6
6
  ConversionError
7
7
  } from "./chunk-KXOZMWBS.js";
8
+ import {
9
+ OUTSIDE_IN_FORMAT_IDS,
10
+ resolveOutsideInLayout
11
+ } from "./chunk-C23JYVPG.js";
8
12
 
9
13
  // src/outside-in/index.ts
10
14
  import {
@@ -14,91 +18,11 @@ import {
14
18
  stringifyMarkdown
15
19
  } from "@bendyline/squisq/markdown";
16
20
  import { MemoryContentContainer } from "@bendyline/squisq/storage";
17
- var OUTSIDE_IN_FORMAT_IDS = ["html", "docx", "pdf", "pptx", "xlsx", "csv"];
18
21
  var OUTSIDE_IN_FORMAT_SET = new Set(OUTSIDE_IN_FORMAT_IDS);
19
22
  var OUTSIDE_IN_VERSION_KEY = "squisq-outside-in";
20
23
  var OUTSIDE_IN_OUTPUT_KEY = "squisq-output";
21
24
  var OUTSIDE_IN_FORMAT_KEY = "squisq-output-format";
22
25
  var OUTSIDE_IN_UPDATE_FROM_MARKDOWN_KEY = "squisq-updatefrommarkdown";
23
- function normalizePath(path) {
24
- const slash = path.replace(/\\/g, "/");
25
- const leading = slash.startsWith("/") ? "/" : "";
26
- const parts = slash.split("/").filter((part) => part !== "");
27
- if (parts.some((part) => part === "." || part === "..")) {
28
- throw new Error(`Outside-in paths must be canonical workspace paths: ${path}`);
29
- }
30
- return leading + parts.join("/");
31
- }
32
- function joinPath(parent, child) {
33
- if (!parent || parent === "/") return parent === "/" ? `/${child}` : child;
34
- return `${parent}/${child}`;
35
- }
36
- function dirname(path) {
37
- const slash = path.lastIndexOf("/");
38
- if (slash < 0) return "";
39
- if (slash === 0) return "/";
40
- return path.slice(0, slash);
41
- }
42
- function basename(path) {
43
- return path.slice(path.lastIndexOf("/") + 1);
44
- }
45
- function slugStem(stem) {
46
- const slug = stem.normalize("NFKD").replace(/\p{Mark}+/gu, "").toLocaleLowerCase("en-US").replace(/[^\p{Letter}\p{Number}]+/gu, "-").replace(/^-+|-+$/g, "");
47
- return slug || "document";
48
- }
49
- function formatFromExtension(extension) {
50
- const normalized = extension.toLowerCase();
51
- if (normalized === "htm") return "html";
52
- return OUTSIDE_IN_FORMAT_SET.has(normalized) ? normalized : null;
53
- }
54
- function resolveOutsideInLayout(targetPath) {
55
- const normalized = normalizePath(targetPath);
56
- const filename = basename(normalized);
57
- const dot = filename.lastIndexOf(".");
58
- if (dot <= 0 || dot === filename.length - 1) return null;
59
- const format = formatFromExtension(filename.slice(dot + 1));
60
- if (!format) return null;
61
- const stem = filename.slice(0, dot);
62
- const parentDirectory = dirname(normalized);
63
- const companionName = `${stem}_files`;
64
- const companionDirectory = joinPath(parentDirectory, companionName);
65
- const markdownFilename = `${slugStem(stem)}.md`;
66
- const backupDirectory = joinPath(companionDirectory, ".original");
67
- const backupFilename = `original.${format}`;
68
- return {
69
- targetPath: normalized,
70
- format,
71
- parentDirectory,
72
- stem,
73
- companionName,
74
- companionDirectory,
75
- markdownFilename,
76
- markdownPath: joinPath(companionDirectory, markdownFilename),
77
- relativeTargetPath: `../${filename}`,
78
- backupDirectory,
79
- backupFilename,
80
- backupPath: joinPath(backupDirectory, backupFilename)
81
- };
82
- }
83
- function isOutsideInTargetPath(path) {
84
- return resolveOutsideInLayout(path) !== null;
85
- }
86
- function chooseOutsideInMarkdownPath(layout, paths) {
87
- const canonical = normalizePath(layout.markdownPath);
88
- const normalized = paths.map(normalizePath);
89
- const exact = normalized.find((path) => path === canonical);
90
- if (exact) return exact;
91
- const canonicalFolded = canonical.toLocaleLowerCase("en-US");
92
- const folded = normalized.find((path) => path.toLocaleLowerCase("en-US") === canonicalFolded);
93
- if (folded) return folded;
94
- const prefix = `${normalizePath(layout.companionDirectory).replace(/\/$/, "")}/`;
95
- const markdown = normalized.filter((path) => {
96
- if (!path.toLocaleLowerCase("en-US").endsWith(".md")) return false;
97
- if (!path.startsWith(prefix)) return false;
98
- return !path.slice(prefix.length).includes("/");
99
- });
100
- return markdown.length === 1 ? markdown[0] : null;
101
- }
102
26
  function rawFrontmatter(source) {
103
27
  const block = splitFrontmatterBlock(source).frontmatter;
104
28
  if (!block) return null;
@@ -277,11 +201,7 @@ async function renderOutsideInDocument(source, options = {}) {
277
201
  }
278
202
 
279
203
  export {
280
- OUTSIDE_IN_FORMAT_IDS,
281
204
  OUTSIDE_IN_UPDATE_FROM_MARKDOWN_KEY,
282
- resolveOutsideInLayout,
283
- isOutsideInTargetPath,
284
- chooseOutsideInMarkdownPath,
285
205
  readOutsideInMetadata,
286
206
  withOutsideInMetadata,
287
207
  isOutsideInMarkdownEditingEnabled,
@@ -74,7 +74,7 @@ import {
74
74
  resolveThemeForDoc
75
75
  } from "@bendyline/squisq/doc";
76
76
  import { splitIconMarkers } from "@bendyline/squisq/icon-marker";
77
- import { readFrontmatterThemeId } from "@bendyline/squisq/markdown";
77
+ import { parseVideoEmbedUrl, readFrontmatterThemeId } from "@bendyline/squisq/markdown";
78
78
 
79
79
  // src/pptx/styles.ts
80
80
  var SLIDE_WIDTH = 12192e3;
@@ -202,6 +202,9 @@ async function docToPptx(doc, options = {}) {
202
202
  if (slideBlocks.length === 0) {
203
203
  slideBlocks.push({ id: "empty-slide", startTime: 0, duration: 0, audioSegment: 0, layers: [] });
204
204
  }
205
+ const videoPosters = slideBlocks.some(
206
+ (block) => block.layers?.some((layer) => layer.type === "videoEmbed")
207
+ ) ? await import("./videoPoster-D7DTZC2L.js") : null;
205
208
  const slideXmls = [];
206
209
  const slideContexts = [];
207
210
  for (let index = 0; index < slideBlocks.length; index++) {
@@ -214,6 +217,7 @@ async function docToPptx(doc, options = {}) {
214
217
  effectiveOptions.signal
215
218
  );
216
219
  ctx.diagramImages = effectiveOptions.diagramImages;
220
+ ctx.videoPosters = videoPosters;
217
221
  slideXmls.push(buildLayerSlideXml(slideBlocks[index], ctx));
218
222
  slideContexts.push(ctx);
219
223
  }
@@ -315,6 +319,8 @@ var SlideContext = class {
315
319
  */
316
320
  this.relIds = new RelIdAllocator();
317
321
  this.relationships = [];
322
+ /** Poster helpers for hosted videos, loaded only when the deck has one. */
323
+ this.videoPosters = null;
318
324
  this.embeddedImages = [];
319
325
  this.nextShapeId = 4;
320
326
  /** Font Awesome families referenced by semantic or materialized text runs. */
@@ -553,6 +559,44 @@ function convertLayerToShapes(layer, ctx, textBottom) {
553
559
  )
554
560
  ];
555
561
  }
562
+ case "videoEmbed": {
563
+ const embed = parseVideoEmbedUrl(layer.content.url);
564
+ const thumbnail = embed?.thumbnailUrl ? ctx.images?.get(embed.thumbnailUrl) : void 0;
565
+ const poster = embed && (thumbnail ?? ctx.videoPosters?.videoPosterPng(embed.aspectRatio < 1)?.data);
566
+ if (embed && poster) {
567
+ const label = layer.content.title || `${embed.providerName} video`;
568
+ const relId = ctx.addImage(
569
+ thumbnail ? embed.thumbnailUrl ?? "poster.jpg" : "video-poster.png",
570
+ poster,
571
+ label
572
+ );
573
+ const embedded = ctx.embeddedImages.find((candidate) => candidate.relId === relId);
574
+ const area = toEmuRect(resolveLayerRect(layer.position, 1920, 1080));
575
+ const width = Math.min(area.width, Math.round(area.height * embed.aspectRatio));
576
+ const height = Math.round(width / embed.aspectRatio);
577
+ return [
578
+ buildImageShape(
579
+ embedded,
580
+ ctx.allocShapeId(),
581
+ area.x + Math.round((area.width - width) / 2),
582
+ area.y + Math.round((area.height - height) / 2),
583
+ width,
584
+ height,
585
+ ctx.addHyperlink(embed.watchUrl)
586
+ )
587
+ ];
588
+ }
589
+ return [
590
+ buildLayerTextShape(
591
+ textLayerForPlaceholder(
592
+ layer.position,
593
+ `[Video: ${layer.content.title || layer.content.url}]`,
594
+ ctx
595
+ ),
596
+ ctx.allocShapeId()
597
+ )
598
+ ];
599
+ }
556
600
  case "mermaid": {
557
601
  const picture = ctx.diagramImages?.get(layer.content.source.trim());
558
602
  if (picture && picture.width > 0 && picture.height > 0) {
@@ -1113,8 +1157,11 @@ function buildBodyShapeCustom(bodyContent, top, height, style) {
1113
1157
  const defRPr = style.hasTheme ? `<a:defRPr><a:solidFill><a:srgbClr val="${style.text}"/></a:solidFill><a:latin typeface="${escapeXml(style.bodyFont)}"/></a:defRPr>` : "";
1114
1158
  return `<p:sp><p:nvSpPr><p:cNvPr id="3" name="Content"/><p:cNvSpPr><a:spLocks noGrp="1"/></p:cNvSpPr><p:nvPr><p:ph idx="1"/></p:nvPr></p:nvSpPr><p:spPr><a:xfrm><a:off x="${BODY_LEFT}" y="${top}"/><a:ext cx="${BODY_WIDTH}" cy="${height}"/></a:xfrm></p:spPr><p:txBody><a:bodyPr/><a:lstStyle><a:lvl1pPr>${defRPr}</a:lvl1pPr></a:lstStyle>` + bodyContent + `</p:txBody></p:sp>`;
1115
1159
  }
1116
- function buildImageShape(img, shapeId, left, top, maxWidth, maxHeight) {
1117
- return `<p:pic><p:nvPicPr><p:cNvPr id="${shapeId}" name="${escapeXml(img.alt || "Picture")}"${img.alt ? ` descr="${escapeXml(img.alt)}"` : ""}/><p:cNvPicPr><a:picLocks noChangeAspect="1"/></p:cNvPicPr><p:nvPr/></p:nvPicPr><p:blipFill><a:blip r:embed="${img.relId}"/><a:stretch><a:fillRect/></a:stretch></p:blipFill><p:spPr><a:xfrm><a:off x="${left}" y="${top}"/><a:ext cx="${maxWidth}" cy="${maxHeight}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></p:spPr></p:pic>`;
1160
+ function buildImageShape(img, shapeId, left, top, maxWidth, maxHeight, hyperlinkRelId) {
1161
+ const name = escapeXml(img.alt || "Picture");
1162
+ const descr = img.alt ? ` descr="${escapeXml(img.alt)}"` : "";
1163
+ const link = hyperlinkRelId ? `<a:hlinkClick r:id="${hyperlinkRelId}"/>` : "";
1164
+ return `<p:pic><p:nvPicPr>` + (link ? `<p:cNvPr id="${shapeId}" name="${name}"${descr}>${link}</p:cNvPr>` : `<p:cNvPr id="${shapeId}" name="${name}"${descr}/>`) + `<p:cNvPicPr><a:picLocks noChangeAspect="1"/></p:cNvPicPr><p:nvPr/></p:nvPicPr><p:blipFill><a:blip r:embed="${img.relId}"/><a:stretch><a:fillRect/></a:stretch></p:blipFill><p:spPr><a:xfrm><a:off x="${left}" y="${top}"/><a:ext cx="${maxWidth}" cy="${maxHeight}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></p:spPr></p:pic>`;
1118
1165
  }
1119
1166
  function convertBodyBlocks(nodes, ctx) {
1120
1167
  const parts = [];
@@ -0,0 +1,89 @@
1
+ // src/outside-in/layout.ts
2
+ var OUTSIDE_IN_FORMAT_IDS = ["html", "docx", "pdf", "pptx", "xlsx", "csv"];
3
+ var OUTSIDE_IN_FORMAT_SET = new Set(OUTSIDE_IN_FORMAT_IDS);
4
+ function normalizePath(path) {
5
+ const slash = path.replace(/\\/g, "/");
6
+ const leading = slash.startsWith("/") ? "/" : "";
7
+ const parts = slash.split("/").filter((part) => part !== "");
8
+ if (parts.some((part) => part === "." || part === "..")) {
9
+ throw new Error(`Outside-in paths must be canonical workspace paths: ${path}`);
10
+ }
11
+ return leading + parts.join("/");
12
+ }
13
+ function joinPath(parent, child) {
14
+ if (!parent || parent === "/") return parent === "/" ? `/${child}` : child;
15
+ return `${parent}/${child}`;
16
+ }
17
+ function dirname(path) {
18
+ const slash = path.lastIndexOf("/");
19
+ if (slash < 0) return "";
20
+ if (slash === 0) return "/";
21
+ return path.slice(0, slash);
22
+ }
23
+ function basename(path) {
24
+ return path.slice(path.lastIndexOf("/") + 1);
25
+ }
26
+ function slugStem(stem) {
27
+ const slug = stem.normalize("NFKD").replace(/\p{Mark}+/gu, "").toLocaleLowerCase("en-US").replace(/[^\p{Letter}\p{Number}]+/gu, "-").replace(/^-+|-+$/g, "");
28
+ return slug || "document";
29
+ }
30
+ function formatFromExtension(extension) {
31
+ const normalized = extension.toLowerCase();
32
+ if (normalized === "htm") return "html";
33
+ return OUTSIDE_IN_FORMAT_SET.has(normalized) ? normalized : null;
34
+ }
35
+ function resolveOutsideInLayout(targetPath) {
36
+ const normalized = normalizePath(targetPath);
37
+ const filename = basename(normalized);
38
+ const dot = filename.lastIndexOf(".");
39
+ if (dot <= 0 || dot === filename.length - 1) return null;
40
+ const format = formatFromExtension(filename.slice(dot + 1));
41
+ if (!format) return null;
42
+ const stem = filename.slice(0, dot);
43
+ const parentDirectory = dirname(normalized);
44
+ const companionName = `${stem}_files`;
45
+ const companionDirectory = joinPath(parentDirectory, companionName);
46
+ const markdownFilename = `${slugStem(stem)}.md`;
47
+ const backupDirectory = joinPath(companionDirectory, ".original");
48
+ const backupFilename = `original.${format}`;
49
+ return {
50
+ targetPath: normalized,
51
+ format,
52
+ parentDirectory,
53
+ stem,
54
+ companionName,
55
+ companionDirectory,
56
+ markdownFilename,
57
+ markdownPath: joinPath(companionDirectory, markdownFilename),
58
+ relativeTargetPath: `../${filename}`,
59
+ backupDirectory,
60
+ backupFilename,
61
+ backupPath: joinPath(backupDirectory, backupFilename)
62
+ };
63
+ }
64
+ function isOutsideInTargetPath(path) {
65
+ return resolveOutsideInLayout(path) !== null;
66
+ }
67
+ function chooseOutsideInMarkdownPath(layout, paths) {
68
+ const canonical = normalizePath(layout.markdownPath);
69
+ const normalized = paths.map(normalizePath);
70
+ const exact = normalized.find((path) => path === canonical);
71
+ if (exact) return exact;
72
+ const canonicalFolded = canonical.toLocaleLowerCase("en-US");
73
+ const folded = normalized.find((path) => path.toLocaleLowerCase("en-US") === canonicalFolded);
74
+ if (folded) return folded;
75
+ const prefix = `${normalizePath(layout.companionDirectory).replace(/\/$/, "")}/`;
76
+ const markdown = normalized.filter((path) => {
77
+ if (!path.toLocaleLowerCase("en-US").endsWith(".md")) return false;
78
+ if (!path.startsWith(prefix)) return false;
79
+ return !path.slice(prefix.length).includes("/");
80
+ });
81
+ return markdown.length === 1 ? markdown[0] : null;
82
+ }
83
+
84
+ export {
85
+ OUTSIDE_IN_FORMAT_IDS,
86
+ resolveOutsideInLayout,
87
+ isOutsideInTargetPath,
88
+ chooseOutsideInMarkdownPath
89
+ };