@taprootio/site-authoring 0.11.0 → 0.11.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taprootio/site-authoring",
3
- "version": "0.11.0",
3
+ "version": "0.11.1",
4
4
  "description": "Taproot site authoring CLI: sign in once in the browser, then pull, validate, push, preview, approve, and deploy a site from the terminal",
5
5
  "type": "module",
6
6
  "license": "ISC",
package/src/constants.js CHANGED
@@ -1,5 +1,7 @@
1
+ import packageMetadata from "../package.json" with { type: "json" };
2
+
1
3
  export const CLI_NAME = "@taprootio/site-authoring";
2
- export const CLI_VERSION = "0.11.0";
4
+ export const CLI_VERSION = packageMetadata.version;
3
5
  export const CLI_BINARY_NAME = "taproot-site";
4
6
  export const RESULT_SCHEMA_VERSION = 1;
5
7
  export const CONFIG_FILE_NAME = "taproot-site.json";
@@ -10,6 +10,17 @@
10
10
  import freeFormSectionRegistry from "./free-form-section-registry.json" with { type: "json" };
11
11
 
12
12
  export const TIPTAP_SITE_DEFAULT = "site-default";
13
+ export const DEFAULT_TIPTAP_IMAGE_MAX_IMAGES_PER_ROW = 2;
14
+ export const TIPTAP_IMAGE_MAX_IMAGES_PER_ROW_OPTIONS = [1, 2, 3] as const;
15
+ export const TIPTAP_IMAGE_NARROW_CONTENT_REM = 40;
16
+
17
+ export function normalizeTiptapImageMaxImagesPerRow(value: unknown): number {
18
+ const count = Number(value);
19
+ return TIPTAP_IMAGE_MAX_IMAGES_PER_ROW_OPTIONS.some(option => option === count)
20
+ ? count
21
+ : DEFAULT_TIPTAP_IMAGE_MAX_IMAGES_PER_ROW;
22
+ }
23
+
13
24
  export const DEFAULT_TIPTAP_IMAGE_MAX_HEIGHT_VH = 60;
14
25
  export const TIPTAP_IMAGE_MAX_HEIGHT_OPTIONS = [45, 60, 75, 90] as const;
15
26
  const SECTION_CONTEXT_PATTERN = new RegExp(
@@ -64,6 +75,7 @@ export interface RenderProseMirrorOptions {
64
75
  /** Normalize free-form root flow into full-bleed Espalier sections. */
65
76
  freeFormSections?: boolean;
66
77
  imageDefaults?: {
78
+ maxImagesPerRow?: number | null;
67
79
  placement?: TiptapImagePresentationPlacement | string | null;
68
80
  /** The page shell emits this default once so rich-text formats inherit it. */
69
81
  maxHeightVh?: TiptapImageMaxHeightVh | number | null;
@@ -71,7 +83,10 @@ export interface RenderProseMirrorOptions {
71
83
  /** Generator-only render hooks for structured nodes that need page context. */
72
84
  nodeRenderers?: {
73
85
  componentBlock?: (node: ProseMirrorNode) => string | undefined;
74
- taprootImage?: (node: ProseMirrorNode) => string | undefined;
86
+ taprootImage?: (
87
+ node: ProseMirrorNode,
88
+ context: { groupColumns?: number; fullWidth: boolean },
89
+ ) => string | undefined;
75
90
  /** Emits an adopted fragment's canonical bytes; without it the node renders nothing. */
76
91
  integrationFragment?: (node: ProseMirrorNode) => string | undefined;
77
92
  };
@@ -151,14 +166,26 @@ function isTextBlock(type: string): boolean {
151
166
  }
152
167
 
153
168
  type RenderPlacement = "root" | "section" | "nested";
169
+ type RenderOptionsWithLayout = RenderProseMirrorOptions & { fullWidthImages?: boolean };
154
170
 
155
171
  function renderNodes(
156
172
  nodes: readonly ProseMirrorNode[],
157
- options: RenderProseMirrorOptions,
173
+ options: RenderOptionsWithLayout,
158
174
  placement: RenderPlacement,
159
175
  ): string {
160
- return nodes.map((node) => {
161
- const html = renderNode(node, options, placement);
176
+ let imageRunEnd = 0;
177
+ let groupColumns: number | undefined;
178
+ return nodes.map((node, index) => {
179
+ const isImage = node.type === "taprootImage";
180
+ if (isImage && index >= imageRunEnd) {
181
+ imageRunEnd = index + 1;
182
+ while (imageRunEnd < nodes.length && nodes[imageRunEnd].type === "taprootImage") imageRunEnd++;
183
+ const count = imageRunEnd - index;
184
+ groupColumns = count > 1
185
+ ? Math.min(count, normalizeTiptapImageMaxImagesPerRow(options.imageDefaults?.maxImagesPerRow))
186
+ : undefined;
187
+ }
188
+ const html = renderNode(node, options, placement, false, isImage ? groupColumns : undefined);
162
189
  options.onRenderedNode?.(node, html);
163
190
  return html;
164
191
  }).join("");
@@ -217,9 +244,10 @@ function isRootBandComponent(node: ProseMirrorNode): boolean {
217
244
 
218
245
  function renderNode(
219
246
  node: ProseMirrorNode,
220
- options: RenderProseMirrorOptions,
247
+ options: RenderOptionsWithLayout,
221
248
  placement: RenderPlacement,
222
249
  compactFollowing = false,
250
+ groupColumns?: number,
223
251
  ): string {
224
252
  switch (node.type) {
225
253
  case "doc":
@@ -263,7 +291,8 @@ function renderNode(
263
291
  case "table":
264
292
  return placement === "root" || placement === "section" ? renderTable(node, options) : "";
265
293
  case "taprootImage":
266
- return options.nodeRenderers?.taprootImage?.(node) ?? renderTaprootImage(node, options);
294
+ return options.nodeRenderers?.taprootImage?.(node, { groupColumns, fullWidth: options.fullWidthImages === true })
295
+ ?? renderTaprootImage(node, options);
267
296
  case "inlineFacts":
268
297
  return renderInlineFacts(node, options, placement);
269
298
  case "componentBlock":
@@ -737,7 +766,7 @@ function renderSection(
737
766
  const content = renderElement(
738
767
  "div",
739
768
  { "data-section-content": true },
740
- renderNodes(nodes, options, contentPlacement),
769
+ renderNodes(nodes, { ...options, fullWidthImages: contentPadding === "none" }, contentPlacement),
741
770
  );
742
771
  const surfaced = surface === "none"
743
772
  ? content
@@ -1181,6 +1210,7 @@ function resolveImagePresentation(
1181
1210
  if (maxHeightOverride) styleParts.push(`--taproot-article-image-max-height: ${maxHeightOverride}`);
1182
1211
  if (borderWidthOverride) styleParts.push(`--esp-image-border: ${imageBorderValue(borderWidthOverride)}`);
1183
1212
  return {
1213
+ "data-tiptap-image": normalizeTiptapImageMaxImagesPerRow(defaults?.maxImagesPerRow),
1184
1214
  // The page shell owns the inherited site default so HTML and structured
1185
1215
  // rich text share it. Serialize only an explicit legacy choice; this
1186
1216
  // includes 60vh when it intentionally overrides a taller site default.
@@ -125,6 +125,7 @@ export const SETTINGS_GROUPS = Object.freeze([
125
125
  // the same as the site behaves.
126
126
  { name: "hideLightboxComments", wireType: "boolean" },
127
127
  { name: "tiptapImageMaxHeightVh", wireType: "int" },
128
+ { name: "tiptapImageMaxImagesPerRow", wireType: "int" },
128
129
  { name: "tiptapImageBorderWidth", wireType: "int" },
129
130
  { name: "albumBorderWidth", wireType: "int" },
130
131
  { name: "albumMaxImagesPerRow", wireType: "int" },