@bendyline/squisq-formats 2.6.16 → 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.
@@ -68,7 +68,70 @@ import {
68
68
  // src/docx/export.ts
69
69
  import { resolveFontFamily } from "@bendyline/squisq/schemas";
70
70
  import { docToMarkdown, resolveThemeForDoc } from "@bendyline/squisq/doc";
71
- import { readFrontmatterThemeId } from "@bendyline/squisq/markdown";
71
+ import {
72
+ findBlockVideoEmbed,
73
+ readFrontmatterThemeId
74
+ } from "@bendyline/squisq/markdown";
75
+
76
+ // src/docx/videoEmbed.ts
77
+ import {
78
+ parseVideoEmbedInput,
79
+ parseVideoEmbedUrl
80
+ } from "@bendyline/squisq/markdown";
81
+ var NS_WP15 = "http://schemas.microsoft.com/office/word/2012/wordprocessingDrawing";
82
+ var WEB_VIDEO_EXT_URI = "{C809E66F-F1BF-436E-B5F7-EEA9579F0CBA}";
83
+ var NS_A = "http://schemas.openxmlformats.org/drawingml/2006/main";
84
+ var NS_PIC = "http://schemas.openxmlformats.org/drawingml/2006/picture";
85
+ var EMU_PER_INCH = 914400;
86
+ function wordVideoSize(embed) {
87
+ if (embed.aspectRatio < 1) {
88
+ return {
89
+ cx: Math.round(5 * EMU_PER_INCH * embed.aspectRatio),
90
+ cy: 5 * EMU_PER_INCH,
91
+ pixelWidth: 315,
92
+ pixelHeight: 560
93
+ };
94
+ }
95
+ return {
96
+ cx: 6 * EMU_PER_INCH,
97
+ cy: Math.round(6 * EMU_PER_INCH / embed.aspectRatio),
98
+ pixelWidth: 560,
99
+ pixelHeight: 315
100
+ };
101
+ }
102
+ function buildWordVideoRun(parts) {
103
+ const { embed, title, imageRelId, linkRelId, docPrId, embeddedHtml } = parts;
104
+ const { cx, cy, pixelWidth, pixelHeight } = wordVideoSize(embed);
105
+ const name = escapeXml(`${embed.providerName} video ${docPrId}`);
106
+ const descr = escapeXml(title || `${embed.providerName} video`);
107
+ const link = linkRelId ? `<a:hlinkClick xmlns:a="${NS_A}" r:id="${linkRelId}"/>` : "";
108
+ return `<w:r><w:drawing><wp:inline distT="0" distB="0" distL="0" distR="0"><wp:extent cx="${cx}" cy="${cy}"/><wp:docPr id="${docPrId}" name="${name}" descr="${descr}">${link}</wp:docPr><wp:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a="${NS_A}" noChangeAspect="1"/></wp:cNvGraphicFramePr><a:graphic xmlns:a="${NS_A}"><a:graphicData uri="${NS_PIC}"><pic:pic xmlns:pic="${NS_PIC}"><pic:nvPicPr><pic:cNvPr id="0" name="${name}" descr="${descr}">${link}</pic:cNvPr><pic:cNvPicPr><a:picLocks noChangeAspect="1"/></pic:cNvPicPr></pic:nvPicPr><pic:blipFill><a:blip r:embed="${imageRelId}"><a:extLst><a:ext uri="${WEB_VIDEO_EXT_URI}"><wp15:webVideoPr xmlns:wp15="${NS_WP15}" embeddedHtml="${escapeXml(embeddedHtml)}" h="${pixelHeight}" w="${pixelWidth}"/></a:ext></a:extLst></a:blip><a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic></wp:inline></w:drawing></w:r>`;
109
+ }
110
+ var GENERIC_TITLE_RE = /^(?:(?:[\w .-]+[ -])?(?:video[ -])?player|video|online video|picture)$/i;
111
+ function firstDescendant(el, localName) {
112
+ for (const child of Array.from(el.children)) {
113
+ if (child.localName === localName) return child;
114
+ const found = firstDescendant(child, localName);
115
+ if (found) return found;
116
+ }
117
+ return null;
118
+ }
119
+ function readWordVideo(drawing, resolveHyperlink) {
120
+ const webVideo = firstDescendant(drawing, "webVideoPr");
121
+ if (!webVideo) return null;
122
+ const fromHtml = parseVideoEmbedInput(webVideo.getAttribute("embeddedHtml") ?? "");
123
+ const click = firstDescendant(drawing, "hlinkClick");
124
+ const relId = click?.getAttributeNS(NS_R, "id") || click?.getAttribute("r:id") || null;
125
+ const fromLink = parseVideoEmbedUrl(relId ? resolveHyperlink(relId) : null);
126
+ const embed = fromLink && (!fromHtml || fromLink.provider === fromHtml.embed.provider && fromLink.id === fromHtml.embed.id) ? fromLink : fromHtml?.embed ?? null;
127
+ if (!embed) return null;
128
+ const docPr = firstDescendant(drawing, "docPr");
129
+ const candidates = [docPr?.getAttribute("title"), docPr?.getAttribute("descr"), fromHtml?.title];
130
+ const title = candidates.map((value) => value?.trim() ?? "").find(
131
+ (value) => value && !GENERIC_TITLE_RE.test(value) && value.toLowerCase() !== `${embed.providerName} video`.toLowerCase() && parseVideoEmbedUrl(value) === null
132
+ ) ?? null;
133
+ return { embed, title };
134
+ }
72
135
 
73
136
  // src/docx/styles.ts
74
137
  var HEADING_STYLE_MAP = {
@@ -130,6 +193,9 @@ async function markdownDocToDocx(doc, options = {}) {
130
193
  const resolvedOptions = options.themeId !== void 0 ? options : { ...options, themeId: readFrontmatterThemeId(doc.frontmatter) };
131
194
  const ctx = new ExportContext(resolvedOptions, doc);
132
195
  const stories = partitionDocumentStories(doc.children);
196
+ if ([...stories.body, ...stories.header, ...stories.footer].some(isVideoBlock)) {
197
+ ctx.videoPosters = await import("./videoPoster-D7DTZC2L.js");
198
+ }
133
199
  ctx.setRelationshipSource("document");
134
200
  const bodyXml = convertBlocks(stories.body, ctx);
135
201
  ctx.setRelationshipSource("header");
@@ -186,6 +252,8 @@ var ExportContext = class {
186
252
  this.hasFootnotes = false;
187
253
  /** Font Awesome families referenced while converting body/header/footer runs. */
188
254
  this.iconFamilies = /* @__PURE__ */ new Set();
255
+ /** Poster + embed-code helpers, loaded only when the document has a video. */
256
+ this.videoPosters = null;
189
257
  this.nextDocPrId = 1;
190
258
  let themeFont;
191
259
  let themeTitleFont;
@@ -306,10 +374,50 @@ function convertBlocks(nodes, ctx) {
306
374
  for (let index = 0; index < nodes.length; index++) {
307
375
  if ((index & 255) === 0) ctx.signal?.throwIfAborted();
308
376
  const node = nodes[index];
309
- parts.push(convertBlock(node, ctx, 0));
377
+ const video = ctx.videoPosters ? findBlockVideoEmbed(node) : null;
378
+ parts.push(video && convertVideoEmbed(video, ctx) || convertBlock(node, ctx, 0));
310
379
  }
311
380
  return parts.join("");
312
381
  }
382
+ function isVideoBlock(node) {
383
+ return findBlockVideoEmbed(node) !== null;
384
+ }
385
+ function convertVideoEmbed(video, ctx) {
386
+ const posters = ctx.videoPosters;
387
+ if (!posters) return "";
388
+ const { embed, title } = video;
389
+ const portrait = embed.aspectRatio < 1;
390
+ const thumbnail = embed.thumbnailUrl ? ctx.resolvedImages.get(embed.thumbnailUrl) : void 0;
391
+ const poster = thumbnail ?? posters.videoPosterPng(portrait);
392
+ if (!poster) return "";
393
+ const ext = poster.contentType.split("/")[1]?.replace("jpeg", "jpg") || "png";
394
+ const { relId, docPrId } = ctx.addImage(
395
+ poster.data,
396
+ poster.contentType,
397
+ `video${ctx.images.length + 1}.${ext}`
398
+ );
399
+ const { pixelWidth, pixelHeight } = wordVideoSize(embed);
400
+ const run = buildWordVideoRun({
401
+ embed,
402
+ title,
403
+ imageRelId: relId,
404
+ linkRelId: ctx.addHyperlink(embed.watchUrl),
405
+ docPrId,
406
+ embeddedHtml: posters.officeVideoEmbedHtml(embed, title, pixelWidth, pixelHeight)
407
+ });
408
+ const caption = convertInlines(
409
+ [
410
+ {
411
+ type: "link",
412
+ url: embed.watchUrl,
413
+ children: [{ type: "text", value: title || embed.watchUrl }]
414
+ }
415
+ ],
416
+ ctx,
417
+ bodyFormat(ctx)
418
+ );
419
+ return `<w:p><w:pPr><w:keepNext/></w:pPr>${run}</w:p><w:p>${caption}</w:p>`;
420
+ }
313
421
  function convertBlock(node, ctx, listDepth) {
314
422
  switch (node.type) {
315
423
  case "heading":
@@ -529,29 +637,29 @@ function convertImage(node, ctx) {
529
637
  const filename = `image${ctx.images.length + 1}.${ext}`;
530
638
  const { relId, docPrId } = ctx.addImage(data, contentType, filename);
531
639
  const dims = imageEntry.width && imageEntry.height ? { width: imageEntry.width, height: imageEntry.height } : readImageDimensions(data);
532
- const EMU_PER_INCH = 914400;
533
- const MAX_WIDTH_EMU = 6 * EMU_PER_INCH;
534
- const MAX_HEIGHT_EMU = 8 * EMU_PER_INCH;
640
+ const EMU_PER_INCH2 = 914400;
641
+ const MAX_WIDTH_EMU = 6 * EMU_PER_INCH2;
642
+ const MAX_HEIGHT_EMU = 8 * EMU_PER_INCH2;
535
643
  let cx;
536
644
  let cy;
537
645
  if (dims) {
538
646
  const fitted = fitWithin(
539
- dims.width / 96 * EMU_PER_INCH,
540
- dims.height / 96 * EMU_PER_INCH,
647
+ dims.width / 96 * EMU_PER_INCH2,
648
+ dims.height / 96 * EMU_PER_INCH2,
541
649
  MAX_WIDTH_EMU,
542
650
  MAX_HEIGHT_EMU
543
651
  );
544
652
  cx = Math.round(fitted.width);
545
653
  cy = Math.round(fitted.height);
546
654
  } else {
547
- cx = 5 * EMU_PER_INCH;
548
- cy = 3 * EMU_PER_INCH;
655
+ cx = 5 * EMU_PER_INCH2;
656
+ cy = 3 * EMU_PER_INCH2;
549
657
  }
550
658
  const name = escapeXml(node.alt || filename);
551
659
  const description = node.alt ? ` descr="${escapeXml(node.alt)}"` : "";
552
- const NS_A = "http://schemas.openxmlformats.org/drawingml/2006/main";
553
- const NS_PIC = "http://schemas.openxmlformats.org/drawingml/2006/picture";
554
- return `<w:r><w:drawing><wp:inline distT="0" distB="0" distL="0" distR="0"><wp:extent cx="${cx}" cy="${cy}"/><wp:docPr id="${docPrId}" name="${name}"${description}/><wp:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a="${NS_A}" noChangeAspect="1"/></wp:cNvGraphicFramePr><a:graphic xmlns:a="${NS_A}"><a:graphicData uri="${NS_PIC}"><pic:pic xmlns:pic="${NS_PIC}"><pic:nvPicPr><pic:cNvPr id="0" name="${name}"/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill><a:blip r:embed="${relId}"/><a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic></wp:inline></w:drawing></w:r>`;
660
+ const NS_A2 = "http://schemas.openxmlformats.org/drawingml/2006/main";
661
+ const NS_PIC2 = "http://schemas.openxmlformats.org/drawingml/2006/picture";
662
+ return `<w:r><w:drawing><wp:inline distT="0" distB="0" distL="0" distR="0"><wp:extent cx="${cx}" cy="${cy}"/><wp:docPr id="${docPrId}" name="${name}"${description}/><wp:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a="${NS_A2}" noChangeAspect="1"/></wp:cNvGraphicFramePr><a:graphic xmlns:a="${NS_A2}"><a:graphicData uri="${NS_PIC2}"><pic:pic xmlns:pic="${NS_PIC2}"><pic:nvPicPr><pic:cNvPr id="0" name="${name}"/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill><a:blip r:embed="${relId}"/><a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic></wp:inline></w:drawing></w:r>`;
555
663
  }
556
664
  function convertFootnoteRef(node, ctx) {
557
665
  const fnId = ctx.getFootnoteId(node.identifier);
@@ -759,7 +867,7 @@ function buildFootnotesXml(ctx) {
759
867
 
760
868
  // src/docx/import.ts
761
869
  import { markdownToDoc } from "@bendyline/squisq/doc";
762
- import { stringifyMarkdown } from "@bendyline/squisq/markdown";
870
+ import { findBlockVideoEmbed as findBlockVideoEmbed2, stringifyMarkdown } from "@bendyline/squisq/markdown";
763
871
  var DOCX_MAIN_PART = "word/document.xml";
764
872
  async function docxToMarkdownDoc(data, options = {}) {
765
873
  const pkg = await openPackage(data, options);
@@ -811,7 +919,9 @@ async function buildImportContext(pkg, options, mainPartPath = DOCX_MAIN_PART) {
811
919
  pkg,
812
920
  options,
813
921
  extractedImages: /* @__PURE__ */ new Map(),
814
- imageCounter: 0
922
+ imageCounter: 0,
923
+ videoDrawings: 0,
924
+ videoParagraphs: /* @__PURE__ */ new WeakSet()
815
925
  };
816
926
  for (const [id, depth] of Object.entries(HEADING_STYLE_MAP)) {
817
927
  ctx.headingStyles.set(id, depth);
@@ -986,8 +1096,10 @@ async function convertBlockElements(children, ctx) {
986
1096
  i += consumed;
987
1097
  continue;
988
1098
  }
1099
+ const videoDrawingsBefore = ctx.videoDrawings;
989
1100
  const block = await convertParagraph2(el, ctx);
990
- if (block) {
1101
+ if (block && ctx.videoDrawings > videoDrawingsBefore) ctx.videoParagraphs.add(block);
1102
+ if (block && !absorbVideoCaption(result, block, ctx)) {
991
1103
  result.push(block);
992
1104
  }
993
1105
  i++;
@@ -1013,6 +1125,24 @@ async function convertBlockElements(children, ctx) {
1013
1125
  }
1014
1126
  return result;
1015
1127
  }
1128
+ function absorbVideoCaption(result, block, ctx) {
1129
+ const previous = result[result.length - 1];
1130
+ if (previous?.type !== "paragraph" || block.type !== "paragraph") return false;
1131
+ if (!ctx.videoParagraphs.has(previous)) return false;
1132
+ const video = findBlockVideoEmbed2(previous);
1133
+ const caption = findBlockVideoEmbed2(block);
1134
+ if (!video || !caption || caption.embed.watchUrl !== video.embed.watchUrl) return false;
1135
+ if (!video.title && caption.title) {
1136
+ previous.children = [
1137
+ {
1138
+ type: "link",
1139
+ url: video.embed.watchUrl,
1140
+ children: [{ type: "text", value: caption.title }]
1141
+ }
1142
+ ];
1143
+ }
1144
+ return true;
1145
+ }
1016
1146
  async function convertParagraph2(el, ctx) {
1017
1147
  const pPr = getFirstChildElement(el, "pPr");
1018
1148
  const styleId = getParagraphStyleId(pPr);
@@ -1133,6 +1263,21 @@ async function convertRun(runEl, ctx) {
1133
1263
  }
1134
1264
  async function convertDrawingContent(el, ctx) {
1135
1265
  const result = [];
1266
+ const video = readWordVideo(el, (relId) => {
1267
+ const rel = ctx.documentRels.get(relId);
1268
+ return rel?.targetMode === "External" ? rel.target : null;
1269
+ });
1270
+ if (video) {
1271
+ const url = video.embed.watchUrl;
1272
+ ctx.videoDrawings++;
1273
+ return [
1274
+ {
1275
+ type: "link",
1276
+ url,
1277
+ children: [{ type: "text", value: video.title ?? url }]
1278
+ }
1279
+ ];
1280
+ }
1136
1281
  const textBoxes = findDescendants(el, "txbxContent");
1137
1282
  for (const textBox of textBoxes) {
1138
1283
  const inlines = await flattenContainerToInlines(textBox, ctx);
@@ -4,7 +4,7 @@ import {
4
4
  docxToDoc,
5
5
  docxToMarkdownDoc,
6
6
  markdownDocToDocx
7
- } from "../chunk-EQAHFGGX.js";
7
+ } from "../chunk-YFSFEPBB.js";
8
8
  import "../chunk-A6N6IN3I.js";
9
9
  import "../chunk-AVOZAKGP.js";
10
10
  import "../chunk-ILCJ3WFD.js";
@@ -1,6 +1,6 @@
1
1
  import { Theme, ThemeRegistry, Doc } from '@bendyline/squisq/schemas';
2
- import { H as HtmlExportOptions } from '../import-D8y5SV0T.js';
3
- export { a as HtmlImportOptions, c as collectImagePaths, g as generateExternalHtml, h as htmlToMarkdown, b as htmlToMarkdownDoc, d as htmlToMarkdownDocSync } from '../import-D8y5SV0T.js';
2
+ import { H as HtmlExportOptions } from '../import-ipP4Ghp8.js';
3
+ export { a as HtmlImportOptions, c as collectImagePaths, g as generateExternalHtml, h as htmlToMarkdown, b as htmlToMarkdownDoc, d as htmlToMarkdownDocSync } from '../import-ipP4Ghp8.js';
4
4
  import { HtmlPolicy, MarkdownDocument } from '@bendyline/squisq/markdown';
5
5
 
6
6
  /**
@@ -116,6 +116,18 @@ interface PlainHtmlExportOptions {
116
116
  * event handlers, and executable URL schemes before emitting HTML.
117
117
  */
118
118
  htmlPolicy?: HtmlPolicy;
119
+ /**
120
+ * How a hosted video (a paragraph that is only a link to a YouTube, Vimeo,
121
+ * Loom, Dailymotion or Wistia page, or that provider's `<iframe>` embed
122
+ * code) renders:
123
+ *
124
+ * - `player` (default) — the provider's player iframe;
125
+ * - `poster` — a static card linking to the video's page (the provider's
126
+ * thumbnail where it serves one). For contexts where a player cannot run:
127
+ * a preview iframe sandboxed without `allow-scripts` (nested frames
128
+ * inherit the sandbox, so the player would load broken), or print.
129
+ */
130
+ videoEmbeds?: 'player' | 'poster';
119
131
  }
120
132
  /**
121
133
  * Render a parsed markdown document as a complete, semantic HTML page.
@@ -10,7 +10,7 @@ import {
10
10
  markdownDocToPlainHtml,
11
11
  markdownDocsToHtmlBundle,
12
12
  markdownDocsToPlainHtmlBundle
13
- } from "../chunk-TLDBJH7D.js";
13
+ } from "../chunk-JY3T4KOP.js";
14
14
  import {
15
15
  arrayBufferToBase64DataUrl,
16
16
  extractFilename,
@@ -123,6 +123,13 @@ interface HtmlImportOptions {
123
123
  * trusted input where you want raw fidelity.
124
124
  */
125
125
  sanitize?: boolean;
126
+ /**
127
+ * Keep the page's `<title>` and `<meta name="description">` as frontmatter
128
+ * `title` / `description`. Default `true`. Head content never becomes body
129
+ * text either way; pass `false` where frontmatter is unwanted (for example
130
+ * when converting an email body).
131
+ */
132
+ headMetadata?: boolean;
126
133
  }
127
134
  /** Parse HTML into a squisq `MarkdownDocument`. */
128
135
  declare function htmlToMarkdownDocSync(html: string, options?: HtmlImportOptions): MarkdownDocument;
package/dist/index.d.ts CHANGED
@@ -6,11 +6,12 @@ export { PdfExportOptions, PdfImportOptions, configurePdfWorker, docToPdf, markd
6
6
  export { HtmlZipExportOptions, docToHtml, docToHtmlZip } from './html/index.js';
7
7
  export { EpubExportOptions, docToEpub, markdownDocToEpub } from './epub/index.js';
8
8
  export { ExtractedFileTheme, InferSourceFormat, InferThemeOptions, InferredFileTheme, compileExtractedTheme, inferThemeFromFile } from './infer/index.js';
9
- export { B as BUILTIN_FORMAT_IDS, a as BuiltinFormatOptions, C as ConversionLimits, b as ConversionResult, c as ConvertOptions, d as ConvertSource, D as DEFAULT_CONVERSION_LIMITS, e as DbkFormatOptions, F as FormatDefinition, f as FormatId, g as FormatRegistry, M as MarkdownFormatOptions, N as NormalizedInput, P as PreparedConversion, h as PreparedExportOptions, r as resolveConversionLimits } from './types-Cubj9A8c.js';
9
+ export { B as BUILTIN_FORMAT_IDS, a as BuiltinFormatOptions, C as ConversionLimits, b as ConversionResult, c as ConvertOptions, d as ConvertSource, D as DEFAULT_CONVERSION_LIMITS, e as DbkFormatOptions, F as FormatDefinition, f as FormatId, g as FormatRegistry, M as MarkdownFormatOptions, N as NormalizedInput, P as PreparedConversion, h as PreparedExportOptions, r as resolveConversionLimits } from './types-CVv9eldp.js';
10
10
  export { ConversionError, ConversionErrorCode, ConversionErrorOptions, convert, createRegistry, defaultFormats, defaultRegistry, prepareConversion } from './registry/index.js';
11
- export { ImportedOutsideInDocument, OUTSIDE_IN_FORMAT_IDS, OUTSIDE_IN_UPDATE_FROM_MARKDOWN_KEY, OutsideInFormatId, OutsideInLayout, OutsideInMetadata, RenderOutsideInOptions, chooseOutsideInMarkdownPath, importOutsideInDocument, isOutsideInMarkdownEditingEnabled, isOutsideInTargetPath, readOutsideInMetadata, renderOutsideInDocument, resolveOutsideInLayout, withOutsideInMarkdownEditing, withOutsideInMetadata } from './outside-in/index.js';
11
+ export { ImportedOutsideInDocument, OUTSIDE_IN_UPDATE_FROM_MARKDOWN_KEY, OutsideInMetadata, RenderOutsideInOptions, importOutsideInDocument, isOutsideInMarkdownEditingEnabled, readOutsideInMetadata, renderOutsideInDocument, withOutsideInMarkdownEditing, withOutsideInMetadata } from './outside-in/index.js';
12
12
  export { Z as ZipSafetyError, a as ZipSafetyErrorCode, b as ZipSafetyErrorOptions, c as ZipSafetyLimits } from './zipLimits-BOKCB7qk.js';
13
- export { H as HtmlExportOptions, a as HtmlImportOptions, c as collectImagePaths, h as htmlToMarkdown, b as htmlToMarkdownDoc, d as htmlToMarkdownDocSync } from './import-D8y5SV0T.js';
13
+ export { H as HtmlExportOptions, a as HtmlImportOptions, c as collectImagePaths, h as htmlToMarkdown, b as htmlToMarkdownDoc, d as htmlToMarkdownDocSync } from './import-ipP4Ghp8.js';
14
+ export { OUTSIDE_IN_FORMAT_IDS, OutsideInFormatId, OutsideInLayout, chooseOutsideInMarkdownPath, isOutsideInTargetPath, resolveOutsideInLayout } from './outside-in/layout.js';
14
15
  export { P as PptxExportOptions, a as PptxImportOptions, d as docToPptx, m as markdownDocToPptx, p as pptxToMarkdownDoc } from './import-Cn_V1_7e.js';
15
16
  export { X as XlsxExportOptions, a as XlsxImportOptions, b as XlsxTable, c as XlsxTableColumn, d as XlsxTablesOptions, e as docToXlsx, g as gridToTables, m as markdownDocToXlsx, x as xlsxToMarkdownDoc, f as xlsxToTables } from './export-C4bBD3Mx.js';
16
17
  import '@bendyline/squisq/schemas';
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  htmlToMarkdown,
6
6
  htmlToMarkdownDoc,
7
7
  htmlToMarkdownDocSync
8
- } from "./chunk-TLDBJH7D.js";
8
+ } from "./chunk-JY3T4KOP.js";
9
9
  import {
10
10
  docToEpub,
11
11
  markdownDocToEpub
@@ -21,18 +21,14 @@ import {
21
21
  compileExtractedTheme
22
22
  } from "./chunk-2JJ5RFDZ.js";
23
23
  import {
24
- OUTSIDE_IN_FORMAT_IDS,
25
24
  OUTSIDE_IN_UPDATE_FROM_MARKDOWN_KEY,
26
- chooseOutsideInMarkdownPath,
27
25
  importOutsideInDocument,
28
26
  isOutsideInMarkdownEditingEnabled,
29
- isOutsideInTargetPath,
30
27
  readOutsideInMetadata,
31
28
  renderOutsideInDocument,
32
- resolveOutsideInLayout,
33
29
  withOutsideInMarkdownEditing,
34
30
  withOutsideInMetadata
35
- } from "./chunk-SAMQZRJU.js";
31
+ } from "./chunk-53CTG4YR.js";
36
32
  import {
37
33
  DEFAULT_CONVERSION_LIMITS,
38
34
  convert,
@@ -41,22 +37,28 @@ import {
41
37
  defaultRegistry,
42
38
  prepareConversion,
43
39
  resolveConversionLimits
44
- } from "./chunk-VE2ADQ5T.js";
40
+ } from "./chunk-SZDCCL6O.js";
45
41
  import {
46
42
  ConversionError
47
43
  } from "./chunk-KXOZMWBS.js";
44
+ import {
45
+ OUTSIDE_IN_FORMAT_IDS,
46
+ chooseOutsideInMarkdownPath,
47
+ isOutsideInTargetPath,
48
+ resolveOutsideInLayout
49
+ } from "./chunk-C23JYVPG.js";
48
50
  import {
49
51
  docToDocx,
50
52
  docxToDoc,
51
53
  docxToMarkdownDoc,
52
54
  markdownDocToDocx
53
- } from "./chunk-EQAHFGGX.js";
55
+ } from "./chunk-YFSFEPBB.js";
54
56
  import {
55
57
  docToPptx,
56
58
  markdownDocToPptx,
57
59
  pptxToDoc,
58
60
  pptxToMarkdownDoc
59
- } from "./chunk-EMAPNC32.js";
61
+ } from "./chunk-76BFHBZD.js";
60
62
  import "./chunk-6N2J7C2B.js";
61
63
  import "./chunk-YVCUNZB5.js";
62
64
  import "./chunk-A6N6IN3I.js";
@@ -91,7 +93,8 @@ import {
91
93
  markdownDocToPdf,
92
94
  pdfToDoc,
93
95
  pdfToMarkdownDoc
94
- } from "./chunk-ZDRSDYXN.js";
96
+ } from "./chunk-VJQZMIXU.js";
97
+ import "./chunk-34KECNZO.js";
95
98
  import "./chunk-BXWNU4T5.js";
96
99
  import "./chunk-IIQYS2YH.js";
97
100
  import "./chunk-USU6HTKB.js";
@@ -1,6 +1,8 @@
1
1
  import { MarkdownDocument } from '@bendyline/squisq/markdown';
2
2
  import { ContentContainer } from '@bendyline/squisq/storage';
3
- import { c as ConvertOptions, b as ConversionResult } from '../types-Cubj9A8c.js';
3
+ import { c as ConvertOptions, b as ConversionResult } from '../types-CVv9eldp.js';
4
+ import { OutsideInLayout, OutsideInFormatId } from './layout.js';
5
+ export { OUTSIDE_IN_FORMAT_IDS, chooseOutsideInMarkdownPath, isOutsideInTargetPath, resolveOutsideInLayout } from './layout.js';
4
6
  import '@bendyline/squisq/schemas';
5
7
  import '@bendyline/squisq/transform';
6
8
  import '../docx/index.js';
@@ -10,7 +12,7 @@ import '../import-Cn_V1_7e.js';
10
12
  import '../export-C4bBD3Mx.js';
11
13
  import '../csv/index.js';
12
14
  import '../pdf/index.js';
13
- import '../import-D8y5SV0T.js';
15
+ import '../import-ipP4Ghp8.js';
14
16
  import '../epub/index.js';
15
17
  import '../container/index.js';
16
18
 
@@ -28,37 +30,11 @@ import '../container/index.js';
28
30
  *
29
31
  * Hosts own filesystem authority and transaction ordering. This module owns
30
32
  * the portable path/frontmatter contract plus registry-backed import/export.
33
+ * The registry-free path rules are also published alone as
34
+ * `@bendyline/squisq-formats/outside-in-layout`.
31
35
  */
32
36
 
33
- declare const OUTSIDE_IN_FORMAT_IDS: readonly ["html", "docx", "pdf", "pptx", "xlsx", "csv"];
34
- type OutsideInFormatId = (typeof OUTSIDE_IN_FORMAT_IDS)[number];
35
37
  declare const OUTSIDE_IN_UPDATE_FROM_MARKDOWN_KEY = "squisq-updatefrommarkdown";
36
- interface OutsideInLayout {
37
- /** User-facing rendered file, relative to the host's workspace root. */
38
- targetPath: string;
39
- /** Registry format used to import and regenerate the target. */
40
- format: OutsideInFormatId;
41
- /** Parent directory of the rendered file. Empty at workspace root. */
42
- parentDirectory: string;
43
- /** Case-preserving rendered filename without its final extension. */
44
- stem: string;
45
- /** Case-preserving `<stem>_files` folder name. */
46
- companionName: string;
47
- /** Full workspace-relative companion directory. */
48
- companionDirectory: string;
49
- /** Slugged Markdown filename inside the companion directory. */
50
- markdownFilename: string;
51
- /** Full workspace-relative Markdown source path. */
52
- markdownPath: string;
53
- /** Rendered target path as stored relative to the Markdown source. */
54
- relativeTargetPath: string;
55
- /** Hidden directory containing the immutable pre-edit rendered file. */
56
- backupDirectory: string;
57
- /** Stable filename for the pre-edit rendered file. */
58
- backupFilename: string;
59
- /** Full workspace-relative path to the pre-edit rendered file. */
60
- backupPath: string;
61
- }
62
38
  interface OutsideInMetadata {
63
39
  version: 1;
64
40
  format: OutsideInFormatId;
@@ -83,14 +59,6 @@ interface RenderOutsideInOptions extends ConvertOptions {
83
59
  basePath?: string;
84
60
  };
85
61
  }
86
- /** Resolve the canonical companion/source layout for a supported rendered file. */
87
- declare function resolveOutsideInLayout(targetPath: string): OutsideInLayout | null;
88
- declare function isOutsideInTargetPath(path: string): boolean;
89
- /**
90
- * Pick an existing source inside the companion directory. Canonical slug wins;
91
- * a sole root-level Markdown file is accepted for older/manual layouts.
92
- */
93
- declare function chooseOutsideInMarkdownPath(layout: OutsideInLayout, paths: readonly string[]): string | null;
94
62
  /** Read outside-in metadata without interpreting the output path as authority. */
95
63
  declare function readOutsideInMetadata(source: string): OutsideInMetadata | null;
96
64
  /** Add or refresh the portable relationship while preserving unrelated frontmatter. */
@@ -114,4 +82,4 @@ declare function renderOutsideInDocument(source: {
114
82
  container?: ContentContainer;
115
83
  }, options?: RenderOutsideInOptions): Promise<ConversionResult>;
116
84
 
117
- export { type ImportedOutsideInDocument, OUTSIDE_IN_FORMAT_IDS, OUTSIDE_IN_UPDATE_FROM_MARKDOWN_KEY, type OutsideInFormatId, type OutsideInLayout, type OutsideInMetadata, type RenderOutsideInOptions, chooseOutsideInMarkdownPath, importOutsideInDocument, isOutsideInMarkdownEditingEnabled, isOutsideInTargetPath, readOutsideInMetadata, renderOutsideInDocument, resolveOutsideInLayout, withOutsideInMarkdownEditing, withOutsideInMetadata };
85
+ export { type ImportedOutsideInDocument, OUTSIDE_IN_UPDATE_FROM_MARKDOWN_KEY, OutsideInFormatId, OutsideInLayout, type OutsideInMetadata, type RenderOutsideInOptions, importOutsideInDocument, isOutsideInMarkdownEditingEnabled, readOutsideInMetadata, renderOutsideInDocument, withOutsideInMarkdownEditing, withOutsideInMetadata };
@@ -1,18 +1,20 @@
1
1
  import {
2
- OUTSIDE_IN_FORMAT_IDS,
3
2
  OUTSIDE_IN_UPDATE_FROM_MARKDOWN_KEY,
4
- chooseOutsideInMarkdownPath,
5
3
  importOutsideInDocument,
6
4
  isOutsideInMarkdownEditingEnabled,
7
- isOutsideInTargetPath,
8
5
  readOutsideInMetadata,
9
6
  renderOutsideInDocument,
10
- resolveOutsideInLayout,
11
7
  withOutsideInMarkdownEditing,
12
8
  withOutsideInMetadata
13
- } from "../chunk-SAMQZRJU.js";
14
- import "../chunk-VE2ADQ5T.js";
9
+ } from "../chunk-53CTG4YR.js";
10
+ import "../chunk-SZDCCL6O.js";
15
11
  import "../chunk-KXOZMWBS.js";
12
+ import {
13
+ OUTSIDE_IN_FORMAT_IDS,
14
+ chooseOutsideInMarkdownPath,
15
+ isOutsideInTargetPath,
16
+ resolveOutsideInLayout
17
+ } from "../chunk-C23JYVPG.js";
16
18
  import "../chunk-7AWFHP5U.js";
17
19
  export {
18
20
  OUTSIDE_IN_FORMAT_IDS,
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Outside-in path layout — the pure, synchronous half of the outside-in
3
+ * contract.
4
+ *
5
+ * Maps a user-facing rendered file to its hidden companion directory and
6
+ * slugged Markdown source:
7
+ *
8
+ * Tucson.pptx
9
+ * Tucson_files/
10
+ * tucson.md
11
+ * .original/original.pptx
12
+ *
13
+ * Published on its own as `@bendyline/squisq-formats/outside-in-layout` so
14
+ * hosts that only need the path rules (file explorers, workspace walkers, a
15
+ * CJS editor-extension host) can bundle them without the format registry or
16
+ * any format runtime. Keep this module free of imports: `/outside-in`
17
+ * re-exports everything here, and the published suite asserts the built
18
+ * entry reaches no other module.
19
+ */
20
+ declare const OUTSIDE_IN_FORMAT_IDS: readonly ["html", "docx", "pdf", "pptx", "xlsx", "csv"];
21
+ type OutsideInFormatId = (typeof OUTSIDE_IN_FORMAT_IDS)[number];
22
+ interface OutsideInLayout {
23
+ /** User-facing rendered file, relative to the host's workspace root. */
24
+ targetPath: string;
25
+ /** Registry format used to import and regenerate the target. */
26
+ format: OutsideInFormatId;
27
+ /** Parent directory of the rendered file. Empty at workspace root. */
28
+ parentDirectory: string;
29
+ /** Case-preserving rendered filename without its final extension. */
30
+ stem: string;
31
+ /** Case-preserving `<stem>_files` folder name. */
32
+ companionName: string;
33
+ /** Full workspace-relative companion directory. */
34
+ companionDirectory: string;
35
+ /** Slugged Markdown filename inside the companion directory. */
36
+ markdownFilename: string;
37
+ /** Full workspace-relative Markdown source path. */
38
+ markdownPath: string;
39
+ /** Rendered target path as stored relative to the Markdown source. */
40
+ relativeTargetPath: string;
41
+ /** Hidden directory containing the immutable pre-edit rendered file. */
42
+ backupDirectory: string;
43
+ /** Stable filename for the pre-edit rendered file. */
44
+ backupFilename: string;
45
+ /** Full workspace-relative path to the pre-edit rendered file. */
46
+ backupPath: string;
47
+ }
48
+ /** Resolve the canonical companion/source layout for a supported rendered file. */
49
+ declare function resolveOutsideInLayout(targetPath: string): OutsideInLayout | null;
50
+ /** True when `path` names a rendered file outside-in editing supports. */
51
+ declare function isOutsideInTargetPath(path: string): boolean;
52
+ /**
53
+ * Pick an existing source inside the companion directory. Canonical slug wins;
54
+ * a sole root-level Markdown file is accepted for older/manual layouts.
55
+ */
56
+ declare function chooseOutsideInMarkdownPath(layout: OutsideInLayout, paths: readonly string[]): string | null;
57
+
58
+ export { OUTSIDE_IN_FORMAT_IDS, type OutsideInFormatId, type OutsideInLayout, chooseOutsideInMarkdownPath, isOutsideInTargetPath, resolveOutsideInLayout };
@@ -0,0 +1,12 @@
1
+ import {
2
+ OUTSIDE_IN_FORMAT_IDS,
3
+ chooseOutsideInMarkdownPath,
4
+ isOutsideInTargetPath,
5
+ resolveOutsideInLayout
6
+ } from "../chunk-C23JYVPG.js";
7
+ export {
8
+ OUTSIDE_IN_FORMAT_IDS,
9
+ chooseOutsideInMarkdownPath,
10
+ isOutsideInTargetPath,
11
+ resolveOutsideInLayout
12
+ };
package/dist/pdf/index.js CHANGED
@@ -5,7 +5,8 @@ import {
5
5
  pdfToContainer,
6
6
  pdfToDoc,
7
7
  pdfToMarkdownDoc
8
- } from "../chunk-ZDRSDYXN.js";
8
+ } from "../chunk-VJQZMIXU.js";
9
+ import "../chunk-34KECNZO.js";
9
10
  import "../chunk-BXWNU4T5.js";
10
11
  import "../chunk-IIQYS2YH.js";
11
12
  import "../chunk-USU6HTKB.js";
@@ -4,7 +4,7 @@ import {
4
4
  pptxToContainer,
5
5
  pptxToDoc,
6
6
  pptxToMarkdownDoc
7
- } from "../chunk-EMAPNC32.js";
7
+ } from "../chunk-76BFHBZD.js";
8
8
  import {
9
9
  analyzePptxLayouts,
10
10
  inspectPptxLayouts
@@ -1,5 +1,5 @@
1
- import { f as FormatId, g as FormatRegistry, F as FormatDefinition, d as ConvertSource, c as ConvertOptions, b as ConversionResult, P as PreparedConversion } from '../types-Cubj9A8c.js';
2
- export { B as BUILTIN_FORMAT_IDS, a as BuiltinFormatOptions, C as ConversionLimits, D as DEFAULT_CONVERSION_LIMITS, e as DbkFormatOptions, M as MarkdownFormatOptions, N as NormalizedInput, h as PreparedExportOptions, r as resolveConversionLimits } from '../types-Cubj9A8c.js';
1
+ import { f as FormatId, g as FormatRegistry, F as FormatDefinition, d as ConvertSource, c as ConvertOptions, b as ConversionResult, P as PreparedConversion } from '../types-CVv9eldp.js';
2
+ export { B as BUILTIN_FORMAT_IDS, a as BuiltinFormatOptions, C as ConversionLimits, D as DEFAULT_CONVERSION_LIMITS, e as DbkFormatOptions, M as MarkdownFormatOptions, N as NormalizedInput, h as PreparedExportOptions, r as resolveConversionLimits } from '../types-CVv9eldp.js';
3
3
  import '@bendyline/squisq/schemas';
4
4
  import '@bendyline/squisq/transform';
5
5
  import '@bendyline/squisq/markdown';
@@ -11,7 +11,7 @@ import '../import-Cn_V1_7e.js';
11
11
  import '../export-C4bBD3Mx.js';
12
12
  import '../csv/index.js';
13
13
  import '../pdf/index.js';
14
- import '../import-D8y5SV0T.js';
14
+ import '../import-ipP4Ghp8.js';
15
15
  import '../epub/index.js';
16
16
  import '../container/index.js';
17
17
 
@@ -9,7 +9,7 @@ import {
9
9
  defaultRegistry,
10
10
  prepareConversion,
11
11
  resolveConversionLimits
12
- } from "../chunk-VE2ADQ5T.js";
12
+ } from "../chunk-SZDCCL6O.js";
13
13
  import {
14
14
  ConversionError
15
15
  } from "../chunk-KXOZMWBS.js";
@@ -7,7 +7,7 @@ import { a as PptxImportOptions, P as PptxExportOptions } from './import-Cn_V1_7
7
7
  import { j as XlsxContainerOptions, X as XlsxExportOptions } from './export-C4bBD3Mx.js';
8
8
  import { CsvContainerOptions, CsvExportOptions } from './csv/index.js';
9
9
  import { PdfImportOptions, PdfExportOptions } from './pdf/index.js';
10
- import { a as HtmlImportOptions, H as HtmlExportOptions } from './import-D8y5SV0T.js';
10
+ import { a as HtmlImportOptions, H as HtmlExportOptions } from './import-ipP4Ghp8.js';
11
11
  import { EpubExportOptions } from './epub/index.js';
12
12
  import { ContainerToZipOptions } from './container/index.js';
13
13
  import { c as ZipSafetyLimits } from './zipLimits-BOKCB7qk.js';