lists instead of manual bullet symbols.`
+ if (type !== "text" && el.tagName !== "LI" && /^[•●○▪‣·▸◆◇■□]\s/u.test(text2.trimStart())) {
+ addDiagnostic(
+ "fallback",
+ "manual_bullet_unrepaired",
+ `Text element <${el.tagName.toLowerCase()}> still starts with a manual bullet; exporting as editable text.`,
+ el
);
- return false;
}
const computed = view.getComputedStyle(el);
if (computed.display === "none" || computed.visibility === "hidden" || parseFloat(computed.opacity || "1") <= 0) {
@@ -13020,8 +11614,481 @@ function extractSlideDataFromDocument(doc = document) {
}
return true;
};
+ const svgColor = (value2, fallback = null) => {
+ if (!value2 || value2 === "none" || value2 === "transparent") return fallback;
+ return rgbToHex2(value2);
+ };
+ const svgNumber = (value2, fallback = 0) => {
+ const parsed = parseFloat(String(value2 || ""));
+ return Number.isFinite(parsed) ? parsed : fallback;
+ };
+ const identityMatrix = () => [1, 0, 0, 1, 0, 0];
+ const multiplyMatrix = (left, right) => [
+ left[0] * right[0] + left[2] * right[1],
+ left[1] * right[0] + left[3] * right[1],
+ left[0] * right[2] + left[2] * right[3],
+ left[1] * right[2] + left[3] * right[3],
+ left[0] * right[4] + left[2] * right[5] + left[4],
+ left[1] * right[4] + left[3] * right[5] + left[5]
+ ];
+ const parseSvgTransform = (value2 = "") => {
+ const raw = String(value2 || "").trim();
+ let matrix = identityMatrix();
+ let layoutMatrix = identityMatrix();
+ let rotation = 0;
+ if (!raw || raw === "none") {
+ return { matrix, layoutMatrix, rotation, reliable: true };
+ }
+ if (/(?:skew|perspective|matrix3d)\s*\(/i.test(raw)) {
+ return { matrix, layoutMatrix, rotation, reliable: false };
+ }
+ const functions = raw.matchAll(/(matrix|translate|scale|rotate)\s*\(([^)]*)\)/gi);
+ let matched = false;
+ for (const match of functions) {
+ matched = true;
+ const name = match[1].toLowerCase();
+ const values2 = match[2].trim().split(/[\s,]+/).filter(Boolean).map((item) => parseFloat(item));
+ let next = identityMatrix();
+ let nextLayout = identityMatrix();
+ if (name === "matrix" && values2.length >= 6) {
+ if (Math.abs(values2[1]) > 1e-6 || Math.abs(values2[2]) > 1e-6) {
+ return { matrix, layoutMatrix, rotation, reliable: false };
+ }
+ next = values2.slice(0, 6);
+ nextLayout = next;
+ } else if (name === "translate") {
+ next = [1, 0, 0, 1, values2[0] || 0, values2[1] || 0];
+ nextLayout = next;
+ } else if (name === "scale") {
+ const x = Number.isFinite(values2[0]) ? values2[0] : 1;
+ const y = Number.isFinite(values2[1]) ? values2[1] : x;
+ next = [x, 0, 0, y, 0, 0];
+ nextLayout = next;
+ } else if (name === "rotate") {
+ const angle = values2[0] || 0;
+ rotation += angle;
+ const radians = angle * Math.PI / 180;
+ const cos2 = Math.cos(radians);
+ const sin2 = Math.sin(radians);
+ const rotationMatrix = [cos2, sin2, -sin2, cos2, 0, 0];
+ if (Number.isFinite(values2[1]) && Number.isFinite(values2[2])) {
+ next = multiplyMatrix(
+ multiplyMatrix([1, 0, 0, 1, values2[1], values2[2]], rotationMatrix),
+ [1, 0, 0, 1, -values2[1], -values2[2]]
+ );
+ } else {
+ next = rotationMatrix;
+ }
+ }
+ matrix = multiplyMatrix(matrix, next);
+ layoutMatrix = multiplyMatrix(layoutMatrix, nextLayout);
+ }
+ return { matrix, layoutMatrix, rotation, reliable: matched };
+ };
+ const transformForSvgNode = (node, svg) => {
+ if (typeof node.getCTM === "function") {
+ try {
+ const ctm = node.getCTM();
+ const matrix2 = ctm ? [ctm.a, ctm.b, ctm.c, ctm.d, ctm.e, ctm.f].map(Number) : null;
+ if (matrix2?.every(Number.isFinite)) {
+ const scaleX = Math.hypot(matrix2[0], matrix2[1]);
+ const scaleY = Math.hypot(matrix2[2], matrix2[3]);
+ const orthogonality = scaleX > 0 && scaleY > 0 ? Math.abs((matrix2[0] * matrix2[2] + matrix2[1] * matrix2[3]) / (scaleX * scaleY)) : Infinity;
+ if (orthogonality > 1e-5 || matrix2[0] * matrix2[3] - matrix2[1] * matrix2[2] <= 0) {
+ return {
+ matrix: matrix2,
+ layoutMatrix: identityMatrix(),
+ rotation: 0,
+ reliable: false,
+ coordinateSpace: "viewport"
+ };
+ }
+ return {
+ matrix: matrix2,
+ layoutMatrix: [scaleX, 0, 0, scaleY, matrix2[4], matrix2[5]],
+ rotation: Math.atan2(matrix2[1], matrix2[0]) * 180 / Math.PI,
+ reliable: true,
+ coordinateSpace: "viewport"
+ };
+ }
+ } catch {
+ }
+ }
+ const chain = [];
+ let current = node;
+ while (current && current !== svg) {
+ chain.unshift(current);
+ current = current.parentElement;
+ }
+ let matrix = identityMatrix();
+ let layoutMatrix = identityMatrix();
+ let rotation = 0;
+ for (const item of chain) {
+ const attributeTransform = parseSvgTransform(item.getAttribute("transform"));
+ if (!attributeTransform.reliable) {
+ return { matrix, layoutMatrix, rotation, reliable: false };
+ }
+ matrix = multiplyMatrix(matrix, attributeTransform.matrix);
+ layoutMatrix = multiplyMatrix(layoutMatrix, attributeTransform.layoutMatrix);
+ rotation += attributeTransform.rotation;
+ const computedTransformValue = view.getComputedStyle(item).transform;
+ if (computedTransformValue && computedTransformValue !== "none" && computedTransformValue !== item.getAttribute("transform")) {
+ const computedTransform = parseSvgTransform(computedTransformValue);
+ if (!computedTransform.reliable) {
+ return { matrix, layoutMatrix, rotation, reliable: false };
+ }
+ let computedMatrix = computedTransform.matrix;
+ const originValues = String(view.getComputedStyle(item).transformOrigin || "").split(/\s+/).map((value2) => parseFloat(value2));
+ if (Number.isFinite(originValues[0]) && Number.isFinite(originValues[1]) && computedTransform.rotation) {
+ computedMatrix = multiplyMatrix(
+ multiplyMatrix(
+ [1, 0, 0, 1, originValues[0], originValues[1]],
+ computedMatrix
+ ),
+ [1, 0, 0, 1, -originValues[0], -originValues[1]]
+ );
+ }
+ matrix = multiplyMatrix(matrix, computedMatrix);
+ layoutMatrix = multiplyMatrix(layoutMatrix, computedTransform.layoutMatrix);
+ rotation += computedTransform.rotation;
+ }
+ }
+ return {
+ matrix,
+ layoutMatrix,
+ rotation,
+ reliable: true,
+ coordinateSpace: "viewBox"
+ };
+ };
+ const transformPoint = (point, matrix) => ({
+ x: matrix[0] * point.x + matrix[2] * point.y + matrix[4],
+ y: matrix[1] * point.x + matrix[3] * point.y + matrix[5]
+ });
+ const parseSvgPoints = (value2) => {
+ const numbers = String(value2 || "").trim().split(/[\s,]+/).filter(Boolean).map(Number);
+ const points = [];
+ for (let index = 0; index + 1 < numbers.length; index += 2) {
+ if (Number.isFinite(numbers[index]) && Number.isFinite(numbers[index + 1])) {
+ points.push({ x: numbers[index], y: numbers[index + 1] });
+ }
+ }
+ return points;
+ };
+ const svgStyleProperties = [
+ "fill",
+ "fill-opacity",
+ "fill-rule",
+ "stroke",
+ "stroke-width",
+ "stroke-opacity",
+ "stroke-linecap",
+ "stroke-linejoin",
+ "stroke-dasharray",
+ "stroke-dashoffset",
+ "opacity",
+ "color",
+ "vector-effect"
+ ];
+ const resolvedSvgStyle = (element2, property) => {
+ let current = element2;
+ while (current) {
+ const value2 = String(
+ view.getComputedStyle(current).getPropertyValue(property) || ""
+ ).trim();
+ if (value2 && !["inherit", "unset", "initial"].includes(value2)) return value2;
+ if (["opacity", "vector-effect"].includes(property)) break;
+ current = current.parentElement;
+ }
+ return "";
+ };
+ const inlineComputedSvgStyles = (original, clone2) => {
+ svgStyleProperties.forEach((property) => {
+ const value2 = resolvedSvgStyle(original, property);
+ if (value2) clone2.style.setProperty(property, value2);
+ });
+ const originalChildren = [...original.children];
+ const cloneChildren = [...clone2.children];
+ originalChildren.forEach((child, index) => {
+ if (cloneChildren[index]) inlineComputedSvgStyles(child, cloneChildren[index]);
+ });
+ };
+ const serializeSvgVisual = (svg, node) => {
+ const rootClone = svg.cloneNode(false);
+ rootClone.setAttribute("xmlns", "http://www.w3.org/2000/svg");
+ if (!rootClone.getAttribute("width")) rootClone.setAttribute("width", "100%");
+ if (!rootClone.getAttribute("height")) rootClone.setAttribute("height", "100%");
+ inlineComputedSvgStyles(svg, rootClone);
+ [...svg.querySelectorAll("defs")].forEach((defs) => {
+ const defsClone = defs.cloneNode(true);
+ inlineComputedSvgStyles(defs, defsClone);
+ rootClone.appendChild(defsClone);
+ });
+ const ancestors = [];
+ let current = node.parentElement;
+ while (current && current !== svg) {
+ ancestors.unshift(current);
+ current = current.parentElement;
+ }
+ let targetParent = rootClone;
+ ancestors.forEach((ancestor) => {
+ const ancestorClone = ancestor.cloneNode(false);
+ inlineComputedSvgStyles(ancestor, ancestorClone);
+ targetParent.appendChild(ancestorClone);
+ targetParent = ancestorClone;
+ });
+ const targetClone = node.cloneNode(true);
+ inlineComputedSvgStyles(node, targetClone);
+ targetClone.querySelectorAll?.("text").forEach((text2) => text2.remove());
+ targetParent.appendChild(targetClone);
+ return `data:image/svg+xml,${encodeURIComponent(rootClone.outerHTML)}`;
+ };
+ const boundsOfPoints = (points) => {
+ const xs = points.map((point) => point.x);
+ const ys = points.map((point) => point.y);
+ return {
+ left: Math.min(...xs),
+ top: Math.min(...ys),
+ width: Math.max(...xs) - Math.min(...xs),
+ height: Math.max(...ys) - Math.min(...ys)
+ };
+ };
+ const isDiamondPoints = (points) => {
+ if (points.length !== 4) return false;
+ const bounds = boundsOfPoints(points);
+ const cx2 = bounds.left + bounds.width / 2;
+ const cy2 = bounds.top + bounds.height / 2;
+ const tolerance = Math.max(bounds.width, bounds.height) * 0.08 + 0.01;
+ return points.every((point) => Math.abs(point.x - cx2) <= tolerance || Math.abs(point.y - cy2) <= tolerance);
+ };
+ const emitNativeSvg = (svg) => {
+ const svgRect = rectFor(svg);
+ if (svgRect.width <= 0 || svgRect.height <= 0) return;
+ const viewBox = String(svg.getAttribute("viewBox") || `0 0 ${svgRect.width} ${svgRect.height}`).trim().split(/[\s,]+/).map(Number);
+ const [vbX = 0, vbY = 0, vbW = svgRect.width, vbH = svgRect.height] = viewBox;
+ const xScale = svgRect.width / (vbW || svgRect.width);
+ const yScale = svgRect.height / (vbH || svgRect.height);
+ const fallbackLayers = slideDataFallbackLayers;
+ const toSlidePoint = (point) => ({
+ x: svgRect.left + (point.x - vbX) * xScale,
+ y: svgRect.top + (point.y - vbY) * yScale
+ });
+ const pushLine = (start, end, node, stroke2, width, coordinateSpace = "viewBox") => {
+ const toLinePoint = (point) => coordinateSpace === "viewport" ? { x: svgRect.left + point.x, y: svgRect.top + point.y } : toSlidePoint(point);
+ const first = toLinePoint(start);
+ const second = toLinePoint(end);
+ pushElement({
+ type: "line",
+ kind: "native",
+ x1: pxToInch(first.x),
+ y1: pxToInch(first.y),
+ x2: pxToInch(second.x),
+ y2: pxToInch(second.y),
+ color: stroke2 || "000000",
+ width
+ }, node);
+ };
+ const hasBrowserOnlySvg = Boolean(
+ svg.querySelector("filter,mask,foreignObject,use,pattern,textPath,clipPath,image")
+ );
+ const canSerializeSvgVisual = (node) => !svg.querySelector("script") && !/(?:href|src)\s*=\s*["']\s*(?:https?:)?\/\//i.test(node.outerHTML || "");
+ const pushSvgImageLayer = (node, code, message) => {
+ if (!canSerializeSvgVisual(node)) {
+ addDiagnostic(
+ "fallback",
+ "complex_svg_raster",
+ "Complex SVG cannot be safely serialized and requires local browser raster rendering.",
+ svg
+ );
+ return;
+ }
+ const paintMetadata = nextPaintMetadata(node);
+ fallbackLayers.push({
+ sourceId: paintMetadata.sourceId,
+ zIndex: readZIndex(node),
+ paintOrder: paintMetadata.paintOrder,
+ subOrder: paintMetadata.subOrder,
+ kind: "svg-image",
+ captureStrategy: "local-svg",
+ bbox: {
+ x: pxToInch(svgRect.left),
+ y: pxToInch(svgRect.top),
+ w: pxToInch(svgRect.width),
+ h: pxToInch(svgRect.height)
+ },
+ data: serializeSvgVisual(svg, node),
+ diagnostics: [{
+ severity: "fallback",
+ code,
+ message,
+ sourceId: node.dataset?.pptxSourceId || node.id || null
+ }]
+ });
+ };
+ svg.querySelectorAll("rect,circle,ellipse,line,polyline,polygon,text,path").forEach((node) => {
+ const tag = node.tagName.toLowerCase();
+ if (tag === "path") {
+ if (!hasBrowserOnlySvg) {
+ pushSvgImageLayer(
+ node,
+ "complex_svg_vector",
+ "Complex SVG geometry is preserved as a local movable vector image."
+ );
+ }
+ return;
+ }
+ const transform = transformForSvgNode(node, svg);
+ if (!transform.reliable) {
+ pushSvgImageLayer(
+ node,
+ "svg_transform_vector",
+ "SVG transform cannot be represented reliably as a native shape; preserving it as SVG."
+ );
+ return;
+ }
+ const transformedToSlidePoint = (transformed) => transform.coordinateSpace === "viewport" ? { x: svgRect.left + transformed.x, y: svgRect.top + transformed.y } : toSlidePoint(transformed);
+ const transformToSlidePoint = (point, matrix) => transformedToSlidePoint(transformPoint(point, matrix));
+ const mapPoint = (x, y) => transformToSlidePoint(
+ { x: svgNumber(x), y: svgNumber(y) },
+ transform.matrix
+ );
+ const mapLayoutPoint = (x, y) => transformToSlidePoint(
+ { x: svgNumber(x), y: svgNumber(y) },
+ transform.layoutMatrix
+ );
+ const fill2 = svgColor(node.getAttribute("fill") || view.getComputedStyle(node).fill);
+ const stroke2 = svgColor(node.getAttribute("stroke") || view.getComputedStyle(node).stroke);
+ const opacity = svgNumber(node.getAttribute("opacity") || view.getComputedStyle(node).opacity, 1);
+ const lineWidth = svgNumber(node.getAttribute("stroke-width"), 1) * 0.75;
+ const common = {
+ type: tag === "text" ? "svg-text" : "svg-shape",
+ kind: "native",
+ svgType: tag,
+ position: null,
+ shape: { fill: fill2, line: stroke2 ? { color: stroke2, width: lineWidth } : null, transparency: Math.round((1 - opacity) * 100), rectRadius: 0 }
+ };
+ if (tag === "rect") {
+ const x = svgNumber(node.getAttribute("x"));
+ const y = svgNumber(node.getAttribute("y"));
+ const width = svgNumber(node.getAttribute("width"));
+ const height = svgNumber(node.getAttribute("height"));
+ const points = [
+ mapPoint(x, y),
+ mapPoint(x + width, y),
+ mapPoint(x + width, y + height),
+ mapPoint(x, y + height)
+ ];
+ const bounds = boundsOfPoints(points);
+ const layoutBounds = boundsOfPoints([
+ mapLayoutPoint(x, y),
+ mapLayoutPoint(x + width, y),
+ mapLayoutPoint(x + width, y + height),
+ mapLayoutPoint(x, y + height)
+ ]);
+ common.position = { x: pxToInch(layoutBounds.left), y: pxToInch(layoutBounds.top), w: pxToInch(layoutBounds.width), h: pxToInch(layoutBounds.height) };
+ common.bbox = { x: pxToInch(bounds.left), y: pxToInch(bounds.top), w: pxToInch(bounds.width), h: pxToInch(bounds.height) };
+ if (transform.rotation) common.shape.rotate = transform.rotation;
+ } else if (tag === "circle" || tag === "ellipse") {
+ const rx = tag === "circle" ? svgNumber(node.getAttribute("r")) : svgNumber(node.getAttribute("rx"));
+ const ry = tag === "circle" ? rx : svgNumber(node.getAttribute("ry"));
+ const cx2 = svgNumber(node.getAttribute("cx"));
+ const cy2 = svgNumber(node.getAttribute("cy"));
+ const points = [
+ mapPoint(cx2 - rx, cy2),
+ mapPoint(cx2 + rx, cy2),
+ mapPoint(cx2, cy2 - ry),
+ mapPoint(cx2, cy2 + ry)
+ ];
+ const bounds = boundsOfPoints(points);
+ const layoutBounds = boundsOfPoints([
+ mapLayoutPoint(cx2 - rx, cy2),
+ mapLayoutPoint(cx2 + rx, cy2),
+ mapLayoutPoint(cx2, cy2 - ry),
+ mapLayoutPoint(cx2, cy2 + ry)
+ ]);
+ common.position = { x: pxToInch(layoutBounds.left), y: pxToInch(layoutBounds.top), w: pxToInch(layoutBounds.width), h: pxToInch(layoutBounds.height) };
+ common.bbox = { x: pxToInch(bounds.left), y: pxToInch(bounds.top), w: pxToInch(bounds.width), h: pxToInch(bounds.height) };
+ if (transform.rotation) common.shape.rotate = transform.rotation;
+ } else if (tag === "line") {
+ pushLine(
+ transformPoint({ x: svgNumber(node.getAttribute("x1")), y: svgNumber(node.getAttribute("y1")) }, transform.matrix),
+ transformPoint({ x: svgNumber(node.getAttribute("x2")), y: svgNumber(node.getAttribute("y2")) }, transform.matrix),
+ node,
+ stroke2,
+ lineWidth,
+ transform.coordinateSpace
+ );
+ return;
+ } else if (tag === "text") {
+ common.text = node.textContent || "";
+ const fontSize = svgNumber(node.getAttribute("font-size"), 16);
+ const origin = mapPoint(node.getAttribute("x"), svgNumber(node.getAttribute("y")) - fontSize);
+ common.position = { x: pxToInch(origin.x), y: pxToInch(origin.y), w: pxToInch(Math.max(1, svgRect.width)), h: pxToInch(Math.max(1, fontSize * yScale * 1.3)) };
+ common.style = { fontSize: svgNumber(node.getAttribute("font-size"), 16) * 0.75, fontFace: "Arial", color: fill2 || "000000", align: "left" };
+ if (transform.rotation) common.style.rotate = transform.rotation;
+ } else {
+ const points = parseSvgPoints(node.getAttribute("points")).map((point) => transformPoint(point, transform.matrix));
+ if (tag === "polygon" && (points.length === 3 || isDiamondPoints(points))) {
+ const slidePoints = points.map(transformedToSlidePoint);
+ const bounds = boundsOfPoints(slidePoints);
+ common.svgType = points.length === 3 ? "triangle" : "diamond";
+ common.position = {
+ x: pxToInch(bounds.left),
+ y: pxToInch(bounds.top),
+ w: pxToInch(bounds.width),
+ h: pxToInch(bounds.height)
+ };
+ common.bbox = { ...common.position };
+ if (transform.rotation) common.shape.rotate = transform.rotation;
+ } else {
+ const closed = tag === "polygon" && points.length > 2 ? [...points, points[0]] : points;
+ for (let index = 0; index + 1 < closed.length; index += 1) {
+ pushLine(
+ closed[index],
+ closed[index + 1],
+ node,
+ stroke2 || fill2,
+ lineWidth,
+ transform.coordinateSpace
+ );
+ }
+ if (tag === "polygon" && fill2 && points.length > 2) {
+ const sourceId = node.dataset?.pptxSourceId || node.id || null;
+ fallbackLayers.push({
+ sourceId,
+ zIndex: readZIndex(node),
+ paintOrder: domPaintOrder.get(sourceId) ?? unmappedPaintOrder++,
+ subOrder: -1,
+ kind: "svg-image",
+ bbox: {
+ x: pxToInch(svgRect.left),
+ y: pxToInch(svgRect.top),
+ w: pxToInch(svgRect.width),
+ h: pxToInch(svgRect.height)
+ },
+ data: serializeSvgVisual(svg, node),
+ diagnostics: [{
+ severity: "fallback",
+ code: "svg_polygon_fill",
+ message: "Polygon fill is preserved as a local SVG layer over an editable outline.",
+ sourceId: node.dataset?.pptxSourceId || node.id || null
+ }]
+ });
+ }
+ return;
+ }
+ }
+ if (common.position || common.type === "line") pushElement(common, node);
+ });
+ processed.add(svg);
+ svg.querySelectorAll("*").forEach((node) => processed.add(node));
+ };
document2.querySelectorAll("*").forEach((el) => {
if (processed.has(el)) return;
+ if (el.tagName === "svg") {
+ emitNativeSvg(el);
+ return;
+ }
if (el.tagName === "DIV" && el.dataset && el.dataset.pptxMerge === "true") {
const containerRect = rectFor(el);
if (containerRect.width === 0 || containerRect.height === 0) {
@@ -13029,18 +12096,23 @@ function extractSlideDataFromDocument(doc = document) {
return;
}
if (el.querySelector('[data-pptx-merge="true"]')) {
- errors.push(
- `data-pptx-merge container cannot contain another data-pptx-merge container. Nested merge is not supported.`
+ addDiagnostic(
+ "fallback",
+ "nested_merge_container",
+ "Nested data-pptx-merge containers require fallback handling.",
+ el
);
processed.add(el);
return;
}
const mergeComputed = view.getComputedStyle(el);
if (mergeComputed.backgroundImage && mergeComputed.backgroundImage !== "none") {
- errors.push(
- "Background images on data-pptx-merge container are not supported. Use solid colors or borders, or layer images via slide.addImage()."
+ addDiagnostic(
+ "fallback",
+ "merge_background_image",
+ "Background image on data-pptx-merge requires fallback rendering.",
+ el
);
- return;
}
const mHasBg = mergeComputed.backgroundColor && mergeComputed.backgroundColor !== "rgba(0, 0, 0, 0)";
const mBorders = [
@@ -13052,7 +12124,7 @@ function extractSlideDataFromDocument(doc = document) {
const mHasBorder = mBorders.some((b) => b > 0);
const mHasUniformBorder = mHasBorder && mBorders.every((b) => b === mBorders[0]);
if (mHasBg || mHasUniformBorder) {
- elements.push({
+ pushElement({
type: "shape",
text: "",
position: {
@@ -13082,12 +12154,15 @@ function extractSlideDataFromDocument(doc = document) {
})(),
shadow: parseBoxShadow(mergeComputed.boxShadow)
}
- });
+ }, el);
}
const textDescendants = Array.from(el.querySelectorAll("p, h1, h2, h3, h4, h5, h6"));
if (textDescendants.length === 0) {
- errors.push(
- `data-pptx-merge container has no / children to merge. Remove the data-pptx-merge attribute or add text elements.`
+ addDiagnostic(
+ "fallback",
+ "empty_merge_container",
+ "data-pptx-merge container has no semantic text children.",
+ el
);
processed.add(el);
return;
@@ -13151,7 +12226,7 @@ function extractSlideDataFromDocument(doc = document) {
processed.add(el);
return;
}
- elements.push({
+ pushElement({
type: "merged-text",
items: mergedRuns,
position: {
@@ -13161,7 +12236,7 @@ function extractSlideDataFromDocument(doc = document) {
h: pxToInch(containerRect.height)
},
style: baseStyle
- });
+ }, el);
processed.add(el);
return;
}
@@ -13221,8 +12296,11 @@ function extractSlideDataFromDocument(doc = document) {
if (el.classList && el.classList.contains("placeholder")) {
const rect = rectFor(el);
if (rect.width === 0 || rect.height === 0) {
- errors.push(
- `Placeholder "${el.id || "unnamed"}" has ${rect.width === 0 ? "width: 0" : "height: 0"}. Check the layout CSS.`
+ addDiagnostic(
+ "fallback",
+ "unmeasurable_placeholder",
+ `Placeholder "${el.id || "unnamed"}" has ${rect.width === 0 ? "width: 0" : "height: 0"}.`,
+ el
);
} else {
placeholders.push({
@@ -13239,7 +12317,7 @@ function extractSlideDataFromDocument(doc = document) {
if (el.tagName === "IMG") {
const rect = rectFor(el);
if (rect.width > 0 && rect.height > 0) {
- elements.push({
+ pushElement({
type: "image",
src: el.src,
position: {
@@ -13248,7 +12326,45 @@ function extractSlideDataFromDocument(doc = document) {
w: pxToInch(rect.width),
h: pxToInch(rect.height)
}
- });
+ }, el);
+ processed.add(el);
+ return;
+ }
+ }
+ if (el.tagName === "DIV") {
+ const computed = view.getComputedStyle(el);
+ const isZeroBox = svgNumber(computed.width) === 0 && svgNumber(computed.height) === 0;
+ const sides = ["Top", "Right", "Bottom", "Left"].map((side) => ({
+ side,
+ width: svgNumber(computed[`border${side}Width`]),
+ color: computed[`border${side}Color`]
+ }));
+ const opaqueSides = sides.filter((side) => side.width > 0 && !isTransparentBg(side.color));
+ if (isZeroBox && opaqueSides.length === 1 && sides.filter((side) => side.width > 0).length >= 3) {
+ const active = opaqueSides[0];
+ const rect = rectFor(el);
+ const horizontal = sides.find((side) => side.side === "Left").width + sides.find((side) => side.side === "Right").width;
+ const vertical = sides.find((side) => side.side === "Top").width + sides.find((side) => side.side === "Bottom").width;
+ const rotations = { Bottom: 0, Left: 90, Top: 180, Right: 270 };
+ pushElement({
+ type: "svg-shape",
+ svgType: "triangle",
+ kind: "native",
+ text: "",
+ position: {
+ x: pxToInch(rect.left - sides.find((side) => side.side === "Left").width),
+ y: pxToInch(rect.top - sides.find((side) => side.side === "Top").width),
+ w: pxToInch(Math.max(1, horizontal)),
+ h: pxToInch(Math.max(1, vertical))
+ },
+ shape: {
+ fill: rgbToHex2(active.color),
+ line: null,
+ transparency: extractAlpha(active.color),
+ rectRadius: 0,
+ rotate: rotations[active.side]
+ }
+ }, el);
processed.add(el);
return;
}
@@ -13259,10 +12375,12 @@ function extractSlideDataFromDocument(doc = document) {
const hasBg = computed.backgroundColor && computed.backgroundColor !== "rgba(0, 0, 0, 0)";
const bgImage = computed.backgroundImage;
if (bgImage && bgImage !== "none") {
- errors.push(
- "Background images on DIV elements are not supported. Use solid colors or borders for shapes, or use slide.addImage() in PptxGenJS to layer images."
+ addDiagnostic(
+ "fallback",
+ "container_background_image",
+ "Container background image requires fallback rendering.",
+ el
);
- return;
}
const borderTop = computed.borderTopWidth;
const borderRight = computed.borderRightWidth;
@@ -13461,46 +12579,528 @@ function extractSlideDataFromDocument(doc = document) {
if (!textTags.includes(el.tagName)) return;
emitTextElement(el);
});
- const paintRank = (type) => {
- if (type === "shape") return 0;
- if (type === "line") return 1;
- if (type === "image") return 2;
- return 3;
- };
elements.sort((a, b) => {
const z = (a.zIndex ?? 0) - (b.zIndex ?? 0);
if (z !== 0) return z;
- return paintRank(a.type) - paintRank(b.type);
+ const paint = (a.paintOrder ?? 0) - (b.paintOrder ?? 0);
+ if (paint !== 0) return paint;
+ const sub = (a.subOrder ?? 0) - (b.subOrder ?? 0);
+ if (sub !== 0) return sub;
+ return (a.stableOrder ?? 0) - (b.stableOrder ?? 0);
+ });
+ document2.querySelectorAll("*").forEach((element2) => {
+ const computed = view.getComputedStyle(element2);
+ const filter = String(computed.filter || element2.style?.filter || "");
+ if (filter && filter !== "none") {
+ addDiagnostic("fallback", "css_filter", "CSS filter requires fallback rendering.", element2);
+ }
+ if (String(element2.tagName).toUpperCase() === "SVG") {
+ if (element2.querySelector("filter,mask,foreignObject,use,pattern,textPath,clipPath,image")) {
+ addDiagnostic(
+ "fallback",
+ "complex_svg_raster",
+ "SVG filter, mask, or foreignObject requires local browser raster rendering.",
+ element2
+ );
+ } else if (element2.querySelector("path")) {
+ addDiagnostic(
+ "fallback",
+ "complex_svg_vector",
+ "Complex SVG geometry is preserved as a local SVG image.",
+ element2
+ );
+ }
+ }
+ });
+ const blockingErrors = diagnostics.filter((diagnostic) => diagnostic.severity === "blocking").map((diagnostic) => diagnostic.message);
+ slideDataFallbackLayers.sort((a, b) => {
+ const z = (a.zIndex ?? 0) - (b.zIndex ?? 0);
+ if (z !== 0) return z;
+ const order = (a.paintOrder ?? 0) - (b.paintOrder ?? 0);
+ if (order !== 0) return order;
+ const sub = (a.subOrder ?? 0) - (b.subOrder ?? 0);
+ if (sub !== 0) return sub;
+ return (a.stableOrder ?? 0) - (b.stableOrder ?? 0);
});
- return { background, elements, placeholders, errors };
+ return {
+ background,
+ elements,
+ fallbackLayers: slideDataFallbackLayers,
+ placeholders,
+ diagnostics,
+ errors: blockingErrors
+ };
}
-// src/export-slide-browser.js
-var EXPORT_VIEWPORT = { width: 1280, height: 720 };
-var RASTER_TEXT_TYPES = /* @__PURE__ */ new Set(["p", "h1", "h2", "h3", "h4", "h5", "h6", "text", "list", "merged-text"]);
-function countVectorTextElements(slideData) {
- return (slideData?.elements || []).filter((el) => RASTER_TEXT_TYPES.has(el.type)).length;
-}
-function slideHtmlForRasterBackdrop(html) {
- const markup = normalizeSlideDocument(html);
- if (markup.includes('data-pptx-raster="1"') && markup.includes("pptx-raster-hide-text")) {
- return markup;
+// src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/fallback-layer-render.js
+var EXPORT_WIDTH = 1280;
+var EXPORT_HEIGHT = 720;
+var LOCAL_RASTER_CODES = /* @__PURE__ */ new Set([
+ "css_gradient",
+ "css_filter",
+ "computed_gradient",
+ "generated_content",
+ "container_background_image",
+ "merge_background_image",
+ "complex_svg_raster"
+]);
+function rasterLayerZIndex(element2, view) {
+ let current = element2;
+ while (current) {
+ const raw = view.getComputedStyle(current).zIndex;
+ if (raw && raw !== "auto") {
+ const parsed = parseInt(raw, 10);
+ if (Number.isFinite(parsed)) return parsed;
+ }
+ current = current.parentElement;
}
- const hideCss = `body[data-pptx-raster="1"], body[data-pptx-raster="1"] * {
+ return 0;
+}
+function escapedAttributeValue(value2) {
+ return String(value2).replace(/["\\]/g, "\\$&");
+}
+function serializeRasterTargetDocument(doc, targetSpecs) {
+ const targets = (Array.isArray(targetSpecs) ? targetSpecs : [targetSpecs]).map((target) => typeof target === "string" ? { sourceId: target, captureStrategy: "visual-subtree" } : target);
+ const sourceRoot = doc._exportRoot || doc.documentElement;
+ const sourceBody = doc.body;
+ const bodyClone = sourceBody.cloneNode(true);
+ let targetCount = 0;
+ targets.forEach(({ sourceId, captureStrategy }) => {
+ const target = bodyClone.querySelector(
+ `[data-pptx-source-id="${escapedAttributeValue(sourceId)}"]`
+ ) || (bodyClone.dataset?.pptxSourceId === sourceId ? bodyClone : null);
+ if (!target) return;
+ target.setAttribute("data-pptx-raster-target", "1");
+ target.setAttribute("data-pptx-capture-strategy", captureStrategy);
+ targetCount += 1;
+ });
+ if (!targetCount) return null;
+ const authorStyles = [...sourceRoot.querySelectorAll("style")].map((style) => style.textContent || "").join("\n");
+ const bodyAttributes = [...bodyClone.attributes].filter((attribute) => !["class", "style"].includes(attribute.name)).map((attribute) => ` ${attribute.name}="${String(attribute.value).replace(/"/g, """)}"`).join("");
+ const isolationCss = `
+html, body, .ppt-export-root, .ppt-export-body {
+ margin: 0 !important;
+ padding: 0 !important;
+ width: ${EXPORT_WIDTH}px !important;
+ height: ${EXPORT_HEIGHT}px !important;
+ overflow: hidden !important;
+ background: transparent !important;
+ background-color: transparent !important;
+}
+.ppt-export-body [data-pptx-source-id] {
+ visibility: hidden !important;
+}
+.ppt-export-body [data-pptx-raster-target="1"],
+.ppt-export-body [data-pptx-capture-strategy="visual-subtree"] * {
+ visibility: visible !important;
+}
+.ppt-export-body [data-pptx-capture-strategy="self-decoration"] > *,
+.ppt-export-body [data-pptx-capture-strategy="pseudo-only"] > * {
+ visibility: hidden !important;
+}
+.ppt-export-body [data-pptx-capture-strategy="pseudo-only"] {
+ background: none !important;
+ background-image: none !important;
+ border-color: transparent !important;
+ box-shadow: none !important;
+ filter: none !important;
+}
+.ppt-export-body [data-pptx-raster-target="1"] :is(p,h1,h2,h3,h4,h5,h6,li,span,a,small,label,code,b,strong,i,em,u,mark,sub,sup),
+.ppt-export-body [data-pptx-raster-target="1"]:is(p,h1,h2,h3,h4,h5,h6,li,span,a,small,label,code,b,strong,i,em,u,mark,sub,sup) {
color: transparent !important;
-webkit-text-fill-color: transparent !important;
text-shadow: none !important;
}
-body[data-pptx-raster="1"] ::marker {
+.ppt-export-body [data-pptx-raster-target="1"] ::marker {
color: transparent !important;
-webkit-text-fill-color: transparent !important;
-}`;
- const styleTag = ``;
- if (/<\/head>/i.test(markup)) {
- return markup.replace(/<\/head>/i, `${styleTag}`).replace(/`;
+}
+function buildRasterFallbackRequests(doc, diagnostics = []) {
+ const bodyRect = doc.body.getBoundingClientRect();
+ const view = doc.defaultView || globalThis.window;
+ const sourceOrder = buildDomPaintOrderMap(doc);
+ const grouped = /* @__PURE__ */ new Map();
+ diagnostics.forEach((diagnostic) => {
+ if (!LOCAL_RASTER_CODES.has(diagnostic.code) || !diagnostic.sourceId) return;
+ const captureStrategy = diagnostic.code === "generated_content" ? "pseudo-only" : ["css_filter", "complex_svg_raster"].includes(diagnostic.code) ? "visual-subtree" : "self-decoration";
+ const key = `${diagnostic.sourceId}:${captureStrategy}`;
+ if (!grouped.has(key)) {
+ grouped.set(key, {
+ sourceId: diagnostic.sourceId,
+ captureStrategy,
+ diagnostics: []
+ });
+ }
+ grouped.get(key).diagnostics.push(diagnostic);
+ });
+ const requests = [];
+ grouped.forEach(({ sourceId, captureStrategy, diagnostics: sourceDiagnostics }) => {
+ const element2 = doc.body.dataset?.pptxSourceId === sourceId ? doc.body : doc.body.querySelector(
+ `[data-pptx-source-id="${escapedAttributeValue(sourceId)}"]`
+ );
+ const failureRequest = (code, reason, details = {}) => ({
+ sourceId,
+ zIndex: details.zIndex ?? 0,
+ paintOrder: sourceOrder.get(sourceId) ?? requests.length,
+ subOrder: 0,
+ kind: "raster",
+ phase: "local-visual",
+ canvas: "full-page",
+ captureStrategy,
+ suppressedNativeVisualIds: [],
+ bbox: details.bbox || { x: 0, y: 0, w: 0, h: 0 },
+ diagnostics: sourceDiagnostics,
+ buildFailure: {
+ code,
+ stage: "request-build",
+ reason
+ }
+ });
+ if (!element2) {
+ requests.push(failureRequest(
+ "local_raster_target_missing",
+ `Raster fallback source "${sourceId}" is not present in the export document.`
+ ));
+ return;
+ }
+ let rect;
+ try {
+ rect = element2.getBoundingClientRect();
+ } catch (error2) {
+ requests.push(failureRequest(
+ "local_raster_serialize_failed",
+ `Raster fallback source "${sourceId}" could not be measured: ${String(error2?.message || error2)}`,
+ { zIndex: rasterLayerZIndex(element2, view) }
+ ));
+ return;
+ }
+ const bbox = {
+ x: (rect.left - bodyRect.left) / 96,
+ y: (rect.top - bodyRect.top) / 96,
+ w: rect.width / 96,
+ h: rect.height / 96
+ };
+ if (!(rect.width > 0) || !(rect.height > 0)) {
+ requests.push(failureRequest(
+ "local_raster_unmeasurable",
+ `Raster fallback source "${sourceId}" has no measurable width or height.`,
+ { zIndex: rasterLayerZIndex(element2, view), bbox }
+ ));
+ return;
+ }
+ try {
+ if (!serializeRasterTargetDocument(doc, { sourceId, captureStrategy })) {
+ throw new Error("Raster target was not retained in the export document.");
+ }
+ } catch (error2) {
+ requests.push(failureRequest(
+ "local_raster_serialize_failed",
+ `Raster fallback source "${sourceId}" could not be serialized: ${String(error2?.message || error2)}`,
+ { zIndex: rasterLayerZIndex(element2, view), bbox }
+ ));
+ return;
+ }
+ const suppressedNativeVisualIds = captureStrategy === "pseudo-only" ? [] : captureStrategy === "self-decoration" ? [sourceId] : [element2, ...element2.querySelectorAll("[data-pptx-source-id]")].map((item) => item.dataset?.pptxSourceId).filter(Boolean);
+ requests.push({
+ sourceId,
+ zIndex: rasterLayerZIndex(element2, view),
+ paintOrder: sourceOrder.get(sourceId) ?? requests.length,
+ subOrder: 0,
+ kind: "raster",
+ phase: "local-visual",
+ canvas: "full-page",
+ captureStrategy,
+ suppressedNativeVisualIds: [...new Set(suppressedNativeVisualIds)],
+ bbox,
+ buildHtml: () => serializeRasterTargetDocument(doc, { sourceId, captureStrategy }),
+ diagnostics: sourceDiagnostics
+ });
+ });
+ return requests.sort((a, b) => a.zIndex - b.zIndex || a.paintOrder - b.paintOrder);
+}
+function buildPageVisualFallbackRequest(doc, localRequests = []) {
+ const sourceIds = [...new Set(localRequests.map((request2) => request2.sourceId).filter(Boolean))];
+ if (!sourceIds.length) return null;
+ const targetSpecs = localRequests.map((request2) => ({
+ sourceId: request2.sourceId,
+ captureStrategy: request2.captureStrategy
+ }));
+ const request = {
+ sourceId: "slide-visuals",
+ sourceIds,
+ captureStrategy: "page-visual",
+ suppressedNativeVisualIds: [...new Set(
+ localRequests.flatMap((request2) => request2.suppressedNativeVisualIds || [])
+ )],
+ zIndex: Math.min(...localRequests.map((request2) => request2.zIndex ?? 0)),
+ paintOrder: Math.min(...localRequests.map((request2) => request2.paintOrder ?? 0)),
+ subOrder: Math.min(...localRequests.map((request2) => request2.subOrder ?? 0)),
+ kind: "raster",
+ phase: "page-visual",
+ canvas: "full-page",
+ bbox: { x: 0, y: 0, w: 13.333, h: 7.5 },
+ diagnostics: localRequests.flatMap((request2) => request2.diagnostics || [])
+ };
+ const missingSourceIds = localRequests.filter((localRequest) => localRequest.buildFailure?.code === "local_raster_target_missing").map((localRequest) => localRequest.sourceId);
+ if (missingSourceIds.length) {
+ return {
+ ...request,
+ buildFailure: {
+ code: "page_visual_target_missing",
+ stage: "request-build",
+ reason: `Page-visual fallback cannot cover missing sources: ${missingSourceIds.join(", ")}.`
+ }
+ };
+ }
+ try {
+ if (!serializeRasterTargetDocument(doc, targetSpecs)) {
+ return {
+ ...request,
+ buildFailure: {
+ code: "page_visual_target_missing",
+ stage: "request-build",
+ reason: `Page-visual fallback could not locate any requested sources: ${sourceIds.join(", ")}.`
+ }
+ };
+ }
+ return {
+ ...request,
+ buildHtml: () => serializeRasterTargetDocument(doc, targetSpecs)
+ };
+ } catch (error2) {
+ return {
+ ...request,
+ buildFailure: {
+ code: "page_visual_serialize_failed",
+ stage: "request-build",
+ reason: `Page-visual fallback could not be serialized: ${String(error2?.message || error2)}`
+ }
+ };
+ }
+}
+function buildWholePageVisualFallbackRequest(doc, diagnostics = [], suppressedNativeVisualIds = []) {
+ const bodySourceId = doc?.body?.dataset?.pptxSourceId;
+ if (!bodySourceId) {
+ return {
+ sourceId: "slide-visuals",
+ phase: "page-visual",
+ kind: "raster",
+ bbox: { x: 0, y: 0, w: 13.333, h: 7.5 },
+ diagnostics,
+ suppressedNativeVisualIds: [...new Set(suppressedNativeVisualIds)],
+ buildFailure: {
+ code: "page_visual_target_missing",
+ stage: "request-build",
+ reason: "Whole-page visual source is unavailable."
+ }
+ };
+ }
+ const request = buildPageVisualFallbackRequest(doc, [{
+ sourceId: bodySourceId,
+ captureStrategy: "visual-subtree",
+ suppressedNativeVisualIds: [...new Set(suppressedNativeVisualIds)],
+ zIndex: 0,
+ paintOrder: 0,
+ subOrder: 0,
+ diagnostics
+ }]);
+ return {
+ ...request,
+ sourceId: "slide-visuals",
+ sourceIds: [bodySourceId],
+ captureStrategy: "whole-page-visual"
+ };
+}
+async function renderRasterFallbackLayers(requests, renderRaster, slideIndex) {
+ const layers = [];
+ const failures = [];
+ if (typeof renderRaster !== "function") {
+ return { layers, failures: [...requests] };
+ }
+ for (const request of requests) {
+ if (request.buildFailure) {
+ failures.push({
+ ...request,
+ error: request.buildFailure.reason
+ });
+ continue;
+ }
+ try {
+ const html = typeof request.buildHtml === "function" ? request.buildHtml() : request.html;
+ if (!html) throw new Error("Raster fallback HTML could not be generated");
+ const rendered = await renderRaster(html, slideIndex, {
+ sourceId: request.sourceId,
+ bbox: request.bbox,
+ zIndex: request.zIndex,
+ paintOrder: request.paintOrder,
+ subOrder: request.subOrder,
+ phase: request.phase,
+ captureStrategy: request.captureStrategy,
+ suppressedNativeVisualIds: request.suppressedNativeVisualIds || []
+ });
+ const raw = String(rendered || "").replace(/^data:.*;base64,/, "");
+ if (!raw) throw new Error("Raster renderer returned no PNG data");
+ layers.push({
+ ...request,
+ buildHtml: void 0,
+ html: void 0,
+ data: `data:image/png;base64,${raw}`
+ });
+ } catch (error2) {
+ failures.push({ ...request, error: String(error2?.message || error2) });
+ }
+ }
+ return { layers, failures };
+}
+function failureDiagnostic(code, failure, slideIndex, severity = "fallback") {
+ const reason = failure.buildFailure?.reason || failure.error;
+ return {
+ severity,
+ kind: severity === "blocking" ? "blocking" : void 0,
+ code: failure.buildFailure?.code || code,
+ message: `${failure.phase || "raster"} fallback failed for ${failure.sourceId || "slide visual"}: ${reason}`,
+ sourceId: failure.sourceId || null,
+ slideNumber: slideIndex + 1,
+ phase: failure.phase || null,
+ stage: failure.buildFailure?.stage || "render",
+ reason
+ };
+}
+async function renderRasterFallbackPlan(plan, renderRaster, slideIndex) {
+ const diagnostics = [];
+ if (plan.pageVisualRequest?.captureStrategy === "whole-page-visual") {
+ const pageResult = await renderRasterFallbackLayers(
+ [plan.pageVisualRequest],
+ renderRaster,
+ slideIndex
+ );
+ if (pageResult.layers.length) {
+ return {
+ layers: pageResult.layers,
+ fullPageFallback: null,
+ diagnostics: [{
+ severity: "fallback",
+ code: "page_visual_fallback",
+ message: `Slide ${slideIndex + 1} used a page visual fallback.`,
+ sourceId: "slide-visuals",
+ slideNumber: slideIndex + 1,
+ phase: "page-visual"
+ }],
+ blocking: false
+ };
+ }
+ diagnostics.push(...pageResult.failures.map((failure) => failureDiagnostic("page_visual_raster_failed", failure, slideIndex)));
+ const fullResult = await renderRasterFallbackLayers(
+ plan.fullPageRequest ? [plan.fullPageRequest] : [],
+ renderRaster,
+ slideIndex
+ );
+ if (fullResult.layers.length) {
+ return {
+ layers: [],
+ fullPageFallback: fullResult.layers[0],
+ diagnostics: [
+ ...diagnostics,
+ {
+ severity: "fallback",
+ code: "full_page_fallback",
+ message: `Slide ${slideIndex + 1} used a full-page fallback.`,
+ sourceId: fullResult.layers[0].sourceId,
+ slideNumber: slideIndex + 1,
+ phase: "full-page"
+ }
+ ],
+ blocking: false
+ };
+ }
+ diagnostics.push(...fullResult.failures.map((failure) => failureDiagnostic("full_page_raster_failed", failure, slideIndex, "blocking")));
+ return { layers: [], fullPageFallback: null, diagnostics, blocking: true };
+ }
+ const localResult = await renderRasterFallbackLayers(
+ plan.localRequests || [],
+ renderRaster,
+ slideIndex
+ );
+ if (!localResult.failures.length) {
+ return {
+ layers: localResult.layers,
+ fullPageFallback: null,
+ diagnostics,
+ blocking: false
+ };
+ }
+ diagnostics.push(...localResult.failures.map((failure) => failureDiagnostic("local_raster_failed", failure, slideIndex)));
+ if (plan.pageVisualRequest) {
+ const pageResult = await renderRasterFallbackLayers(
+ [plan.pageVisualRequest],
+ renderRaster,
+ slideIndex
+ );
+ if (pageResult.layers.length) {
+ const layer = pageResult.layers[0];
+ diagnostics.push({
+ severity: "fallback",
+ code: "page_visual_fallback",
+ message: `Slide ${slideIndex + 1} used a transparent page visual fallback.`,
+ sourceId: layer.sourceId,
+ slideNumber: slideIndex + 1,
+ phase: "page-visual",
+ reason: "One or more local visual layers could not be rendered."
+ });
+ return {
+ layers: [layer],
+ fullPageFallback: null,
+ diagnostics,
+ blocking: false
+ };
+ }
+ diagnostics.push(...pageResult.failures.map((failure) => failureDiagnostic("page_visual_raster_failed", failure, slideIndex)));
+ }
+ if (plan.fullPageRequest) {
+ const fullResult = await renderRasterFallbackLayers(
+ [plan.fullPageRequest],
+ renderRaster,
+ slideIndex
+ );
+ if (fullResult.layers.length) {
+ const fullPageFallback = fullResult.layers[0];
+ diagnostics.push({
+ severity: "fallback",
+ code: "full_page_fallback",
+ message: `Slide ${slideIndex + 1} was exported as a full-page PNG fallback.`,
+ sourceId: fullPageFallback.sourceId,
+ slideNumber: slideIndex + 1,
+ phase: "full-page",
+ reason: "Local and transparent page visual fallback rendering failed."
+ });
+ return {
+ layers: [],
+ fullPageFallback,
+ diagnostics,
+ blocking: false
+ };
+ }
+ diagnostics.push(...fullResult.failures.map((failure) => failureDiagnostic("full_page_raster_failed", failure, slideIndex, "blocking")));
}
- return `${styleTag}${markup.replace(/ root.querySelector(sel),
querySelectorAll: (sel) => root.querySelectorAll(sel),
createElement: (tag) => document.createElement(tag),
+ createTreeWalker: (...args) => document.createTreeWalker(...args),
getElementById: (id) => root.querySelector(`#${id}`),
head: root.querySelector("style")?.parentElement || root,
- _exportRoot: root
+ _exportRoot: root,
+ _pptxSecurityDiagnostics: body._pptxSecurityDiagnostics || []
};
}
function createExportRoot() {
@@ -13575,7 +13178,7 @@ async function waitForExportPaint() {
});
}
function mountMarkupOnRoot(root, markup) {
- const parsed = new DOMParser().parseFromString(markup, "text/html");
+ const parsed = sanitizeSlideDocument(new DOMParser().parseFromString(markup, "text/html"));
root.replaceChildren();
parsed.querySelectorAll("style").forEach((node) => {
const style = document.createElement("style");
@@ -13583,6 +13186,7 @@ function mountMarkupOnRoot(root, markup) {
root.appendChild(style);
});
const body = document.createElement("div");
+ body._pptxSecurityDiagnostics = parsed._pptxSecurityDiagnostics || [];
body.className = "ppt-export-body";
if (parsed.body) {
for (const attr of parsed.body.attributes) {
@@ -13613,25 +13217,130 @@ async function loadHtmlInExportRoot(html) {
await waitForExportPaint();
return wrapExportDocument(root, body);
}
+function analyzeMountedSlideForPptx(doc, source = "") {
+ if (!doc?.body) {
+ return {
+ valid: false,
+ issues: [{
+ severity: "blocking",
+ kind: "blocking",
+ code: "unreadable_document",
+ message: "The slide document could not be read.",
+ sourceId: "slide-document"
+ }]
+ };
+ }
+ const issues = [...doc._pptxSecurityDiagnostics || []];
+ const seen = new Set(issues.map((item) => `${item.code}:${item.sourceId || ""}`));
+ const add = (code, message, element2 = null, severity = "fallback") => {
+ const sourceId = element2?.dataset?.pptxSourceId || element2?.id || null;
+ const key = `${code}:${sourceId || ""}`;
+ if (seen.has(key)) return;
+ seen.add(key);
+ issues.push({
+ severity,
+ kind: severity === "blocking" ? "blocking" : void 0,
+ code,
+ message,
+ sourceId,
+ tag: element2?.tagName?.toLowerCase?.() || null
+ });
+ };
+ const body = doc.body;
+ if (body.querySelector('script,iframe,object,embed,base,meta[http-equiv="refresh" i],foreignObject,maction')) {
+ add("active_content_residual", "Active content remained after sanitization.", body, "blocking");
+ }
+ if (!String(source || "").trim() || !/<\/html>\s*$/i.test(String(source || "").trim())) {
+ add("incomplete_html", "The slide document is incomplete.", body, "blocking");
+ }
+ let bodyRect;
+ try {
+ bodyRect = body.getBoundingClientRect();
+ if (!(bodyRect.width > 0) || !(bodyRect.height > 0)) {
+ add("unmeasurable_canvas", "The slide canvas could not be measured.", body, "blocking");
+ }
+ } catch {
+ add("unmeasurable_canvas", "The slide canvas could not be measured.", body, "blocking");
+ }
+ if (bodyRect) {
+ if (Math.abs(bodyRect.width - EXPORT_VIEWPORT.width) > 2 || Math.abs(bodyRect.height - EXPORT_VIEWPORT.height) > 2) {
+ add("canvas_size", "The slide canvas size requires page visual fallback.", body);
+ }
+ const dimensions = measureBodyDimensions(doc);
+ if (dimensions.errors?.length) {
+ add("canvas_overflow", "Slide content exceeds the canvas.", body);
+ }
+ const view = doc.defaultView || window;
+ body.querySelectorAll("p,h1,h2,h3,h4,h5,h6,li").forEach((element2) => {
+ const rect = element2.getBoundingClientRect();
+ if (rect.width <= 0 || rect.height <= 0) return;
+ if (rect.left < bodyRect.left - 1 || rect.top < bodyRect.top - 1 || rect.right > bodyRect.right + 1 || rect.bottom > bodyRect.bottom + 1) {
+ add("text_out_of_bounds", "Text extends outside the slide canvas.", element2);
+ }
+ const computed = view.getComputedStyle(element2);
+ if (parseFloat(computed.fontSize || 0) > 12 && rect.bottom > bodyRect.bottom - 48) {
+ add("bottom_safety_margin", "Text enters the bottom safety margin.", element2);
+ }
+ });
+ }
+ return { valid: issues.length === 0, issues: issues.slice(0, 32) };
+}
async function prepareSlideOnce(html, aggressive, options = {}) {
let exportRoot = null;
try {
const doc = await loadHtmlInExportRoot(html);
exportRoot = doc._exportRoot;
- sanitizeSlideDocumentRoot(doc, aggressive);
+ const repairResult = sanitizeSlideDocumentRoot(doc, aggressive);
await waitForExportPaint();
const bodyDimensions = measureBodyDimensions(doc);
const slideData = extractSlideDataFromDocument(doc);
+ const analysis = analyzeMountedSlideForPptx(doc, html);
+ const mergedDiagnostics = [
+ ...analysis.issues || [],
+ ...repairResult?.diagnostics || [],
+ ...slideData.diagnostics || []
+ ];
+ const diagnosticKeys = /* @__PURE__ */ new Set();
+ const diagnostics = mergedDiagnostics.filter((diagnostic) => {
+ const key = `${diagnostic.severity}:${diagnostic.code}:${diagnostic.sourceId || ""}`;
+ if (diagnosticKeys.has(key)) return false;
+ diagnosticKeys.add(key);
+ return true;
+ });
+ slideData.diagnostics = diagnostics;
+ const rasterRequests = buildRasterFallbackRequests(doc, diagnostics);
+ const pageFallbackCodes = /* @__PURE__ */ new Set([
+ "canvas_size",
+ "canvas_overflow",
+ "text_out_of_bounds",
+ "bottom_safety_margin"
+ ]);
+ const pageFallbackDiagnostics = diagnostics.filter((item) => pageFallbackCodes.has(item.code));
+ const nativeVisualSourceIds = [...new Set(
+ (slideData.elements || []).filter((element2) => !EDITABLE_TEXT_TYPES.has(element2.type)).map((element2) => element2.sourceId).filter(Boolean)
+ )];
+ const pageVisualRequest = pageFallbackDiagnostics.length ? buildWholePageVisualFallbackRequest(
+ doc,
+ pageFallbackDiagnostics,
+ nativeVisualSourceIds
+ ) : buildPageVisualFallbackRequest(doc, rasterRequests);
const overflowWarnings = bodyDimensions.errors || [];
- if (overflowWarnings.length) {
- console.warn("[ppt-live-export] slide overflows canvas; exporting anyway:", overflowWarnings.join("; "));
- }
const safeBodyDimensions = { ...bodyDimensions, errors: [] };
- const errors = slideData.errors || [];
- if (!errors.length || options.allowValidationErrors) {
- return { slideData, bodyDimensions: safeBodyDimensions, aggressive, warnings: overflowWarnings };
+ const blocking = diagnostics.filter((diagnostic) => diagnostic.severity === "blocking");
+ if (!blocking.length || options.allowValidationErrors) {
+ return {
+ slideData,
+ bodyDimensions: safeBodyDimensions,
+ diagnostics,
+ rasterRequests,
+ pageVisualRequest,
+ aggressive,
+ warnings: overflowWarnings
+ };
}
- return { error: new Error(errors.join("\n")) };
+ const error2 = new Error(blocking.map((diagnostic) => diagnostic.message).join("\n"));
+ error2.diagnostics = blocking;
+ return { error: error2 };
} finally {
if (exportRoot) removeExportRoot(exportRoot);
}
@@ -13649,27 +13358,45 @@ async function prepareSlidesForPptxExport(slides, options = {}) {
for (const [index, slide] of slides.entries()) {
if (!slide?.html) continue;
const item = await prepareSlideForPptxExport(slide.html, options);
- let rasterBase64 = null;
- const vectorTextCount = countVectorTextElements(item.slideData);
- const rasterOnly = vectorTextCount === 0;
- if (typeof options.renderRaster === "function") {
- try {
- if (typeof options.onRasterProgress === "function") {
- options.onRasterProgress(index, slide);
- }
- const rasterHtml = rasterOnly ? slideExportHtml(slide) : slideHtmlForRasterBackdrop(slide.html);
- rasterBase64 = await options.renderRaster(rasterHtml, index);
- } catch {
- rasterBase64 = null;
- }
+ if (item.rasterRequests?.length && typeof options.onRasterProgress === "function") {
+ options.onRasterProgress(index, slide);
+ }
+ const rasterResult = await renderRasterFallbackPlan({
+ localRequests: item.rasterRequests || [],
+ pageVisualRequest: item.pageVisualRequest,
+ fullPageRequest: {
+ sourceId: `slide-${index + 1}`,
+ zIndex: 0,
+ paintOrder: 0,
+ kind: "raster",
+ phase: "full-page",
+ bbox: { x: 0, y: 0, w: 13.333, h: 7.5 },
+ buildHtml: () => slideExportHtml(slide),
+ diagnostics: []
+ }
+ }, options.renderRaster, index);
+ if (rasterResult.blocking) {
+ const error2 = new Error(
+ rasterResult.diagnostics.map((diagnostic) => diagnostic.message).join("\n") || `Slide ${index + 1} fallback rendering failed`
+ );
+ error2.diagnostics = rasterResult.diagnostics;
+ throw error2;
}
+ item.slideData.fallbackLayers = [
+ ...item.slideData.fallbackLayers || [],
+ ...rasterResult.layers
+ ];
+ item.slideData.fullPageFallback = rasterResult.fullPageFallback;
+ item.slideData.diagnostics = [
+ ...item.slideData.diagnostics || [],
+ ...rasterResult.diagnostics
+ ];
prepared.push({
index,
slideId: slide.id,
notes: slide,
...item,
- rasterBase64,
- rasterOnly: Boolean(rasterBase64 && rasterOnly)
+ fallbackDiagnostics: rasterResult.diagnostics
});
}
return prepared;
@@ -13677,53 +13404,12 @@ async function prepareSlidesForPptxExport(slides, options = {}) {
clearExportSessionHost();
}
}
-function buildElementSlideHtml(slide = {}) {
- const theme = slide.theme || {};
- const title = String(slide.title || "Slide").replace(/[<>&]/g, (ch) => ({
- "<": "<",
- ">": ">",
- "&": "&"
- })[ch] || ch);
- const subtitle = String(slide.subtitle || slide.claim || "").replace(/[<>&]/g, (ch) => ({
- "<": "<",
- ">": ">",
- "&": "&"
- })[ch] || ch);
- const background = theme.background || "#ffffff";
- const ink = theme.ink || "#111111";
- const muted = theme.muted || "#666666";
- return `
-
-
-
-
-
-
- ${title}
- ${subtitle ? `${subtitle}
` : ""}
-
-`;
-}
function slideExportHtml(slide) {
- if (slide?.html) return normalizeSlideDocument(slide.html);
- return buildElementSlideHtml(slide);
+ if (slide?.html) return sanitizeSlideMarkup(normalizeSlideDocument(slide.html));
+ return sanitizeSlideMarkup(buildElementSlideHtml(slide));
}
-// ../../../../../../../../../../../../node_modules/tslib/tslib.es6.js
+// node_modules/.pnpm/tslib@1.14.1/node_modules/tslib/tslib.es6.js
var extendStatics = function(d, b) {
extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function(d2, b2) {
d2.__proto__ = b2;
@@ -13864,7 +13550,7 @@ function __spreadArrays() {
return r;
}
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/utils/base64.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/utils/base64.js
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var lookup = new Uint8Array(256);
for (i = 0; i < chars.length; i++) {
@@ -13926,7 +13612,7 @@ var decodeFromBase64DataUri = function(dataUri) {
return decodeFromBase64(data);
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/utils/strings.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/utils/strings.js
var toCharCode = function(character) {
return character.charCodeAt(0);
};
@@ -14064,7 +13750,7 @@ var findLastMatch = function(value2, regex) {
return { match: lastMatch, pos: position };
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/utils/arrays.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/utils/arrays.js
var last = function(array) {
return array[array.length - 1];
};
@@ -14185,7 +13871,7 @@ var toUint8Array = function(input) {
}
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/utils/async.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/utils/async.js
var waitForTick = function() {
return new Promise(function(resolve) {
setTimeout(function() {
@@ -14194,7 +13880,7 @@ var waitForTick = function() {
});
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/utils/unicode.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/utils/unicode.js
var utf16Encode = function(input, byteOrderMark) {
if (byteOrderMark === void 0) {
byteOrderMark = true;
@@ -14292,7 +13978,7 @@ var hasUtf16BOM = function(bytes) {
return hasUtf16BigEndianBOM(bytes) || hasUtf16LittleEndianBOM(bytes);
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/utils/numbers.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/utils/numbers.js
var numberToString = function(num) {
var numStr = String(num);
if (Math.abs(num) < 1) {
@@ -14327,12 +14013,12 @@ var bytesFor = function(n) {
return bytes;
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/utils/errors.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/utils/errors.js
var error = function(msg) {
throw new Error(msg);
};
-// ../../../../../../../../../../../../node_modules/@pdf-lib/standard-fonts/es/utils.js
+// node_modules/.pnpm/@pdf-lib+standard-fonts@1.0.0/node_modules/@pdf-lib/standard-fonts/es/utils.js
var import_pako = __toESM(require_pako());
var chars2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var lookup2 = new Uint8Array(256);
@@ -14385,49 +14071,49 @@ var padStart2 = function(value2, length, padChar) {
return padding + value2;
};
-// ../../../../../../../../../../../../node_modules/@pdf-lib/standard-fonts/es/Courier-Bold.compressed.json
+// node_modules/.pnpm/@pdf-lib+standard-fonts@1.0.0/node_modules/@pdf-lib/standard-fonts/es/Courier-Bold.compressed.json
var Courier_Bold_compressed_default = "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";
-// ../../../../../../../../../../../../node_modules/@pdf-lib/standard-fonts/es/Courier-BoldOblique.compressed.json
+// node_modules/.pnpm/@pdf-lib+standard-fonts@1.0.0/node_modules/@pdf-lib/standard-fonts/es/Courier-BoldOblique.compressed.json
var Courier_BoldOblique_compressed_default = "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";
-// ../../../../../../../../../../../../node_modules/@pdf-lib/standard-fonts/es/Courier-Oblique.compressed.json
+// node_modules/.pnpm/@pdf-lib+standard-fonts@1.0.0/node_modules/@pdf-lib/standard-fonts/es/Courier-Oblique.compressed.json
var Courier_Oblique_compressed_default = "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";
-// ../../../../../../../../../../../../node_modules/@pdf-lib/standard-fonts/es/Courier.compressed.json
+// node_modules/.pnpm/@pdf-lib+standard-fonts@1.0.0/node_modules/@pdf-lib/standard-fonts/es/Courier.compressed.json
var Courier_compressed_default = "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";
-// ../../../../../../../../../../../../node_modules/@pdf-lib/standard-fonts/es/Helvetica-Bold.compressed.json
+// node_modules/.pnpm/@pdf-lib+standard-fonts@1.0.0/node_modules/@pdf-lib/standard-fonts/es/Helvetica-Bold.compressed.json
var Helvetica_Bold_compressed_default = "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";
-// ../../../../../../../../../../../../node_modules/@pdf-lib/standard-fonts/es/Helvetica-BoldOblique.compressed.json
+// node_modules/.pnpm/@pdf-lib+standard-fonts@1.0.0/node_modules/@pdf-lib/standard-fonts/es/Helvetica-BoldOblique.compressed.json
var Helvetica_BoldOblique_compressed_default = "eJyNnVtzG0eyrf8KA0/7RMhzRIq6+U2+zMX2mJYsEuJMzANEtihsgYQMEITaO/Z/P41CV+bKlaug86JQf6uArsrKXNVX8H8m3y9vb7u7+8m3k4t/btazm+7o+PT0xcnRsxdPXzybPJr8dXl3/+vsthsa/L1bPHT386vZN98tF9dn7xfzPzbdrslmseAmR7smR9Bmdjtf9NxqEKbd/Objbve7Dwzb/7ifLeZXr+5uFkPLb45PBrL+6/xLd/3b/P7q4+Tb+9WmezT5/uNsNbu671a/d7vP/vjlvru77q7fLG9nd2Onv/tu+WXy7b+/OX5++uibk5MXj46Pj08fvXx28p9Hk/Oh8Woxv+t+W67n9/Pl3W5Xjx+D8Pbj/OrTXbdeT759OvCLbrUuzSaPH5/85fHjx8NOfl0OQ9gN5/vl5361G8XRf139n6Pjly+ePtr9+7z8+3L378vH5d/nR6+ul++7o9/79X13uz76x93VcvV5uZrdd9d/OTp6tVgcvdl9z/roTbfuVg8D9YDO10ezo/vV7Lq7na0+HS0/HP0yv1ve95+7b4ZGi6NXfzua3V3/3+XqaD58wXrzfj2/ns9W8279l6GzPw67up7f3fx+9bErc1B68vv98JHZ6rqqQ8PvZ5//Pk7J8+MXjybv6tbTJ8NcvFpf7QK9GsUfOtv+5uTx80eT3++v/z6dfHu8E4f/X+z+f/p4P1//7O5X86shoP/+n8n03eTbk+dDo1+Hrqw/z4Y4/u+jPX7y5Mked1+uFrNb46fDPBb+x2Y5xOv9wpSnT5/tlbvN7fvdRN3cZe16uVjMVsZfDBNT+OdudbXL/yo8PznZC7PbQVoP8THJOlx6UGY89/rzbNXdLboPLYk+VrsxW+++cf3JO/5iHO7nxWadu3A1lO0s7+Jj//ljd5ebD0OZL8VI1ovZ+mMO1p/dapnp8q7L8H4rWt5/XHWi7YflZiXo/EG0Xc+/CNg9dGJuuxBTT4f5nUirq+VieZfxurudR8lmYLGzgUS7PzazRcY3q24oZx/ms+PjmjTdulhNVV4+fzrOvci+Vxl9l9H3Gf3ge372fI9+zJ35q3+wpsLf8nf9PSfMP3KYf8of/Dnv8RcvvRryf+YP/pr7dZYH9Ftu9Tp/15v8wd9zv97mD57nD174rJ2OEz3Nrd5ldJn3+K+cfO+HxexTdx9sw0L+ftBinfLnoqdYKs7WV/P51Xx1tbnNs7bZ2fZ6WH+6vMfib6Ez9rFZHs/73Ooqt7rOrURxfsgfvMnoY+7yPKP/znv8lFt5CduScJv3eJfRMqPPouqz1QsLXOdI3Ofv2uQPPuRK2OZWwkl7R7vjnmL6uau7/IqJcPLicc3KVaP9oWy8ny+um0v99XIrzD2szh6x+3Kc5slxXCvuw+7AEH3Wx6zWjg+L5Wou+LprfMvVZjUs41cewJMnWDbreTl0TdGtRy26rG4280G5Xd7rI4edXL74K3IMvSXOh7lg4vhpOJSThwPXs5ubTqTtnuOhGB1w7OauW3Wi9odjodnNavYZTO1pzazhdKITPujhfT9bH4jwYXWljxVsAqI+nBSMnx8Oseef1/O1kIax3n9cbsKxYlr2Q3L7zK1mD6IeZlebe3XoUrz8w6L7krVGZd3OrlbqcOf9qlM7vl7ez65Cxbk0H2YSA2DKCuvQO9tdDyFVx6ibu5vZanO7mG3EbpY3w2HmJ/F1MxwHzMttyFkXXvlhz5PnI1uurj8Mx3nhwNCPUOIi6wcgkfsezmAPz57aHm4Hp9sscBe2sszEYnu9K/r1Wixgi7hjX3kityOSpRjUUJ/DKfGQ9+Ic4h9pSt0JYgb68h/zxpcmOan+dXH2/Ogo96AuF9fzhzkktH8k9swPmEVxeLcbHzo/9KG+EYN1OfeiMoGh5q/0/YVScdyeiBnVg38m9s5ngj7gZwFpJ37OMHgEnIScVCdWA33+5HkVx6seYlfkOr52xjzwUeq4/Ko64OXRytFoqn6kL4djp1Ktb4vGCuFMVgkZooe5Zk/0w9e499OX9dRz+Wd3dyMy903chZ/FqUF6chwskkOZ+4oXEjuabYz1isfq5z85chbVtx+XKzGqM9q7h4GqwE70qOBP6yJGYbNqoh14xPTiVi5wrDflKGcl+htT0KPY4tFWzQRvN4v7+edFL/rVKP+3cYCWSMPx1v18trief/iQ56pvW8OvcT+esCJZvDYOptmBVactXTXGe9eywVbG/BoD5Ish1T9efhuOGPAanJ0CrZafujs8ETJzXHU383U89PUSjZMNy3Gui3qosd4MVR3ORzzYdAxphdmIzLKV6v9qfOBfVOGnL+uxa7nSFa+DWZx/vP+Y4fdNA1wo37Kx3DdMpmuuji3hVevw4UBWxgD7+XKrNHjf5gqtGWktPa1ldN3ac65j2/fBwxJeMetxQbe4FwZ+H0zaPXG7POCIqWv2dbcbMZLGGr6Ux5leC3zwY1ef4hHOiyen4ONDAq+GRF7n7/ud8/W0Tv6isZD8fHD9/SVOnJ9K2H0dZYrJFtwyYpict2r8l9hti8MQtY+zBSwNtch3pyaxwn0u1BJgvhwPmzzVvjKBjVLoWgO6iWaKAxqnVc2qPhv5XR4gWgbLnltCXA820amMbSz531MnbOEitzk1O7+eXymj/SF+ERyYHTrc/ZUOa627jXl7czivD+7rVeM7XzVNOp4O2AzE73EjPnBA+WNruad9+yVieXZnB2TxSMC+7WAp0ASZXx7c02J5s5vvu6UI97Jtppu8jtUMGr6qUck3Bye3g5XcY95I3zu5jtvFnbt80Oye31ruftzs7kb+59Hk525199tsvtrdQ/735NXubvXk0Tenj//zaNzau0dA+35GNJo6wr8NW099a+8qAeHAUDgL33OWu4BLb+A2VYHu6z+g4DxBGUMW2P7qUED7wkH0Omy9HbZe+laNGaIwehfOQyzO6+gBhdEDh9EDraMHRKMHxUYPbKzrwIqdILkYtl7Y1nTYemZbl8PW8bFv1iEhg74D3gybT3yrfhBQiAVw+D6gNRaAKBagWCyAWSyAjbFw8hAyYRu0Pm7lEfW552MjLE1DVBzGqUidc6VWBcrVENWscVm4VT3L380lbFzVsYm5mE2iijauy9pkrm0TqMCNU5VX/jojqHdDVPTOVeVX9TxHlD3AuDICE7MbmESWYFz7gslsDiawQ5gQbaJi8IqKwDAqQtcwxtZhgvCPqoGJGKK6M67sxMR2ZbKxGNfuYjJbjAnsMyZEs6n4ISfkNrfqBWoEQrjQaAboQoaovo2TCzlnF6oKuJAhciHj0oWqepa/m13IuHIhE7MLmUQuZFy7kMnsQiaQCxknF6r8dUbgQobIhZwrF6rqeY4ou5Bx5UImZhcyiVzIuHYhk9mFTGAXMiG6UMXgQhWBC1WELmSMXcgE4UJVAxcyRMVnXLmQie3KZBcyrl3IZHYhE9iFTIguVPFDTshtbtUL1AiEcCEMDVpR5FTpUSRTIpGdKchgT5GTR0VRGlVoctbYH1tWFJVvxRbZvKJODhZFbWOxDXtZVMnQokiuFsTXDQ7+FjmZHInK6UKT88a8sOdFURlfbJHdL+pkgVHUPhjbsBlGlR0xqtEWgwbeGDgYZODoklFgq4yq8MvQAEwzcjKMKCr7jC2+4itspFHUbhrbsKVGlX01qtFcg/bQqItto33f4ofiJ1zXCXouUjIqlMhvg8RuCyJ4LVJyWpSkz0KDM7kf9liUlMOinv0VVXJXlLS3Ygt2VtTIV1EiVwXptaTgqEjJT4Ok3BQanMvYs5OipHwU9eyiqJKHoqQdFFuwf6LG7ola9E5QwDmBgm8CRddEzJ6JmnBMkMEvkVK1o6S8EvWDXsA+iZJ2SWzBHokaOyRq0R9BeZAZvpVte03bkRKuOI4eLdEQmYpxMkPn7IRVARs0RB5oXBpgVc/yd7P1GVe+Z2I2PZPI8YxruzOZvc4EMjrj5HKVv84I/M0QmZtz5WxVPc8RZU8zrgzNxOxmJpGVGdc+ZjKbmAnsYCZE+6oYvKsiMK6K0LWMsWWZIPyqamBWhqj+jCubMrFdmWxQxrU7mczWZAL7kgnRlCp+yAm5za16gRqBEC5U+4o25Iwq3AUyIhDYiUwCK3JGXuSCNCOTz8T3sx25oPzI1WxIrpEjuaAtyXX2JFfIlFwgVzLhtWDgS87ImEBQzmTyuYgve5MLypxcze7kGtmTC9qfXGeDcoUdypVoUcbBo4yBSRlDl3LINuWK8CkTwaicUYG6oKzK1QP1y2blgnYr19muXGG/ciUalvEHkatb0a5XrBUT4Vq1Y+hazsgIXCDXAoFdyyRwLWfkWi5I1zL5THw/u5YLyrVcza7lGrmWC9q1XGfXcoVcywVyLRNeCwau5YxcCwTlWiafi/iya7mgXMvV7FqukWu5oF3LdXYtV9i1XImuZRxcyxi4ljF0LYfsWq4I1zIRXMsZVagLyrVcPVC/7FouaNdynV3LFXYtV6JrGX8QuboV7XrFWjERrrUaf9HDd1cJmUDF5FeG2a1GAbyqEnKqiqVPjeJZ+l72qIqVQ1Ut+1NVyJ0q1t5UVXamysmXKiZXGvHrRMCRKiE/MqzcaBTPUwzZiSpWPlS17EJVIQ+qWDtQVdl/Kmf3qTx6z0jBeUYCvjMSdJ2K2HMqF44zSuA3lVBlVay8pmrNmmOfqVi7TFXZYypnh6k8+stIH1LWbVObPhM9euEqY66jrRiiwjVOxuKcnaUqYC2GyFuMS3Op6ln+brYX48pfTMwGYxI5jHFtMSazx5hAJmOcXKby1xmBzxgio3GunKaq5zmi7DXGldmYmN3GJLIb49pvTGbDMYEdx4RoORWD51QEplMRuo4xth0ThO9UDYzHENWecWU9JrYrk83HuHYfk9l+TGD/MSEaUMUPOSG3uVUvUCMQ2YW+G+iruBU/W1B1DEAipIXrPcRAFkRBKoziU1gITSG1fB3tquvYtyydHIXuAscEc1q7C4imHBQbCDAbCLBxIHvywxj3U9+KbvoDxh2Q8NYfKO5Ao6P+EOIOzLoLbOwukGibP4wl71vTsLUr9Oe+VUcHCLrsdP97bHVyd2T8yTVDo/9i+AxRDI1TII2raJqYQ2oSxdU4B9cEjrAJMcyGKdaVX2Q0zQhCb4jibzxPQpVoJipO01FeCIzTURFPR+U8HZXL6aiimI4q8XRUnqajCmk6qkDTUTFPx8gvMppmhNNREU9H5WI6RomnY8Q0HX8dZ+KFb9VdAarxBxRCDxw6BLQGHJDFGpiFGdgYYSA1uI524zzxrToCQHUEgMIIgMMIgNYRALIRALMRABtHAKSOwFGdrePHhmymRvbTOFUnvhUH+hNOFSAx0J9oqoDGgf4UpgoYDfQnmCogcaA/wUCd2DgdbeJWHuamMaaNHNMmj4kPyUARo92I0W7CaH+e7E95nvhWPC4qSBwEFZ4OggqNB0EFyQPJotDhUWH1fAZQPBbaoXLc8tS27FjIUT2BQRQOj5zj4RFQe000YDtqcuTHRs782MjYcjcC37JIO4qRdo6RdmqRdsSRdsUj7cwi7cgibWgT4r7J+aHOO36eqFOOnyfpbONnkdWgiPzg04ufJ3xmsSO9LVBlKy7RBaWFNryLH+qCBAoBqSoa1CQHhhpQjEjV4aJGHDmSqchIpXqLKiQ/CVSFpFJBsipqk5rkMuUGuWKpBRUvqVzHJHNJRxmqmwQqdFJVzVOTXP7UgJyAVG0K1Ij9gWSyClLJNaK6aSUSewmpXy8k4TDU4GAhNXyHGh0upORGJEdjiiJ4FAlkV6Qm5/plgtfwyla8fLdH4srdTtgd3o+XnXabUztG3W2VC1knvmklDgzr0nH8Bc1BOo2S4H6N55dJurzzy0Rd2fklv6PqiIJw8B1VUzEc+Abni4gwMPkNThZEiKrWilPQW2KfA8Fha7/1+EvMK4ggCRRHVlU0YxuMaVQgslHA+JLCUSZZxDq2aEVctDrcpG+FkuegXcBjg9FecQ4MUfSdq7hXFSNeGcS6IoyyMY6vCSKyVWvFNOgtsc+B4AgaT7EbjtPKCeZT34q3HAqKd4MKEjcgCk/3HgqNtx0KolsKhdHdhMLCjYRC6nrp6K2Z+RnOOaIw3S5chO+Zhq13Ycuv0JxN0sWZs4m6LrOj9dzXd2nnviOqFgPTYIjmwjhNiHE1KybmqTGJ5sc4T5IJPFMmxOkyTHOG6w6FgWevse6QepG/e5rRu4xgWtNCxDxPcJVolivmqQ4vU8F8R06THkWa+Siq6Y8tcg5EnRIhipwNUeWUiGrMi6hRcqT3OlX0OE0Ovdepmlw09jdt8HcNDvmjX2+UYs6koFM6BY1zCl5EgYxCSvmEEmUTSiqXUM+ZhCrlEUqcRahxDqEWMwgVyh96hy3HiXOn/Q5bbnAh9zOV9J2kkDHq1S4h5WwBlXIFFM6U+qYApIkhyhHjlCDGVXaYmFPDJMoL45wUJnBGmBDTwTDlAr7sQ2HgLGi87EPqRf7uaUbvMoJpT+/GMM8TXiWa7Yp5quO5Oc44KzTxLNP8s6zSgNvkbOAWlBQsc26wzinCeswUVilhSH7bjCmnT5JVFlGji+Z+p03lXVOBDGOFEo3lnG/UgtKOVM4+e7of8s4ZZZwLlGsuqCxzNeeXa5RZLnBOucLZ5ErMI+eUQeFFHo4IZ03rRR6WL8T3TwV7JxjkRX7fJQk5F0yjLDDO819PN2H6DdHsG6fJN67m3sQ89SbRzBvniTeB592EOO2GadbxGgSFgee8cQ2C1Iv83dOM3mUE050uSjDPk10lmuuKearrU2Mw1YZoqo3TVBtXU21inmqTaKqN81SbwFNtQpxqwzTV+OAnhYGnuvHgJ6kX+bunGb3LCKY6PSfJPE91lWiqK6ap/m2c5fHJhN9whpHV2UVGT9a5EB6tc+zP1jmDR+gcwjN0Du0hOkd1BoH5czJlK14xKyg+0ViQuKtSeLquVmi8f1IQ3Q8pjG6CFBbufBQS7yr+BvM2Xk3codigy4Oy+4iI9KA6OahwmxBwHmsnxtqJsS5Ditn9PkDika/C062cQuODXgXJh8OLQk9/FRYfCS8oPtv1G1bHGP3XE3zEtGzFR0wLEo+YFp4eMS00PmJakHzEtCj0iGlh9IhpYeER09eeRj6MOrQ9eTPZ382HrfhsTkHi2ZzC07M5hcZncwqSz+YUhZ7NKaxOEaD42NGb0Z9hq2Y+ouDKLpzHrTze88Z4z+V4z/N4eSJBEeM9p2eR3sBEOvFl5M0EHzJ8M64Url3GpfkNrQ8jrVcxYfYNUUiMq7iYmINjEkXIuA6TyRwrEyhBjFOW4HVoRpQvjevQpJ4L1IiVzCET27HibDJ+OFYpr0zg5DIhZli+1G4Icg2vq1Mrzjp1XX2U6oPEkHqGKJzGVThNzOE0icJpXIfTZA6nCZR6xin1Kn8rEKWec5V6VT0XqBErmXomtmPFqWf8cKxS6pnAqWdCTD18tJ0yBFIPH22nVpx66tH2KqXn2E6kwKE98BybbiLCrJ9j02oj5I3n2LTMqaqfY5Pq26bAyXvoeQfZ5rwpHIy5TurY5GsxTwke1f+fmOdkj3JK+ShT4qcHQWSWYhGk50DkJ1JBNJ8C2TcYpruc/b30rfoNgOoZE6AwKcBhD0Br+AFZOIFZDIGNgQNS89eRv6D6FksYkDjVeEvFCjSearwVZQkKnWq8xQIEFE81dmh3jvfCt+K7GgXFdzUKEu9qFJ7e1Sg0vqtREL2rURi9q1FYeFejkPiuxg5dLRc08nru6m12n3jmW3WUgKqxIMJRAodRIoVTV8B18IBs8MBs8M4+9p8/duWc68TYMoxqmWdr2ZiapZyaZZ4aPp0FRUyanc4CyjNkp7OOVnErD2QVvdyFdXc7z1O+CaW4yfaxaXjFRnrFJnsFP5IKinCRjXCRTXKRbZjwPm7lJO1z5uG7iC8JURDSu4jMVYTUu4gsUazyu4gscGz4XUTG5LV4/H5KiFxXH7+zmP03Hb8z106cj99ZIE9Ox+/EwcUMUa0YJ582rhzBxGwLJpE3GGcbMIEN3ITo4obJKPy1z4UKHZl6xV2uBbZ34+TxzoXRm9iuOWX5ppHvG2fzN4FXgCqkZaAKyxwMXhCMH8oBsTSY1MiBxiJhcitFeLkw3kgFXjgqXwnUGLpeR6oqFpMqwYpiqOGocm0xse2cvMoY10uNyS1jTYuOCdpYtznbeoEa5aRWo3Cgj2tSFDiOUeX1Kaoy1rGJiHhswHGPagpvlFOQo0yhjiKvZOlywKkUeFU7cDlANxErnL4coNXGate4HKBlXvn05QCpou1HgYs+qrwiRlV6YmwinDE2YH+MarLBKKf1Msq0akaRDTOo7GgkxnU0vkjXquW0pkaVV1ZS1foam3zNS+RaG1vwihvVtO5GOa2+Qc5rcHzJrhXOtB5H9esZqNbm2OBgBrbW6djocJqmNTuqBxMxrd9BXTWFg2FrrOihjVrXQwNc3aNwcG3SK31s8rXVJ636UW2s/bHR4SUqHwdE+dAStW3VQN8UDlpDPko4n+ATPed4PAAoPsdznlZ+4Ol64jmu8YDomZ3zsJoDC0/qnOO67aja6BMj9EMo9XoyjrXx6o1zGvWhV29czONvvHrjnCPRevXGhRiTxqs3xik66ZWVkTdeWSFOwTr0ygqJKmxfeWWFdArgwVdWSOVQHnhlhTQKqnx7Q0WQwyvf3giUQtt+eyNIKqwH394IKoX0wNsbQeNwNt/eCAqFUrzakGPFYcyvNjiiADZebXCuQtd+tcElClrr1QYXOFz61QbHFCh+JYBCwSFqvhKQFArY4VcCkqzC99VXAlILCuZXXglIOof24CsBSaVAN56F13HlsItn4YFRqFvPwoOgwnvgWXjQKKTNZ+FB4TA2noUHTqFLj45zVDhc9hPbEC5nFC4XKFwuqHC5msPlGoXLBQ6XKxwuV2K4nFO4TKBwGedwjb8cDMGqhEJVMQWqYhWmquUgVYVCVDEHqHIOT+UxOJVSaEZMgRkpheViDMkL34qnKxcYCkDibO+CQgA0ntddhKEDo2sIFzBkIPEkbYf8Z5nLVpy5guJZlgtncSumQkFivgtPc11onOeC5O8FF4Vmv7B6fgooTu8O7ab1mW/FU5aCaggAiesWhadTmkLj9YeC6KJDYXSlobBxxoDUETiKp7MXk/SI9g7FQXd5cuxKDSI9X52cr3AhBnCexk5MVkdlumN2ccWzc3dB5aVvxVPygsR5eOHp5LvQeMZdkDzNLgqdWxdWcwxQvJR7MclPbe9YvhCxo5sws5ucjZtG6m1k6m1y6vFlBFBEUm5EUm5CUk5H14Ot2Ospuh4gMZApuR7QOJBpcD1g1N0puB6QWEPTCT5wN0XvAiQe85qSdwGND3RNhXeBQo9uTdG7AMUH46ajd536VrwZMEXvAiRuCkzJu4DGy//T4F3A6Fdrp+BdQOK1/Cl41zEQvAo9Ha1r/yNlU7QuQPZ2CaD8C21Tsi6k+HaJ4/gTbdNgXcD87RJjZl0+1GVIuGUukmWjIpayIpa5Iti6QBG1YtYFKBcGXaidknP5vO2c69TGb84FKCaec0w8p5Z4jvhmkyueks48JZ3VlDTSh3rqc933qb4vR8Mbf6npEh0Pmb2RBiy+iAMCvokD2F7FAeZv3AD0V24A1nduANkbac521vfct+KLfJfJ+oCnd/su0foA0cSBYoMBVvMSUO22o5ktsJdofYDizeLLZH3A07HBJVofIDoCuAzWByxcR79E63NUS+gpkFv8ZebL0fte+FY8n7hE70OUzycuyfuQgvcBjqcZl8H7gNFpxuVodDAEczpk6tXMS/I6xPRq5qVwO5T4rc1L9Dtk9Ibm5ST/GPYlWZ7P1yY22oiBbloD3eiBbsRA2fdQUgN150MYX0+9tOv0YAbpJkQS2NP0bYikCndLNyKSkHwu34pICjleuhnBAnhfeseMuXJB9Y4ZS+SHrXfMWGZnTO+YMSePrByM0hC5pXGyTOPKN03M5mkSOahxtlET2EtNiIZqmFzVbzKFUuV7T1wDYHOGyGmNk906F55rYjZel7L7mkYWbJx92AQ24yosxaCTLZsgLctU4VumsXmZ0HAw05ONmcKmbQI7d7qTyILw8CptRPPk5iYcjI/yddNa8Wk5vOnN+GSvN4UMn275VSdU9/yUxs7fvOunGgj/V/f9lJZWAXnnT4m0Fqh7f0KDFQEpLQooqXUB9bw0oEqrA0p6gcAWvEagRssESrRSgASLBVJaL1CiJQMltWqgnhcOVGntQImXD9R4BUEtLiKo0DoSbgYHxxC3iUWBgWkjpTUFJVpWgiRWFtTz4hLUvL6gTEsMSrzKoMYLDWhLHZK03KAmHRUbCFNFmX0VtYa1YpPkrijyAoQar0HqUQGhiZUI1I3+UFqPUPtaANWqhPKBALbWJmxyKIB5hUIxLlJDOU38V0LKlv+uj6F4/8mF3d8k3P+Vh93WNmz5dZ6yFa/zFJSu81TXwx4Zom4Zl32rKnSwom1Gfe4B99d47vTYMey0Ieq0cdnpqkKnK9pm1OcecKeN506HZ5Wg55FT96MoxxCawEAC3zZ43+gfjyuKeXCQ7jA0pDQwlOSwoAEMCuhW0l72iYeDUh5MfcwHRmKIhmFcjqGqMICKthn1uQfcb+O50/bYB/TaGXXbBdlvk6HjxraC9aIf3HcXcuftIQzovDPqvAuy8+HP048dDX+enlkv+sGdl3+eftTGByWg65VQxyuW3Ya/ej12EP7qdSR92jd3V/zV61Gpv0AHvTVE3TUu+4t/JHfsHv6RXEJ97gH3Wf2R3L30fqAL23PZ8uMEQ6qXRfCDm4o24avp7+G9T8cawGXf6O/hvRcHFKDQjdD34fABWPi1ivdjpH2rj1t5DDmOVwP1QOy2PgXtk/oBkasx+LAV93WVgw9CvMV7NXbce9DHmbyijo+0Hgt8zAiGYEj2pqoLgWhExg9/EY0Nj22okzxKdWwzSvbia0YwVEOyh1VdCERDNX74i2io+L4kdZKHqt6XrNJwWrdYzGiwBnG4DnU/TV9IyIN25WtfxwM3pVddToN3JQ9/f3I0WX+eXe0+cjrScsKd/2zNSZYbWvzC4fRscm07LVtX+79dC8hN/Dr493UdqG/ZCB3h0PZ03APu2BDtvfKH/OltRr1A1CPjqVu7ihuDtN85Xko9MfIQPrANW1/CVh+3YkdSfe8pXacfO8IXdk8ifsif32b0JaNeIOqm9KK9RD8+MPaVX08/ifghf36b0ZeMeoGor9JMRkm8JlI7rN4SORHaQ+Prtg3+pcH7FufhtM6qRj1fiBtHJK7BnCTlQX7RVtIvkvaa0igaJ1NV9WtzPAhQeBQgPejv2mr8ReO+gXkoqOWx0Gsh4zj4rZCTiB/y57cZfcmoF4j6q84HR4lfDxg7m94OOCH+IL5iK9gXwXrFqMvyNHDU+Bn9sc/pEf0T4g/iK7aCfRGsV4z6LM/+9tqHCV4kr6SLW/GooKB6LRxR/gHjwtORw57in5R1HH/XuCD69eLC6NeLd2xpRzllKx4yFSROigpPJxqFxpOiguRJUVHopKiweOhVUHw69MMkXIKuiA6dnkQh0Jv9XB37xjhsIONMIYE4APYwIPRpAjrGBkgNAKA6R478pF1cXmheWqjtYKRG4nANxzE7zgM3LY3elRQCk2IcDFMwjFNE8mXgm8Zl4JuDl4EjhDDpH4HQYgzZgZ+A0C1S+No/AKEbxFA2fv5BqxTWqN60wsQhJlUFuv5JzRPfijbDf0hzTz+N7rR33E/oToCifTpPlv0J3QkQ3wNyxR3UmV1VcmR3yvYo/0qGFmgsh34lQzfJI2z8SoZW9bhbv5KhZYpG41cy9uoirM6LsAYv8uq7kOvuIp8HLfJJz6Jx0rNon/TEKwL49fkHIbRAu2r/IMTYoP79l21GvUD09ervz+6l2wle6SxbsdoKEot64akKC42LekFyUS8KLeqF0ZXOwsKVztsJ/tndWxwxIOjdnt5N6k1l24pHXXdpsMDTIdbdJNwwNiT/RsTdJN4eNkZHWXcTvBlcye7g9dS23B7FPRR99+QuPuEVEQWh9XxXFHM4xNNdkevAqGe7osAhyk92BQzByreS71K1M8+xw9+7OyZEsdO/d8dijl36vTvmOnb59+5Y4Njx790RhtjhJRcKBMdOXXKpUvpNoWMpcBwP/KaQbiJiqn9TSKuN+DZ+U0jLKdbyN4WkiHFPS4gMZZqD5hIyNqj3zmAODFH0jau4m5gjbhLF2riOsskcXxM4sibEmOL9xtOI+hwIjmD75uJygnfWlmEd3m35H25ahl0t816WegfoXYRgV3gR90ls1ecP8p7bDrdMVzgVh46kK5xPRPu+8T3cr688NwKHrtg1ebkSKXRLPvsRKHXp4LMfS7xqRwi6glftnsRWff4g96D9FAcdi2MvSIHOkAJ9IqVvfhv3kOXc0XC9kBl0LlwvfELtevFZ7sqBx0bqWQf2IR9MG4Ie4PE1fZD3r46vRwktiRDsviLYfTauZcO4lm3j+jzB84PP+FlA6aygXjBr3WGMekuM9xjpq0x94eqi+3Bfv3T//29On5laP3gdP2S43jMUmt/wjTjGMWrqpm9sEa89Bi3ERYxhFcNiuHZIafVqsNRCZ0WL3dw+E7juUGnxF0tJqzettRq6o1rkya+SF8oQpN2zHrgVJ6yg2ktE1jmA4/X3Z0aug27p4+jG6qFs2aUsR3T9ygR76d2/bBm38kDUlfPCU1EXGk+yC5In2UWhk+zCYvYWFK+c75BdOfew/REarMO419FcVphFiGx+EDZieV9v5ZSN0Mr5Q70wudobHmyEC7KraHcjtNvtPjRDNFHGKe2cc+7RvfwayYqvc0tORXum2uNiiJLSuIymPSFF6Vn5UqDG+GW2mphT1iTKW+M6eU3mDDaB0tg45TI/O8HT8Eduus6B4/w2TknunDPdlUMT5LltRCU+nDRQJveZxDoQ5wKjYs9zeEQMUTIYp2JwzsXAf6niWcTXuSUXQ+VQDIaoGIzLWKc/HkE7WArUGL8sBvW3I1iiYmj97QiWuRjS345gTsWQ/nYETcMfuek6B46LwTgVg3MuBlcOTZDnuxFVDHAFizK5zyQWg7h8VZVwUQVLIgqcGFHl8iA1FUnUqVSCeN36VCqbfL/uqRK4hA7er1NtUjmlG1xaOBhBXWDNG1y6ARfbwRtculEqPH2DS6tchEFNpRjUP1ofW7emIRVnVLlESU2FSvrX0wDqMnJZunwpVFZf3+JUzK3roHs9Xi+qYUKUH0j0gATYuAcILzXSKfC4Vf525/iinyF/1oc43SIP6oWdQlUyTWT3JyjHM3NDlhzE8UJAlOrZ+3ha/iKLo7LP32EOJ+5oZSsWeEHVbACJQi88VXehsaQLohItjO4nFhbuJxYS6/MBrPeFkZ1/PfGteNPxITkV8HQz8gE9CRAZESj+i2vOquUAquNwRD9dtoWhnRpxa95mP942THgrnXeb7Xbb9NitMNZtdtNtttAtDm0/kN0VZL/vULbirY2C4nN1e5RvdBSe7nHsaXquruB416MguqNRGN3M6MdaeeJbMa96rBVAItV6qhWgMdX6UCvA/Cf8nI21AiTmWJ9qpQ/z0Od56PM89I156OU89Hoe+jwPvZiHvjEPpQie2pYVgaNYBM6xCJxaETjiInDFi8CZFYEjKwJDVN91QcSsMkSpZZzyy7hKMhNzpplE6Wacc84ETjwTYvYZphTkyywUCkgHQ5SRxiktnYvcNDEnqEs5S02jVDXO+WoCJ224dvM0IkrfxrUbEnMi52s3xHVKi2s3JFBy52s3kXOaw006yHSklOwoUb6jpFIe9Zz1qFLio8S5jxqnP2qxAlChIsjvgohYQbohpWpAiQoiSKImUM9lEdRcGShTcaDE9YEalwhoUCVIqVBQUrWCei4XVKliUNJFgy24blCj0kGJqke8BjQmxZ8TeI75T8gBIOGJ5T95xgHzedafNL9Aw1PIf+JsAorPG/8JPs4kdppdPOHcfeHhSUkDYQdnTENK/j3yerLZZRLHZTiOy3Eel2lpXK6kcZkUx2WYxmWcxoUvSXWaxvEFKY4xSnmcQU9jjWoab5DjmINE4w4ajP0///v/AGoZ428=";
-// ../../../../../../../../../../../../node_modules/@pdf-lib/standard-fonts/es/Helvetica-Oblique.compressed.json
+// node_modules/.pnpm/@pdf-lib+standard-fonts@1.0.0/node_modules/@pdf-lib/standard-fonts/es/Helvetica-Oblique.compressed.json
var Helvetica_Oblique_compressed_default = "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";
-// ../../../../../../../../../../../../node_modules/@pdf-lib/standard-fonts/es/Helvetica.compressed.json
+// node_modules/.pnpm/@pdf-lib+standard-fonts@1.0.0/node_modules/@pdf-lib/standard-fonts/es/Helvetica.compressed.json
var Helvetica_compressed_default = "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";
-// ../../../../../../../../../../../../node_modules/@pdf-lib/standard-fonts/es/Times-Bold.compressed.json
+// node_modules/.pnpm/@pdf-lib+standard-fonts@1.0.0/node_modules/@pdf-lib/standard-fonts/es/Times-Bold.compressed.json
var Times_Bold_compressed_default = "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";
-// ../../../../../../../../../../../../node_modules/@pdf-lib/standard-fonts/es/Times-BoldItalic.compressed.json
+// node_modules/.pnpm/@pdf-lib+standard-fonts@1.0.0/node_modules/@pdf-lib/standard-fonts/es/Times-BoldItalic.compressed.json
var Times_BoldItalic_compressed_default = "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";
-// ../../../../../../../../../../../../node_modules/@pdf-lib/standard-fonts/es/Times-Italic.compressed.json
+// node_modules/.pnpm/@pdf-lib+standard-fonts@1.0.0/node_modules/@pdf-lib/standard-fonts/es/Times-Italic.compressed.json
var Times_Italic_compressed_default = "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";
-// ../../../../../../../../../../../../node_modules/@pdf-lib/standard-fonts/es/Times-Roman.compressed.json
+// node_modules/.pnpm/@pdf-lib+standard-fonts@1.0.0/node_modules/@pdf-lib/standard-fonts/es/Times-Roman.compressed.json
var Times_Roman_compressed_default = "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";
-// ../../../../../../../../../../../../node_modules/@pdf-lib/standard-fonts/es/Symbol.compressed.json
+// node_modules/.pnpm/@pdf-lib+standard-fonts@1.0.0/node_modules/@pdf-lib/standard-fonts/es/Symbol.compressed.json
var Symbol_compressed_default = "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";
-// ../../../../../../../../../../../../node_modules/@pdf-lib/standard-fonts/es/ZapfDingbats.compressed.json
+// node_modules/.pnpm/@pdf-lib+standard-fonts@1.0.0/node_modules/@pdf-lib/standard-fonts/es/ZapfDingbats.compressed.json
var ZapfDingbats_compressed_default = "eJxtmNtu20YQhl+F4FULyMGeD7pz3AY1ChtG7NpFA18w1NomIlECSRcxgrx7SVk7+wOdG8H5OJydf2Z2d5gf9cV+t0v9VK/r+6vXsXlOlbHe28paq229qj/t++m62aXZ4J/m8PRb1z9/baZxefK63Z6eXN5dVMvTCh83u277xr/6kLrnl2XNq7TpXnczuZyabdee98/b2VzM/x4/dd/T5qab2pd6PQ2vaVVfvDRD005puE3Lu7eH1HbN9hTjx4/77/X6y5lcnUmjVzHIVVDicVX/1W/SsO36dLMfu6nb9/X6TAoBD+5euvZbn8axXtuZ36dhPJrVQqgPQoh5hev91LWLkIv94W1Ygq9+aX+tZAx2tfz64284/sblN/rqfLP/mqrbt3FKu7G67Nv9cNgPzZQ2H6rz7bb6vLgZq89pTMO/M/xfEqturJpqSM/d7GJIm2oamk3aNcO3av80O5xh3yyKmm1193ZIT02bqovTKjP+MAf++7zsZvZ3276kYyWWXB0z99S18/PbafPHQ71W4fjn/fxnFO+ZvkrT0LVzTr78qB/+nk38bHM9exgP8zr1z9U7jt6840YW5uSJKcZOCaBBnKgm5mU8MVNYyMwWFvO7Ukagkmgg6sDWQ5yFFqjzUrLEaQ3BEmiwNsMSaZS0vgWfOkPHWQowNeTUc0kumnxZvsgPxlGai6VTGUqAVCTQ6QkWnc77DKEiLktSUBJKqHIQZ86d8gCpHYoiEzMsb1ubYy8vW50DChB5ZhGqrijD0EqUIeiaEHIfCg5Kpuu0ApiToaGPSY0uaQsyr65L2oKi1yFt1PLaQ3lzfXTgXodGoJYzglndSLDMPg1sTPJpQJHJigw0QrGERqD9YhyTOgONQDUyuF1zaxuokc/BW2ztXCMrGZ9WMW1oQZHIXWNBkSCfRZEL5BMUiZw6CzVSFCfUSGZFNjIldoKDkonTKQiJIGzWmFd3BizJJ9SINoLDriOfUCOZS+zg+KGD1qGiLNMLxtJD1/ns00ON6EzyUCM6vbxhoBKaqbG3DFQCNiL1iHccBPV0DHhQH/JW8EW90dkyFKGywCJU0WkVSvSGeiSUODWFFD0HYdPQVoiRgfPMA+/nnRgiAyNYSjpWNQcNSMrtFCUH4ZIRpSCWocFCSuhCEY6hoUClc0WC52BJlCYYLQdhN+hygRRRlo5BKRRLS6oihSqh+ZzzRGG1Mo4Iz1LoP0qsxDGFzk0JE42ji0jCPejomJKCuwil4m5CiRMEUMVSzVLDUstSx1Juc0oVWMpqY295qVltmtWmWW2a1aZZbZrVplltmtWmWW2G1WZYbYbVZlhthtVmWG2G1WZYbYbVZlhtltVmWW2W1WZZbZbVZlltltVmWW2W1QYjQCh7E2aAQHeGhCFgPoNoy8KNb2wxBhmGKBxoUZXlLGsLI6AsftEDHV0wIURVbANLcTKlGGBIKPOAxCmhePCKUwFzAmpDFRQvjA9R06Hq8TONvshgKDCuRAZTXigUxjxNFfKRo3CLhnIJBMFRvMZpqpNBMlQJzGT5WFQMVQI/AikPMIhEU1aDjqJvQwmjSHB05cC9jbYwc5UtAHNLhDw41ha+lEqF4JaH3gmB61SYcqInxTDmQK8v08vjqv4zDf1N0w3Lf4A8/vwPpfK11w==";
-// ../../../../../../../../../../../../node_modules/@pdf-lib/standard-fonts/es/Font.js
+// node_modules/.pnpm/@pdf-lib+standard-fonts@1.0.0/node_modules/@pdf-lib/standard-fonts/es/Font.js
var compressedJsonForFontName = {
"Courier": Courier_compressed_default,
"Courier-Bold": Courier_Bold_compressed_default,
@@ -14498,10 +14184,10 @@ var Font = (
})()
);
-// ../../../../../../../../../../../../node_modules/@pdf-lib/standard-fonts/es/all-encodings.compressed.json
+// node_modules/.pnpm/@pdf-lib+standard-fonts@1.0.0/node_modules/@pdf-lib/standard-fonts/es/all-encodings.compressed.json
var all_encodings_compressed_default = "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";
-// ../../../../../../../../../../../../node_modules/@pdf-lib/standard-fonts/es/Encoding.js
+// node_modules/.pnpm/@pdf-lib+standard-fonts@1.0.0/node_modules/@pdf-lib/standard-fonts/es/Encoding.js
var decompressedEncodings = decompressJson(all_encodings_compressed_default);
var allUnicodeMappings = JSON.parse(decompressedEncodings);
var Encoding = (
@@ -14537,7 +14223,7 @@ var Encodings = {
WinAnsi: new Encoding("WinAnsi", allUnicodeMappings.win1252)
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/utils/objects.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/utils/objects.js
var values = function(obj) {
return Object.keys(obj).map(function(k) {
return obj[k];
@@ -14551,7 +14237,7 @@ var rectanglesAreEqual = function(a, b) {
return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/utils/validators.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/utils/validators.js
var backtick = function(val2) {
return "`" + val2 + "`";
};
@@ -14728,7 +14414,7 @@ var assertPositive = function(value2, valueName) {
}
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/utils/pdfDocEncoding.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/utils/pdfDocEncoding.js
var pdfDocEncodingToUnicode = new Uint16Array(256);
for (idx = 0; idx < 256; idx++) {
pdfDocEncodingToUnicode[idx] = idx;
@@ -14786,7 +14472,7 @@ var pdfDocEncodingDecode = function(bytes) {
return String.fromCodePoint.apply(String, codePoints);
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/utils/Cache.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/utils/Cache.js
var Cache = (
/** @class */
(function() {
@@ -14813,7 +14499,7 @@ var Cache = (
);
var Cache_default = Cache;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/errors.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/errors.js
var MethodNotImplementedError = (
/** @class */
(function(_super) {
@@ -15186,7 +14872,7 @@ var MissingKeywordError = (
})(PDFParsingError)
);
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/syntax/CharCodes.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/syntax/CharCodes.js
var CharCodes;
(function(CharCodes2) {
CharCodes2[CharCodes2["Null"] = 0] = "Null";
@@ -15250,10 +14936,10 @@ var CharCodes;
})(CharCodes || (CharCodes = {}));
var CharCodes_default = CharCodes;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/PDFContext.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/PDFContext.js
var import_pako3 = __toESM(require_pako());
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/document/PDFHeader.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/document/PDFHeader.js
var PDFHeader = (
/** @class */
(function() {
@@ -15294,7 +14980,7 @@ var PDFHeader = (
);
var PDFHeader_default = PDFHeader;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/objects/PDFObject.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/objects/PDFObject.js
var PDFObject = (
/** @class */
(function() {
@@ -15317,7 +15003,7 @@ var PDFObject = (
);
var PDFObject_default = PDFObject;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/objects/PDFNumber.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/objects/PDFNumber.js
var PDFNumber = (
/** @class */
(function(_super) {
@@ -15355,7 +15041,7 @@ var PDFNumber = (
);
var PDFNumber_default = PDFNumber;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/objects/PDFArray.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/objects/PDFArray.js
var PDFArray = (
/** @class */
(function(_super) {
@@ -15471,7 +15157,7 @@ var PDFArray = (
);
var PDFArray_default = PDFArray;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/objects/PDFBool.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/objects/PDFBool.js
var ENFORCER = {};
var PDFBool = (
/** @class */
@@ -15520,7 +15206,7 @@ var PDFBool = (
);
var PDFBool_default = PDFBool;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/syntax/Delimiters.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/syntax/Delimiters.js
var IsDelimiter = new Uint8Array(256);
IsDelimiter[CharCodes_default.LeftParen] = 1;
IsDelimiter[CharCodes_default.RightParen] = 1;
@@ -15533,7 +15219,7 @@ IsDelimiter[CharCodes_default.RightCurly] = 1;
IsDelimiter[CharCodes_default.ForwardSlash] = 1;
IsDelimiter[CharCodes_default.Percent] = 1;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/syntax/Whitespace.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/syntax/Whitespace.js
var IsWhitespace = new Uint8Array(256);
IsWhitespace[CharCodes_default.Null] = 1;
IsWhitespace[CharCodes_default.Tab] = 1;
@@ -15542,7 +15228,7 @@ IsWhitespace[CharCodes_default.FormFeed] = 1;
IsWhitespace[CharCodes_default.CarriageReturn] = 1;
IsWhitespace[CharCodes_default.Space] = 1;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/syntax/Irregular.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/syntax/Irregular.js
var IsIrregular = new Uint8Array(256);
for (idx = 0, len = 256; idx < len; idx++) {
IsIrregular[idx] = IsWhitespace[idx] || IsDelimiter[idx] ? 1 : 0;
@@ -15551,7 +15237,7 @@ var idx;
var len;
IsIrregular[CharCodes_default.Hash] = 1;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/objects/PDFName.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/objects/PDFName.js
var decodeName = function(name) {
return name.replace(/#([\dABCDEF]{2})/g, function(_, hex2) {
return charFromHexCode(hex2);
@@ -15674,7 +15360,7 @@ var PDFName = (
);
var PDFName_default = PDFName;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/objects/PDFNull.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/objects/PDFNull.js
var PDFNull = (
/** @class */
(function(_super) {
@@ -15706,7 +15392,7 @@ var PDFNull = (
);
var PDFNull_default = new PDFNull();
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/objects/PDFDict.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/objects/PDFDict.js
var PDFDict = (
/** @class */
(function(_super) {
@@ -15839,7 +15525,7 @@ var PDFDict = (
);
var PDFDict_default = PDFDict;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/objects/PDFStream.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/objects/PDFStream.js
var PDFStream = (
/** @class */
(function(_super) {
@@ -15910,7 +15596,7 @@ var PDFStream = (
);
var PDFStream_default = PDFStream;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/objects/PDFRawStream.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/objects/PDFRawStream.js
var PDFRawStream = (
/** @class */
(function(_super) {
@@ -15943,7 +15629,7 @@ var PDFRawStream = (
);
var PDFRawStream_default = PDFRawStream;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/objects/PDFRef.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/objects/PDFRef.js
var ENFORCER3 = {};
var pool2 = /* @__PURE__ */ new Map();
var PDFRef = (
@@ -15990,7 +15676,7 @@ var PDFRef = (
);
var PDFRef_default = PDFRef;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/operators/PDFOperator.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/operators/PDFOperator.js
var PDFOperator = (
/** @class */
(function() {
@@ -16045,7 +15731,7 @@ var PDFOperator = (
);
var PDFOperator_default = PDFOperator;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/operators/PDFOperatorNames.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/operators/PDFOperatorNames.js
var PDFOperatorNames;
(function(PDFOperatorNames2) {
PDFOperatorNames2["NonStrokingColor"] = "sc";
@@ -16124,7 +15810,7 @@ var PDFOperatorNames;
})(PDFOperatorNames || (PDFOperatorNames = {}));
var PDFOperatorNames_default = PDFOperatorNames;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/structures/PDFFlateStream.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/structures/PDFFlateStream.js
var import_pako2 = __toESM(require_pako());
var PDFFlateStream = (
/** @class */
@@ -16156,7 +15842,7 @@ var PDFFlateStream = (
);
var PDFFlateStream_default = PDFFlateStream;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/structures/PDFContentStream.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/structures/PDFContentStream.js
var PDFContentStream = (
/** @class */
(function(_super) {
@@ -16219,7 +15905,7 @@ var PDFContentStream = (
);
var PDFContentStream_default = PDFContentStream;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/utils/rng.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/utils/rng.js
var SimpleRNG = (
/** @class */
(function() {
@@ -16237,7 +15923,7 @@ var SimpleRNG = (
})()
);
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/PDFContext.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/PDFContext.js
var byAscendingObjectNumber = function(_a, _b) {
var a = _a[0];
var b = _b[0];
@@ -16412,7 +16098,7 @@ var PDFContext = (
);
var PDFContext_default = PDFContext;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/structures/PDFPageLeaf.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/structures/PDFPageLeaf.js
var PDFPageLeaf = (
/** @class */
(function(_super) {
@@ -16614,7 +16300,7 @@ var PDFPageLeaf = (
);
var PDFPageLeaf_default = PDFPageLeaf;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/PDFObjectCopier.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/PDFObjectCopier.js
var PDFObjectCopier = (
/** @class */
(function() {
@@ -16698,7 +16384,7 @@ var PDFObjectCopier = (
);
var PDFObjectCopier_default = PDFObjectCopier;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/document/PDFCrossRefSection.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/document/PDFCrossRefSection.js
var PDFCrossRefSection = (
/** @class */
(function() {
@@ -16817,7 +16503,7 @@ var PDFCrossRefSection = (
);
var PDFCrossRefSection_default = PDFCrossRefSection;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/document/PDFTrailer.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/document/PDFTrailer.js
var PDFTrailer = (
/** @class */
(function() {
@@ -16859,7 +16545,7 @@ var PDFTrailer = (
);
var PDFTrailer_default = PDFTrailer;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/document/PDFTrailerDict.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/document/PDFTrailerDict.js
var PDFTrailerDict = (
/** @class */
(function() {
@@ -16893,7 +16579,7 @@ var PDFTrailerDict = (
);
var PDFTrailerDict_default = PDFTrailerDict;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/structures/PDFObjectStream.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/structures/PDFObjectStream.js
var PDFObjectStream = (
/** @class */
(function(_super) {
@@ -16967,7 +16653,7 @@ var PDFObjectStream = (
);
var PDFObjectStream_default = PDFObjectStream;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/writers/PDFWriter.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/writers/PDFWriter.js
var PDFWriter = (
/** @class */
(function() {
@@ -17106,7 +16792,7 @@ var PDFWriter = (
);
var PDFWriter_default = PDFWriter;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/objects/PDFInvalidObject.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/objects/PDFInvalidObject.js
var PDFInvalidObject = (
/** @class */
(function(_super) {
@@ -17140,7 +16826,7 @@ var PDFInvalidObject = (
);
var PDFInvalidObject_default = PDFInvalidObject;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/structures/PDFCrossRefStream.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/structures/PDFCrossRefStream.js
var EntryType;
(function(EntryType2) {
EntryType2[EntryType2["Deleted"] = 0] = "Deleted";
@@ -17320,7 +17006,7 @@ var PDFCrossRefStream = (
);
var PDFCrossRefStream_default = PDFCrossRefStream;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/writers/PDFStreamWriter.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/writers/PDFStreamWriter.js
var PDFStreamWriter = (
/** @class */
(function(_super) {
@@ -17425,7 +17111,7 @@ var PDFStreamWriter = (
);
var PDFStreamWriter_default = PDFStreamWriter;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/objects/PDFHexString.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/objects/PDFHexString.js
var PDFHexString = (
/** @class */
(function(_super) {
@@ -17496,7 +17182,7 @@ var PDFHexString = (
);
var PDFHexString_default = PDFHexString;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/embedders/StandardFontEmbedder.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/embedders/StandardFontEmbedder.js
var StandardFontEmbedder = (
/** @class */
(function() {
@@ -17579,7 +17265,7 @@ var StandardFontEmbedder = (
);
var StandardFontEmbedder_default = StandardFontEmbedder;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/embedders/CMap.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/embedders/CMap.js
var createCmap = function(glyphs, glyphId) {
var bfChars = new Array(glyphs.length);
for (var idx = 0, len = glyphs.length; idx < len; idx++) {
@@ -17619,7 +17305,7 @@ var cmapCodePointFormat = function(codePoint) {
throw new Error(msg);
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/embedders/FontFlags.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/embedders/FontFlags.js
var makeFontFlags = function(options) {
var flags = 0;
var flipBit = function(bit) {
@@ -17657,7 +17343,7 @@ var deriveFontFlags = function(font) {
return flags;
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/objects/PDFString.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/objects/PDFString.js
var PDFString = (
/** @class */
(function(_super) {
@@ -17767,7 +17453,7 @@ var PDFString = (
);
var PDFString_default = PDFString;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/embedders/CustomFontEmbedder.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/embedders/CustomFontEmbedder.js
var CustomFontEmbedder = (
/** @class */
(function() {
@@ -17998,7 +17684,7 @@ var CustomFontEmbedder = (
);
var CustomFontEmbedder_default = CustomFontEmbedder;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/embedders/CustomFontSubsetEmbedder.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/embedders/CustomFontSubsetEmbedder.js
var CustomFontSubsetEmbedder = (
/** @class */
(function(_super) {
@@ -18064,7 +17750,7 @@ var CustomFontSubsetEmbedder = (
);
var CustomFontSubsetEmbedder_default = CustomFontSubsetEmbedder;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/embedders/FileEmbedder.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/embedders/FileEmbedder.js
var AFRelationship;
(function(AFRelationship2) {
AFRelationship2["Source"] = "Source";
@@ -18134,7 +17820,7 @@ var FileEmbedder = (
);
var FileEmbedder_default = FileEmbedder;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/embedders/JpegEmbedder.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/embedders/JpegEmbedder.js
var MARKERS = [
65472,
65473,
@@ -18246,7 +17932,7 @@ var JpegEmbedder = (
);
var JpegEmbedder_default = JpegEmbedder;
-// ../../../../../../../../../../../../node_modules/@pdf-lib/upng/UPNG.js
+// node_modules/.pnpm/@pdf-lib+upng@1.0.1/node_modules/@pdf-lib/upng/UPNG.js
var import_pako4 = __toESM(require_pako());
var UPNG = {};
UPNG.toRGBA8 = function(out) {
@@ -19602,7 +19288,7 @@ UPNG.encode.concatRGBA = function(bufs) {
};
var UPNG_default = UPNG;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/utils/png.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/utils/png.js
var getImageType = function(ctype) {
if (ctype === 0)
return PngType.Greyscale;
@@ -19667,7 +19353,7 @@ var PNG = (
})()
);
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/embedders/PngEmbedder.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/embedders/PngEmbedder.js
var PngEmbedder = (
/** @class */
(function() {
@@ -19733,7 +19419,7 @@ var PngEmbedder = (
);
var PngEmbedder_default = PngEmbedder;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/streams/Stream.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/streams/Stream.js
var Stream = (
/** @class */
(function() {
@@ -19834,7 +19520,7 @@ var Stream = (
);
var Stream_default = Stream;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/streams/DecodeStream.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/streams/DecodeStream.js
var emptyBuffer = new Uint8Array(0);
var DecodeStream = (
/** @class */
@@ -19972,7 +19658,7 @@ var DecodeStream = (
);
var DecodeStream_default = DecodeStream;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/streams/Ascii85Stream.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/streams/Ascii85Stream.js
var isSpace = function(ch) {
return ch === 32 || ch === 9 || ch === 13 || ch === 10;
};
@@ -20047,7 +19733,7 @@ var Ascii85Stream = (
);
var Ascii85Stream_default = Ascii85Stream;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/streams/AsciiHexStream.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/streams/AsciiHexStream.js
var AsciiHexStream = (
/** @class */
(function(_super) {
@@ -20104,7 +19790,7 @@ var AsciiHexStream = (
);
var AsciiHexStream_default = AsciiHexStream;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/streams/FlateStream.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/streams/FlateStream.js
var codeLenCodeMap = new Int32Array([
16,
17,
@@ -20983,7 +20669,7 @@ var FlateStream = (
);
var FlateStream_default = FlateStream;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/streams/LZWStream.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/streams/LZWStream.js
var LZWStream = (
/** @class */
(function(_super) {
@@ -21106,7 +20792,7 @@ var LZWStream = (
);
var LZWStream_default = LZWStream;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/streams/RunLengthStream.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/streams/RunLengthStream.js
var RunLengthStream = (
/** @class */
(function(_super) {
@@ -21148,7 +20834,7 @@ var RunLengthStream = (
);
var RunLengthStream_default = RunLengthStream;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/streams/decode.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/streams/decode.js
var decodeStream = function(stream2, encoding, params) {
if (encoding === PDFName_default.of("FlateDecode")) {
return new FlateStream_default(stream2);
@@ -21191,7 +20877,7 @@ var decodePDFRawStream = function(_a) {
return stream2;
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/embedders/PDFPageEmbedder.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/embedders/PDFPageEmbedder.js
var fullPageBoundingBox = function(page) {
var mediaBox = page.MediaBox();
var width = mediaBox.lookup(2, PDFNumber_default).asNumber() - mediaBox.lookup(0, PDFNumber_default).asNumber();
@@ -21271,7 +20957,7 @@ var PDFPageEmbedder = (
);
var PDFPageEmbedder_default = PDFPageEmbedder;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/interactive/ViewerPreferences.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/interactive/ViewerPreferences.js
var asEnum = function(rawValue, enumType) {
if (rawValue === void 0)
return void 0;
@@ -21505,7 +21191,7 @@ var ViewerPreferences = (
);
var ViewerPreferences_default = ViewerPreferences;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/acroform/PDFAcroField.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/acroform/PDFAcroField.js
var tfRegex = /\/([^\0\t\n\f\r\ ]+)[\0\t\n\f\r\ ]*(\d*\.\d+|\d+)?[\0\t\n\f\r\ ]+Tf/;
var PDFAcroField = (
/** @class */
@@ -21636,7 +21322,7 @@ var PDFAcroField = (
);
var PDFAcroField_default = PDFAcroField;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/annotation/BorderStyle.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/annotation/BorderStyle.js
var BorderStyle = (
/** @class */
(function() {
@@ -21665,7 +21351,7 @@ var BorderStyle = (
);
var BorderStyle_default = BorderStyle;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/annotation/PDFAnnotation.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/annotation/PDFAnnotation.js
var PDFAnnotation = (
/** @class */
(function() {
@@ -21781,7 +21467,7 @@ var PDFAnnotation = (
);
var PDFAnnotation_default = PDFAnnotation;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/annotation/AppearanceCharacteristics.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/annotation/AppearanceCharacteristics.js
var AppearanceCharacteristics = (
/** @class */
(function() {
@@ -21898,7 +21584,7 @@ var AppearanceCharacteristics = (
);
var AppearanceCharacteristics_default = AppearanceCharacteristics;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/annotation/PDFWidgetAnnotation.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/annotation/PDFWidgetAnnotation.js
var PDFWidgetAnnotation = (
/** @class */
(function(_super) {
@@ -22001,7 +21687,7 @@ var PDFWidgetAnnotation = (
);
var PDFWidgetAnnotation_default = PDFWidgetAnnotation;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/acroform/PDFAcroTerminal.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/acroform/PDFAcroTerminal.js
var PDFAcroTerminal = (
/** @class */
(function(_super) {
@@ -22057,7 +21743,7 @@ var PDFAcroTerminal = (
);
var PDFAcroTerminal_default = PDFAcroTerminal;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/acroform/PDFAcroButton.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/acroform/PDFAcroButton.js
var PDFAcroButton = (
/** @class */
(function(_super) {
@@ -22142,7 +21828,7 @@ var PDFAcroButton = (
);
var PDFAcroButton_default = PDFAcroButton;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/acroform/PDFAcroCheckBox.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/acroform/PDFAcroCheckBox.js
var PDFAcroCheckBox = (
/** @class */
(function(_super) {
@@ -22190,7 +21876,7 @@ var PDFAcroCheckBox = (
);
var PDFAcroCheckBox_default = PDFAcroCheckBox;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/acroform/flags.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/acroform/flags.js
var flag = function(bitIndex) {
return 1 << bitIndex;
};
@@ -22227,7 +21913,7 @@ var AcroChoiceFlags;
AcroChoiceFlags2[AcroChoiceFlags2["CommitOnSelChange"] = flag(27 - 1)] = "CommitOnSelChange";
})(AcroChoiceFlags || (AcroChoiceFlags = {}));
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/acroform/PDFAcroChoice.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/acroform/PDFAcroChoice.js
var PDFAcroChoice = (
/** @class */
(function(_super) {
@@ -22344,7 +22030,7 @@ var PDFAcroChoice = (
);
var PDFAcroChoice_default = PDFAcroChoice;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/acroform/PDFAcroComboBox.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/acroform/PDFAcroComboBox.js
var PDFAcroComboBox = (
/** @class */
(function(_super) {
@@ -22369,7 +22055,7 @@ var PDFAcroComboBox = (
);
var PDFAcroComboBox_default = PDFAcroComboBox;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/acroform/PDFAcroNonTerminal.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/acroform/PDFAcroNonTerminal.js
var PDFAcroNonTerminal = (
/** @class */
(function(_super) {
@@ -22402,7 +22088,7 @@ var PDFAcroNonTerminal = (
);
var PDFAcroNonTerminal_default = PDFAcroNonTerminal;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/acroform/PDFAcroSignature.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/acroform/PDFAcroSignature.js
var PDFAcroSignature = (
/** @class */
(function(_super) {
@@ -22418,7 +22104,7 @@ var PDFAcroSignature = (
);
var PDFAcroSignature_default = PDFAcroSignature;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/acroform/PDFAcroText.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/acroform/PDFAcroText.js
var PDFAcroText = (
/** @class */
(function(_super) {
@@ -22483,7 +22169,7 @@ var PDFAcroText = (
);
var PDFAcroText_default = PDFAcroText;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/acroform/PDFAcroPushButton.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/acroform/PDFAcroPushButton.js
var PDFAcroPushButton = (
/** @class */
(function(_super) {
@@ -22508,7 +22194,7 @@ var PDFAcroPushButton = (
);
var PDFAcroPushButton_default = PDFAcroPushButton;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/acroform/PDFAcroRadioButton.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/acroform/PDFAcroRadioButton.js
var PDFAcroRadioButton = (
/** @class */
(function(_super) {
@@ -22562,7 +22248,7 @@ var PDFAcroRadioButton = (
);
var PDFAcroRadioButton_default = PDFAcroRadioButton;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/acroform/PDFAcroListBox.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/acroform/PDFAcroListBox.js
var PDFAcroListBox = (
/** @class */
(function(_super) {
@@ -22586,7 +22272,7 @@ var PDFAcroListBox = (
);
var PDFAcroListBox_default = PDFAcroListBox;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/acroform/utils.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/acroform/utils.js
var createPDFAcroFields = function(kidDicts) {
if (!kidDicts)
return [];
@@ -22673,7 +22359,7 @@ var ascend = function(startNode, visitor) {
ascend(Parent, visitor);
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/acroform/PDFAcroForm.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/acroform/PDFAcroForm.js
var PDFAcroForm = (
/** @class */
(function() {
@@ -22749,7 +22435,7 @@ var PDFAcroForm = (
);
var PDFAcroForm_default = PDFAcroForm;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/structures/PDFCatalog.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/structures/PDFCatalog.js
var PDFCatalog = (
/** @class */
(function(_super) {
@@ -22818,7 +22504,7 @@ var PDFCatalog = (
);
var PDFCatalog_default = PDFCatalog;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/structures/PDFPageTree.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/structures/PDFPageTree.js
var PDFPageTree = (
/** @class */
(function(_super) {
@@ -22960,7 +22646,7 @@ var PDFPageTree = (
);
var PDFPageTree_default = PDFPageTree;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/syntax/Numeric.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/syntax/Numeric.js
var IsDigit = new Uint8Array(256);
IsDigit[CharCodes_default.Zero] = 1;
IsDigit[CharCodes_default.One] = 1;
@@ -22983,7 +22669,7 @@ for (idx = 0, len = 256; idx < len; idx++) {
var idx;
var len;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/parser/BaseParser.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/parser/BaseParser.js
var Newline = CharCodes_default.Newline;
var CarriageReturn = CharCodes_default.CarriageReturn;
var BaseParser = (
@@ -23086,7 +22772,7 @@ var BaseParser = (
);
var BaseParser_default = BaseParser;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/parser/ByteStream.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/parser/ByteStream.js
var ByteStream = (
/** @class */
(function() {
@@ -23148,7 +22834,7 @@ var ByteStream = (
);
var ByteStream_default = ByteStream;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/syntax/Keywords.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/syntax/Keywords.js
var Space = CharCodes_default.Space;
var CarriageReturn2 = CharCodes_default.CarriageReturn;
var Newline2 = CharCodes_default.Newline;
@@ -23230,7 +22916,7 @@ var Keywords = {
EOF3endstream: __spreadArrays([Newline2], endstream)
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/parser/PDFObjectParser.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/parser/PDFObjectParser.js
var PDFObjectParser = (
/** @class */
(function(_super) {
@@ -23421,7 +23107,7 @@ var PDFObjectParser = (
);
var PDFObjectParser_default = PDFObjectParser;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/parser/PDFObjectStreamParser.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/parser/PDFObjectStreamParser.js
var PDFObjectStreamParser = (
/** @class */
(function(_super) {
@@ -23493,7 +23179,7 @@ var PDFObjectStreamParser = (
);
var PDFObjectStreamParser_default = PDFObjectStreamParser;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/parser/PDFXRefStreamParser.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/parser/PDFXRefStreamParser.js
var PDFXRefStreamParser = (
/** @class */
(function() {
@@ -23574,7 +23260,7 @@ var PDFXRefStreamParser = (
);
var PDFXRefStreamParser_default = PDFXRefStreamParser;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/parser/PDFParser.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/parser/PDFParser.js
var PDFParser = (
/** @class */
(function(_super) {
@@ -23890,7 +23576,7 @@ var PDFParser = (
);
var PDFParser_default = PDFParser;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/annotation/flags.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/annotation/flags.js
var flag2 = function(bitIndex) {
return 1 << bitIndex;
};
@@ -23908,7 +23594,7 @@ var AnnotationFlags;
AnnotationFlags2[AnnotationFlags2["LockedContents"] = flag2(10 - 1)] = "LockedContents";
})(AnnotationFlags || (AnnotationFlags = {}));
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/objects.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/objects.js
var asPDFName = function(name) {
return name instanceof PDFName_default ? name : PDFName_default.of(name);
};
@@ -23919,7 +23605,7 @@ var asNumber = function(num) {
return num instanceof PDFNumber_default ? num.asNumber() : num;
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/rotations.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/rotations.js
var RotationTypes;
(function(RotationTypes2) {
RotationTypes2["Degrees"] = "degrees";
@@ -23987,7 +23673,7 @@ var rotateRectangle = function(rectangle, borderWidth, degreeAngle) {
return { x: x - b, y: y - b, width: w, height: h };
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/operators.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/operators.js
var clip = function() {
return PDFOperator_default.of(PDFOperatorNames_default.ClipNonZero);
};
@@ -24179,7 +23865,7 @@ var endMarkedContent = function() {
return PDFOperator_default.of(PDFOperatorNames_default.EndMarkedContent);
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/colors.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/colors.js
var ColorTypes;
(function(ColorTypes2) {
ColorTypes2["Grayscale"] = "Grayscale";
@@ -24222,7 +23908,7 @@ var colorToComponents = function(color) {
return color.type === Grayscale ? [color.gray] : color.type === RGB ? [color.red, color.green, color.blue] : color.type === CMYK ? [color.cyan, color.magenta, color.yellow, color.key] : error("Invalid color: " + JSON.stringify(color));
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/svgPath.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/svgPath.js
var cx = 0;
var cy = 0;
var px = 0;
@@ -24576,7 +24262,7 @@ var svgPathToOperators = function(path) {
return apply(parse(path));
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/operations.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/operations.js
var drawLinesOfText = function(lines, options) {
var operators = [
pushGraphicsState(),
@@ -25002,7 +24688,7 @@ var drawOptionList = function(options) {
]);
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/errors.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/errors.js
var EncryptedPDFError = (
/** @class */
(function(_super) {
@@ -25189,7 +24875,7 @@ var InvalidMaxLengthError = (
})(Error)
);
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/text/alignment.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/text/alignment.js
var TextAlignment;
(function(TextAlignment2) {
TextAlignment2[TextAlignment2["Left"] = 0] = "Left";
@@ -25197,7 +24883,7 @@ var TextAlignment;
TextAlignment2[TextAlignment2["Right"] = 2] = "Right";
})(TextAlignment || (TextAlignment = {}));
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/text/layout.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/text/layout.js
var MIN_FONT_SIZE = 4;
var MAX_FONT_SIZE = 500;
var computeFontSize = function(lines, font, bounds, multiline) {
@@ -25390,7 +25076,7 @@ var layoutSinglelineText = function(text2, _a) {
};
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/form/appearances.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/form/appearances.js
var normalizeAppearance = function(appearance) {
if ("normal" in appearance)
return appearance;
@@ -25775,7 +25461,7 @@ var defaultOptionListAppearanceProvider = function(optionList, widget, font) {
}));
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/PDFEmbeddedPage.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/PDFEmbeddedPage.js
var PDFEmbeddedPage = (
/** @class */
(function() {
@@ -25825,7 +25511,7 @@ var PDFEmbeddedPage = (
);
var PDFEmbeddedPage_default = PDFEmbeddedPage;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/PDFFont.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/PDFFont.js
var PDFFont = (
/** @class */
(function() {
@@ -25899,7 +25585,7 @@ var PDFFont = (
);
var PDFFont_default = PDFFont;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/PDFImage.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/PDFImage.js
var PDFImage = (
/** @class */
(function() {
@@ -25966,7 +25652,7 @@ var PDFImage = (
);
var PDFImage_default = PDFImage;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/image/alignment.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/image/alignment.js
var ImageAlignment;
(function(ImageAlignment2) {
ImageAlignment2[ImageAlignment2["Left"] = 0] = "Left";
@@ -25974,7 +25660,7 @@ var ImageAlignment;
ImageAlignment2[ImageAlignment2["Right"] = 2] = "Right";
})(ImageAlignment || (ImageAlignment = {}));
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/form/PDFField.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/form/PDFField.js
var assertFieldAppearanceOptions = function(options) {
assertOrUndefined(options === null || options === void 0 ? void 0 : options.x, "options.x", ["number"]);
assertOrUndefined(options === null || options === void 0 ? void 0 : options.y, "options.y", ["number"]);
@@ -26187,7 +25873,7 @@ var PDFField = (
);
var PDFField_default = PDFField;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/form/PDFCheckBox.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/form/PDFCheckBox.js
var PDFCheckBox = (
/** @class */
(function(_super) {
@@ -26290,7 +25976,7 @@ var PDFCheckBox = (
);
var PDFCheckBox_default = PDFCheckBox;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/form/PDFDropdown.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/form/PDFDropdown.js
var PDFDropdown = (
/** @class */
(function(_super) {
@@ -26493,7 +26179,7 @@ var PDFDropdown = (
);
var PDFDropdown_default = PDFDropdown;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/form/PDFOptionList.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/form/PDFOptionList.js
var PDFOptionList = (
/** @class */
(function(_super) {
@@ -26674,7 +26360,7 @@ var PDFOptionList = (
);
var PDFOptionList_default = PDFOptionList;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/form/PDFRadioGroup.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/form/PDFRadioGroup.js
var PDFRadioGroup = (
/** @class */
(function(_super) {
@@ -26825,7 +26511,7 @@ var PDFRadioGroup = (
);
var PDFRadioGroup_default = PDFRadioGroup;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/form/PDFSignature.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/form/PDFSignature.js
var PDFSignature = (
/** @class */
(function(_super) {
@@ -26849,7 +26535,7 @@ var PDFSignature = (
);
var PDFSignature_default = PDFSignature;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/form/PDFTextField.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/form/PDFTextField.js
var PDFTextField = (
/** @class */
(function(_super) {
@@ -27073,7 +26759,7 @@ var PDFTextField = (
);
var PDFTextField_default = PDFTextField;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/StandardFonts.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/StandardFonts.js
var StandardFonts;
(function(StandardFonts2) {
StandardFonts2["Courier"] = "Courier";
@@ -27092,7 +26778,7 @@ var StandardFonts;
StandardFonts2["ZapfDingbats"] = "ZapfDingbats";
})(StandardFonts || (StandardFonts = {}));
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/form/PDFForm.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/form/PDFForm.js
var PDFForm = (
/** @class */
(function() {
@@ -27449,7 +27135,7 @@ var addFieldToParent = function(_a, _b, partialName) {
field.setParent(parentRef);
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/sizes.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/sizes.js
var PageSizes = {
"4A0": [4767.87, 6740.79],
"2A0": [3370.39, 4767.87],
@@ -27503,7 +27189,7 @@ var PageSizes = {
Tabloid: [792, 1224]
};
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/PDFDocumentOptions.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/PDFDocumentOptions.js
var ParseSpeeds;
(function(ParseSpeeds2) {
ParseSpeeds2[ParseSpeeds2["Fastest"] = Infinity] = "Fastest";
@@ -27512,7 +27198,7 @@ var ParseSpeeds;
ParseSpeeds2[ParseSpeeds2["Slow"] = 100] = "Slow";
})(ParseSpeeds || (ParseSpeeds = {}));
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/PDFEmbeddedFile.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/PDFEmbeddedFile.js
var PDFEmbeddedFile = (
/** @class */
(function() {
@@ -27570,7 +27256,7 @@ var PDFEmbeddedFile = (
);
var PDFEmbeddedFile_default = PDFEmbeddedFile;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/PDFJavaScript.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/PDFJavaScript.js
var PDFJavaScript = (
/** @class */
(function() {
@@ -27624,7 +27310,7 @@ var PDFJavaScript = (
);
var PDFJavaScript_default = PDFJavaScript;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/core/embedders/JavaScriptEmbedder.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/core/embedders/JavaScriptEmbedder.js
var JavaScriptEmbedder = (
/** @class */
(function() {
@@ -27662,7 +27348,7 @@ var JavaScriptEmbedder = (
);
var JavaScriptEmbedder_default = JavaScriptEmbedder;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/PDFDocument.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/PDFDocument.js
var PDFDocument = (
/** @class */
(function() {
@@ -28348,7 +28034,7 @@ function assertIsLiteralOrHexString(pdfObject) {
}
}
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/PDFPageOptions.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/PDFPageOptions.js
var BlendMode;
(function(BlendMode2) {
BlendMode2["Normal"] = "Normal";
@@ -28365,7 +28051,7 @@ var BlendMode;
BlendMode2["Exclusion"] = "Exclusion";
})(BlendMode || (BlendMode = {}));
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/PDFPage.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/PDFPage.js
var PDFPage = (
/** @class */
(function() {
@@ -29017,7 +28703,7 @@ var PDFPage = (
);
var PDFPage_default = PDFPage;
-// ../../../../../../../../../../../../node_modules/pdf-lib/es/api/form/PDFButton.js
+// node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/form/PDFButton.js
var PDFButton = (
/** @class */
(function(_super) {
@@ -29111,11 +28797,11 @@ var PDFButton = (
);
var PDFButton_default = PDFButton;
-// src/export-deck-browser.js
+// src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/export-deck-browser.js
var import_jszip2 = __toESM(require_jszip_min(), 1);
-// ../../../../../../../../../node_modules/.pnpm/pptxgenjs@4.0.1/node_modules/pptxgenjs/dist/pptxgen.es.js
-var import_jszip = __toESM(require_jszip_min2());
+// node_modules/.pnpm/pptxgenjs@4.0.1/node_modules/pptxgenjs/dist/pptxgen.es.js
+var import_jszip = __toESM(require_jszip_min());
function __awaiter2(thisArg, _arguments, P, generator) {
function adopt(value2) {
return value2 instanceof P ? value2 : new P(function(resolve) {
@@ -34434,11 +34120,24 @@ var PptxGenJS = class {
}
};
-// src/pptx-html-build.js
+// src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/pptx-html-build.js
var PX_PER_IN = 96;
var EMU_PER_IN = 914400;
var SLIDE_W_IN = 13.333;
var SLIDE_H_IN = 7.5;
+var EDITABLE_TEXT_TYPES2 = /* @__PURE__ */ new Set([
+ "p",
+ "h1",
+ "h2",
+ "h3",
+ "h4",
+ "h5",
+ "h6",
+ "text",
+ "svg-text",
+ "list",
+ "merged-text"
+]);
var WIDTH_SAFETY_IN = 0.15;
function capTextBoxWidth(x, w) {
return Math.min(w, Math.max(0.15, SLIDE_W_IN - x - 0.02));
@@ -34516,7 +34215,62 @@ async function addBackground(slideData, targetSlide) {
}
}
function addElements(slideData, targetSlide, pres) {
- for (const el of slideData.elements) {
+ if (slideData.fullPageFallback) {
+ const payload = toImagePayload(
+ slideData.fullPageFallback.data || slideData.fullPageFallback.src
+ );
+ if (!payload) throw new Error("Full-page fallback has no image payload");
+ targetSlide.addImage({
+ ...payload,
+ x: 0,
+ y: 0,
+ w: SLIDE_W_IN,
+ h: SLIDE_H_IN
+ });
+ return;
+ }
+ const suppressedNativeVisualIds = /* @__PURE__ */ new Set([
+ ...slideData.suppressedNativeVisualIds || [],
+ ...(slideData.fallbackLayers || []).flatMap((layer) => layer.suppressedNativeVisualIds || [])
+ ]);
+ const paintItems = [
+ ...(slideData.elements || []).map((element2, order) => ({
+ type: "element",
+ item: element2,
+ zIndex: element2.zIndex ?? 0,
+ order: element2.paintOrder ?? order,
+ subOrder: element2.subOrder ?? 0,
+ stableOrder: order
+ })),
+ ...(slideData.fallbackLayers || []).map((layer, order) => ({
+ type: "fallback",
+ item: layer,
+ zIndex: layer.zIndex ?? 0,
+ order: layer.paintOrder ?? (slideData.elements || []).length + order,
+ subOrder: layer.subOrder ?? 0,
+ stableOrder: (slideData.elements || []).length + order
+ }))
+ ].sort((a, b) => a.zIndex - b.zIndex || a.order - b.order || a.subOrder - b.subOrder || a.stableOrder - b.stableOrder);
+ for (const paintItem of paintItems) {
+ if (paintItem.type === "fallback") {
+ const layer = paintItem.item;
+ const payload = toImagePayload(layer.data || layer.src);
+ const bbox = layer.bbox || {};
+ if (!payload) continue;
+ const fullPageCanvas = layer.canvas === "full-page";
+ targetSlide.addImage({
+ ...payload,
+ x: fullPageCanvas ? 0 : bbox.x ?? 0,
+ y: fullPageCanvas ? 0 : bbox.y ?? 0,
+ w: fullPageCanvas ? SLIDE_W_IN : bbox.w ?? SLIDE_W_IN,
+ h: fullPageCanvas ? SLIDE_H_IN : bbox.h ?? SLIDE_H_IN
+ });
+ continue;
+ }
+ const el = paintItem.item;
+ if (suppressedNativeVisualIds.has(el.sourceId) && !EDITABLE_TEXT_TYPES2.has(el.type)) {
+ continue;
+ }
if (el.type === "image") {
const payload = toImagePayload(el.src);
if (!payload) continue;
@@ -34535,14 +34289,21 @@ function addElements(slideData, targetSlide, pres) {
h: el.y2 - el.y1,
line: { color: el.color, width: el.width }
});
- } else if (el.type === "shape") {
+ } else if (el.type === "shape" || el.type === "svg-shape") {
const shapeOptions = {
x: el.position.x,
y: el.position.y,
w: el.position.w,
- h: el.position.h,
- shape: el.shape.rectRadius > 0 ? pres.ShapeType.roundRect : pres.ShapeType.rect
+ h: el.position.h
};
+ const nativeShapeType = {
+ circle: pres.ShapeType.ellipse,
+ ellipse: pres.ShapeType.ellipse,
+ triangle: pres.ShapeType.triangle,
+ diamond: pres.ShapeType.diamond,
+ rect: pres.ShapeType.rect
+ }[el.svgType];
+ shapeOptions.shape = nativeShapeType || (el.shape.rectRadius > 0 ? pres.ShapeType.roundRect : pres.ShapeType.rect);
if (el.shape.fill) {
shapeOptions.fill = { color: el.shape.fill };
if (el.shape.transparency != null) shapeOptions.fill.transparency = el.shape.transparency;
@@ -34550,7 +34311,12 @@ function addElements(slideData, targetSlide, pres) {
if (el.shape.line) shapeOptions.line = el.shape.line;
if (el.shape.rectRadius > 0) shapeOptions.rectRadius = el.shape.rectRadius;
if (el.shape.shadow) shapeOptions.shadow = el.shape.shadow;
- targetSlide.addText(el.text || "", shapeOptions);
+ if (el.shape.rotate != null) shapeOptions.rotate = el.shape.rotate;
+ if (el.type === "svg-shape") {
+ targetSlide.addShape(shapeOptions.shape, shapeOptions);
+ } else {
+ targetSlide.addText(el.text || "", shapeOptions);
+ }
} else if (el.type === "list" || el.type === "merged-text") {
const { x: boxX, w: boxW } = safeTextBoxGeometry(el.position.x, el.position.w, el.style.align, false);
const listOptions = {
@@ -34616,10 +34382,38 @@ async function buildSlideFromExtracted(slideData, bodyDimensions, pres, options
if (validationWarnings.length) {
console.warn("[ppt-live-export] slide validation warnings (export continues):", validationWarnings.join("; "));
}
+ const diagnostics = [
+ ...slideData?.diagnostics || [],
+ ...validationWarnings.map((message) => ({
+ severity: "fallback",
+ code: "pptx_layout_warning",
+ message,
+ sourceId: null,
+ tag: null
+ }))
+ ];
const targetSlide = options.slide || pres.addSlide();
- await addBackground(slideData, targetSlide);
- addElements(slideData, targetSlide, pres);
- return { slide: targetSlide, placeholders: slideData.placeholders || [] };
+ try {
+ await addBackground(slideData, targetSlide);
+ addElements(slideData, targetSlide, pres);
+ } catch (error2) {
+ const diagnostic = {
+ severity: "blocking",
+ kind: "blocking",
+ code: "pptx_serialization",
+ message: String(error2?.message || error2 || "PPTX serialization failed."),
+ sourceId: null,
+ tag: null
+ };
+ error2.diagnostic = diagnostic;
+ error2.diagnostics = [...diagnostics, diagnostic];
+ throw error2;
+ }
+ return {
+ slide: targetSlide,
+ placeholders: slideData.placeholders || [],
+ diagnostics
+ };
}
function createPptxDeck(deck = {}) {
const pptx = new PptxGenJS();
@@ -34646,7 +34440,7 @@ function buildSpeakerNotes(sourceSlide = {}) {
].filter(Boolean).join("\n\n");
}
-// src/pptx-element-export.js
+// src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/pptx-element-export.js
var SLIDE_W = 13.333;
var SLIDE_H = 7.5;
function drawSlideBackdrop(pptx, slide, theme, index) {
@@ -34739,7 +34533,7 @@ function drawElement(pptx, slide, element2, theme) {
h: box.h,
margin: 0.08,
fit: "shrink",
- color: hex(resolveColor2(style.color, theme)),
+ color: hex(resolveColor(style.color, theme)),
fontFace: "Aptos",
fontSize: pxToPt(style.fontSize || 22),
bold: Number(style.fontWeight || 500) >= 700,
@@ -34751,8 +34545,8 @@ function drawElement(pptx, slide, element2, theme) {
slide.addShape(pptx.ShapeType.roundRect, {
...box,
rectRadius: 0.08,
- fill: { color: hex(resolveColor2(style.background, theme)), transparency: transparency(style.opacity) },
- line: { color: hex(resolveColor2(style.background, theme)), transparency: 100 }
+ fill: { color: hex(resolveColor(style.background, theme)), transparency: transparency(style.opacity) },
+ line: { color: hex(resolveColor(style.background, theme)), transparency: 100 }
});
if (element2.text) slide.addText(element2.text, common);
return;
@@ -34773,7 +34567,7 @@ function drawElement(pptx, slide, element2, theme) {
...common,
y: box.y + 0.08,
h: box.h * 0.48,
- color: hex(resolveColor2(style.color || "primary", theme)),
+ color: hex(resolveColor(style.color || "primary", theme)),
fontSize: pxToPt(style.fontSize || 42),
bold: true
});
@@ -34803,7 +34597,7 @@ function drawElement(pptx, slide, element2, theme) {
if (element2.type === "media") {
slide.addShape(pptx.ShapeType.roundRect, {
...box,
- fill: { color: hex(resolveColor2(style.background || "soft", theme)), transparency: 10 },
+ fill: { color: hex(resolveColor(style.background || "soft", theme)), transparency: 10 },
line: { color: hex(theme.primary), transparency: 55, dash: "dash" }
});
slide.addText(String(element2.text || "Image placeholder"), {
@@ -34820,7 +34614,7 @@ function drawElement(pptx, slide, element2, theme) {
function drawPanel(pptx, slide, box, style, theme) {
slide.addShape(pptx.ShapeType.roundRect, {
...box,
- fill: { color: hex(resolveColor2(style.background || "panel", theme)), transparency: transparency(style.opacity) },
+ fill: { color: hex(resolveColor(style.background || "panel", theme)), transparency: transparency(style.opacity) },
line: { color: hex(theme.primary), transparency: 82 },
shadow: { type: "outer", color: "111827", opacity: 0.12, blur: 1, angle: 45, distance: 1 }
});
@@ -34830,8 +34624,8 @@ function drawTextBackground(pptx, slide, box, style, theme) {
if (bg === "transparent") return;
slide.addShape(pptx.ShapeType.roundRect, {
...box,
- fill: { color: hex(resolveColor2(bg, theme)), transparency: transparency(style.opacity) },
- line: { color: hex(resolveColor2(bg, theme)), transparency: 100 }
+ fill: { color: hex(resolveColor(bg, theme)), transparency: transparency(style.opacity) },
+ line: { color: hex(resolveColor(bg, theme)), transparency: 100 }
});
}
function drawBars(pptx, slide, element2, box, theme) {
@@ -34893,7 +34687,7 @@ function pct(value2) {
function pxToPt(value2) {
return Math.max(6, Math.min(66, Math.round((Number(value2) || 22) * 0.58)));
}
-function resolveColor2(value2, theme) {
+function resolveColor(value2, theme) {
if (!value2 || value2 === "transparent") return theme.background;
if (value2 === "ink") return theme.ink;
if (value2 === "muted") return theme.muted;
@@ -34932,7 +34726,7 @@ async function exportElementDeckToPptx(deck) {
return pptx;
}
-// src/export-deck-browser.js
+// src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/export-deck-browser.js
var MIME_PPTX = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
var RASTER_TEXT_TYPES2 = /* @__PURE__ */ new Set(["p", "h1", "h2", "h3", "h4", "h5", "h6", "text", "list", "merged-text"]);
function filterSlideDataForRasterBackdrop(slideData) {
@@ -35039,7 +34833,7 @@ async function exportPngZipFromPages(deck, pages) {
};
}
-// src/export-html.js
+// src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/export-html.js
function buildHtmlDeck(state2) {
if ((state2.slides || []).some((slide) => slide.html)) {
return buildSourceHtmlDeck(state2);
@@ -35062,7 +34856,7 @@ ${deckCss()}
}
function buildSourceHtmlDeck(state2) {
const slides = (state2.slides || []).map((slide, index) => ``).join("\n");
return `
@@ -35128,7 +34922,7 @@ body{margin:0;background:#111827;font-family:-apple-system,BlinkMacSystemFont,"S
`;
}
-// src/export-format-icons.js
+// src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/export-format-icons.js
var SVG_ATTRS = 'xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false"';
var ICONS = {
pptx: ``,
@@ -35149,17 +34943,8 @@ function exportFormatTone(formatId) {
return tones[formatId] || "#475569";
}
-// src/bitfun-backend-adapter.js
-var EVENT_LISTENERS = /* @__PURE__ */ new Set();
+// src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/agent-prompt.js
var PPT_DESIGN_SKILL_KEY = "user::bitfun-system::ppt-design";
-function emitEvent(event) {
- EVENT_LISTENERS.forEach((listener) => {
- try {
- listener(event);
- } catch {
- }
- });
-}
function serializeInput(input) {
try {
return JSON.stringify(input ?? {}, null, 2);
@@ -35183,23 +34968,37 @@ function describeStyle(style = {}) {
if (style.stylePreset) parts.push(`\u98CE\u683C\u9884\u8BBE: ${style.stylePreset}`);
return parts.length ? parts.join("\u3001") : "";
}
+function formatContractDiagnostic(diagnostic) {
+ if (!diagnostic) return "";
+ if (typeof diagnostic === "string") return diagnostic.trim();
+ const code = String(diagnostic.code || "unknown_contract_error");
+ const continuation = String(diagnostic.continuationPrompt || "").trim();
+ return [`\u8BCA\u65AD\u4EE3\u7801\uFF1A${code}`, continuation].filter(Boolean).join("\n");
+}
function buildAgentPrompt(input) {
const hasDeck = hasCurrentDeck(input);
const styleLine = describeStyle(input?.style);
const instruction = input?.instruction || input?.userInput || "";
- let prompt = hasDeck ? `\u4F7F\u7528 PPT-Design skill \u7F16\u8F91\u73B0\u6709 PPT\u3002\u7F16\u8F91\u6307\u4EE4\uFF1A${instruction || "\uFF08\u89C1 currentDeck \u4E0A\u4E0B\u6587\uFF09"}\u3002` : `\u4F7F\u7528 PPT-Design skill \u751F\u6210 PPT\u3002\u7528\u6237\u9700\u6C42\uFF1A${instruction || "\uFF08\u89C1 input JSON\uFF09"}\u3002`;
- if (styleLine) {
- prompt += `
+ let prompt = hasDeck ? `\u7F16\u8F91\u73B0\u6709 PPT\u3002\u7F16\u8F91\u6307\u4EE4\uFF1A${instruction || "\uFF08\u89C1 currentDeck \u4E0A\u4E0B\u6587\uFF09"}\u3002` : `\u751F\u6210 PPT\u3002\u7528\u6237\u9700\u6C42\uFF1A${instruction || "\uFF08\u89C1 input JSON\uFF09"}\u3002`;
+ prompt = `\u5148\u8C03\u7528 Skill\uFF0C\u5E76\u4E14 skill key \u5FC5\u987B\u7CBE\u786E\u4E3A \`${PPT_DESIGN_SKILL_KEY}\`\u3002
+${prompt}`;
+ if (styleLine) prompt += `
\u6837\u5F0F\u504F\u597D\uFF1A${styleLine}\u3002`;
- }
prompt += `
+## \u751F\u6210\u6587\u4EF6\u534F\u8BAE
+
+- \u5F53\u524D agent \u5DE5\u4F5C\u533A\u6839\u76EE\u5F55\u5C31\u662F deck \u6839\u76EE\u5F55\uFF1B\u6240\u6709\u8DEF\u5F84\u5747\u76F8\u5BF9\u8BE5\u5DE5\u4F5C\u533A\u6839\u76EE\u5F55\u3002
+- \u5148\u5199\u5DE5\u4F5C\u533A\u6839\u76EE\u5F55\u4E0B\u7684 \`project.json\`\uFF0C\u518D\u5199\u5DE5\u4F5C\u533A\u6839\u76EE\u5F55\u4E0B\u7684 \`slides/slide-NN.html\`\u3002
+- \u53EA\u6709\u5728 \`slide_order\` \u5F15\u7528\u7684\u6BCF\u4E00\u9875\u90FD\u5DF2\u6709\u5B8C\u6574 HTML \u540E\uFF0C\u624D\u5C06 \`project.json\` \u7684 \`status\` \u8BBE\u4E3A \`"complete"\`\u3002
+- \u5B8C\u6210\u524D\u505A\u4E00\u6B21\u6709\u754C\u68C0\u67E5\uFF1A\u6838\u5BF9 \`outline[].slide_id\`\u3001\`slide_order\` \u548C\u5BF9\u5E94\u9875\u9762\u6587\u4EF6\uFF1B\u7F3A\u4EC0\u4E48\u53EA\u8865\u4EC0\u4E48\uFF0C\u68C0\u67E5\u540E\u7ACB\u5373\u7ED3\u675F\u3002
+
## \u7EA6\u675F
- \u7528\u6237\u53EA\u80FD\u770B\u5230 PPT Live UI\uFF0C\u65E0\u6CD5\u56DE\u7B54\u63D0\u95EE\u3002\u5982\u6709\u6B67\u4E49\u81EA\u884C\u5224\u65AD\u6700\u4F18\u65B9\u6848\u5E76\u8BB0\u5F55\u5047\u8BBE\u3002
- \u4E0D\u8981\u8C03\u7528 AskUserQuestion\u3001ControlHub\u3001GenerativeUI\u3001ComputerUse \u7B49\u4EA4\u4E92\u5DE5\u5177\u3002
- \u7814\u7A76\u7528 WebSearch / WebFetch \u5373\u53EF\u3002
-- **\u4E00\u6B21\u5199\u5BF9\uFF0C\u7981\u6B62\u4E8B\u540E\u5BA1\u8BA1**\uFF1A\u6BCF\u9875 HTML \u5728\u5199\u5165\u65F6\u5C31\u8981\u6EE1\u8DB3\u6240\u6709\u7EA6\u675F\uFF08\u753B\u5E03\u5C3A\u5BF8\u3001\u56DB\u6761 OOXML \u786C\u7EA6\u675F\u3001\u9632\u6EA2\u51FA\u9884\u7B97\uFF09\u3002\u6240\u6709\u9875\u9762\u5199\u5B8C\u540E\u4E0D\u5F97\u518D\u9010\u9875 Read\u2192Edit \u8FD4\u5DE5\u6216 Grep \u6279\u91CF\u68C0\u67E5\u3002\u5199\u5B8C\u5373\u7ED3\u675F\u3002
+- **\u4E00\u6B21\u5199\u5BF9\uFF0C\u7981\u6B62\u4E8B\u540E\u5BA1\u8BA1**\uFF1A\u6BCF\u9875 HTML \u5728\u5199\u5165\u65F6\u5C31\u8981\u6EE1\u8DB3\u6240\u6709\u7EA6\u675F\uFF08\u753B\u5E03\u5C3A\u5BF8\u3001\u56DB\u6761 OOXML \u786C\u7EA6\u675F\u3001\u9632\u6EA2\u51FA\u9884\u7B97\uFF09\u3002\u5B8C\u6210\u68C0\u67E5\u53EA\u6838\u5BF9\u751F\u6210\u6587\u4EF6\u534F\u8BAE\uFF0C\u4E0D\u9010\u9875 Read\u2192Edit \u8FD4\u5DE5\u6216 Grep \u6279\u91CF\u5BA1\u8BA1\u9875\u9762\u5185\u5BB9\u3002
`;
if (hasDeck) {
prompt += `
@@ -35214,14 +35013,30 @@ function buildAgentPrompt(input) {
Input JSON:
\`\`\`json
${serializeInput(input)}
-\`\``;
+\`\`\``;
if (input?.continueAfterInterruption) {
- prompt = `\u4E0A\u4E00\u6B21\u751F\u6210\u88AB\u4E2D\u65AD\u4E86\u3002\u8BF7\u7EE7\u7EED\u5B8C\u6210\u4EFB\u52A1\uFF1A\u68C0\u67E5 project.json \u548C\u5DF2\u5199\u7684 slides/ \u6587\u4EF6\uFF0C\u53EA\u8865\u5199\u8FD8\u6CA1\u5B8C\u6210\u7684\u9875\u9762\uFF0C\u4E0D\u8981\u91CD\u5199\u5DF2\u6709\u7684\u9875\u9762\u3002
+ const diagnostic = formatContractDiagnostic(input.projectContractDiagnostic);
+ prompt = `\u4E0A\u4E00\u6B21\u751F\u6210\u88AB\u4E2D\u65AD\u6216\u672A\u901A\u8FC7\u6587\u4EF6\u5951\u7EA6\u3002\u8BF7\u5728\u540C\u4E00\u4F1A\u8BDD\u4E2D\u5B9A\u5411\u7EED\u8DD1\uFF0C\u4E0D\u8981\u91CD\u5199\u5DF2\u5B8C\u6210\u9875\u9762\u3002
+${diagnostic ? `
+${diagnostic}
+` : ""}
+\u68C0\u67E5 \`project.json\` \u548C\u5DF2\u5199\u7684 \`slides/\` \u6587\u4EF6\uFF0C\u53EA\u4FEE\u590D\u8BCA\u65AD\u6307\u51FA\u7684\u5185\u5BB9\uFF1B\u5B8C\u6210\u540E\u6267\u884C\u4E00\u6B21\u6709\u754C\u68C0\u67E5\u3002
${prompt}`;
}
return prompt;
}
+
+// src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/bitfun-backend-adapter.js
+var EVENT_LISTENERS = /* @__PURE__ */ new Set();
+function emitEvent(event) {
+ EVENT_LISTENERS.forEach((listener) => {
+ try {
+ listener(event);
+ } catch {
+ }
+ });
+}
function installAgentBackend(app) {
let agentEventsHooked = false;
const ensureAgentEvents = () => {
@@ -35233,23 +35048,16 @@ function installAgentBackend(app) {
});
};
app.backend = {
- // The agent delivers through project files written using the ppt-design
- // skill's native workflow; 'files' tells ui.js to read them back.
protocol: "files",
async call(action, input, options = {}) {
if (action !== "ppt.generate") {
throw new Error(`Unsupported PPT Live action: ${action}`);
}
ensureAgentEvents();
- const prompt = buildAgentPrompt(input);
- const result = await app.agent.run(prompt, {
+ const result = await app.agent.run(buildAgentPrompt(input), {
runId: options.idempotencyKey,
sessionName: "PPT Live",
- // Reuse the session when the caller carries one so follow-up edits
- // resume with the loaded skill/preset/research context.
sessionId: options.sessionId,
- // The agent works inside a dedicated deck project directory under
- // the app's own appdata storage (never the user's workspace).
appDataWorkspace: options.appDataWorkspace
});
if (!result?.sessionId || !result?.turnId) {
@@ -35281,12 +35089,440 @@ function installAgentBackend(app) {
}
function installBitFunBackendAdapter(app = window.app) {
if (!app || app.backend?.call) return;
- if (app.agent?.run) {
- installAgentBackend(app);
+ if (app.agent?.run) installAgentBackend(app);
+}
+
+// src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/deck-project-contract.js
+var DeckProjectContractError = class extends Error {
+ constructor(diagnostic) {
+ super(`[${diagnostic.code}] ${diagnostic.summary}`);
+ this.name = "DeckProjectContractError";
+ this.diagnostic = diagnostic;
+ }
+};
+function contractError(code, summary, continuationPrompt, details = {}) {
+ return new DeckProjectContractError({
+ code,
+ summary,
+ continuationPrompt,
+ ...details
+ });
+}
+function missingSlideFilesDiagnostic(missingPaths) {
+ return {
+ code: "missing_slide_files",
+ summary: `Missing or incomplete slide files: ${missingPaths.join(", ")}`,
+ continuationPrompt: `\u53EA\u8865\u5199\u8FD9\u4E9B\u7F3A\u5931\u6216\u4E0D\u5B8C\u6574\u9875\u9762\uFF1A${missingPaths.join("\u3001")}\u3002\u4FDD\u7559\u5176\u4ED6\u9875\u9762\u4E0D\u53D8\uFF1B\u8865\u9F50\u540E\u518D\u628A\u72B6\u6001\u786E\u8BA4\u4E3A complete \u5E76\u6267\u884C\u4E00\u6B21\u6709\u754C\u68C0\u67E5\u3002`,
+ missingPaths
+ };
+}
+var defaultSleep = (delayMs) => new Promise((resolve) => setTimeout(resolve, delayMs));
+async function readVisibleFileWithRetry(readFile, relPath, {
+ maxAttempts = 6,
+ delayMs = 120,
+ sleep = defaultSleep,
+ accept
+} = {}) {
+ let lastValue = "";
+ let lastError = null;
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
+ try {
+ lastValue = String(await readFile(relPath) || "");
+ if (accept(lastValue)) return lastValue;
+ } catch (error2) {
+ lastError = error2;
+ }
+ if (attempt < maxAttempts) await sleep(delayMs);
+ }
+ return { lastValue, lastError };
+}
+function createDeckProjectSkeleton({
+ title = "",
+ language = "",
+ style = {}
+} = {}) {
+ return {
+ status: "planning",
+ title,
+ language,
+ outline: [],
+ slide_order: [],
+ style,
+ assumptions: []
+ };
+}
+function createDeckProjectSeed({
+ hasExistingDeck = false,
+ title = "",
+ language = "",
+ style = {},
+ slides = [],
+ serializeElementSlide = null
+} = {}) {
+ if (!hasExistingDeck) {
+ return {
+ plan: createDeckProjectSkeleton({ title, language, style }),
+ slideFiles: []
+ };
+ }
+ const outline = slides.map((slide, index) => {
+ const slideId = `slide-${String(index + 1).padStart(2, "0")}`;
+ return {
+ id: slideId,
+ title: String(slide?.title || ""),
+ bullets: [],
+ slide_id: slideId
+ };
+ });
+ const slideFiles = [];
+ const missingPaths = [];
+ slides.forEach((slide, index) => {
+ const relPath = `slides/slide-${String(index + 1).padStart(2, "0")}.html`;
+ let html = String(slide?.html || "");
+ if (!isCompleteSlideHtml(html) && Array.isArray(slide?.elements) && serializeElementSlide) {
+ try {
+ html = String(serializeElementSlide(slide) || "");
+ } catch {
+ html = "";
+ }
+ }
+ if (isCompleteSlideHtml(html)) slideFiles.push({ relPath, html: html.trim() });
+ else missingPaths.push(relPath);
+ });
+ const diagnostic = missingPaths.length ? missingSlideFilesDiagnostic(missingPaths) : null;
+ return {
+ plan: {
+ status: diagnostic ? "planning" : "complete",
+ title,
+ language,
+ outline,
+ slide_order: outline.map((item) => item.slide_id),
+ style,
+ assumptions: []
+ },
+ slideFiles,
+ diagnostic
+ };
+}
+function seedPersistenceError(code, phase, missingPaths) {
+ return new DeckProjectContractError({
+ code,
+ phase,
+ summary: "Deck project seed persistence failed.",
+ continuationPrompt: `\u8BF7\u5728\u540C\u4E00\u4F1A\u8BDD\u4E2D\u8865\u5199\u8FD9\u4E9B deck \u9879\u76EE\u8DEF\u5F84\uFF1A${missingPaths.join("\u3001")}\uFF0C\u4FDD\u7559\u5DF2\u6210\u529F\u5199\u5165\u7684\u6587\u4EF6\u5E76\u7EE7\u7EED\u751F\u6210\u3002`,
+ missingPaths
+ });
+}
+async function persistDeckProjectSeed(fs, projectDir, seed) {
+ try {
+ await fs.mkdir(`${projectDir}/slides`, { recursive: true });
+ } catch {
+ throw seedPersistenceError("seed_fs_mkdir_failed", "mkdir", ["slides"]);
+ }
+ try {
+ await fs.writeFile(`${projectDir}/project.json`, `${JSON.stringify(seed.plan, null, 2)}
+`);
+ } catch {
+ throw seedPersistenceError("seed_fs_write_failed", "project-write", ["project.json"]);
+ }
+ for (const slideFile of seed.slideFiles || []) {
+ try {
+ await fs.writeFile(`${projectDir}/${slideFile.relPath}`, slideFile.html);
+ } catch {
+ throw seedPersistenceError("seed_fs_write_failed", "slide-write", [slideFile.relPath]);
+ }
+ }
+}
+function buildDeckRunRequestInput(baseInput, {
+ sessionId = "",
+ projectContractDiagnostic = null
+} = {}) {
+ return {
+ ...baseInput,
+ ...sessionId ? { continueAfterInterruption: true } : {},
+ ...projectContractDiagnostic ? { projectContractDiagnostic } : {}
+ };
+}
+function parseProjectJson(raw) {
+ try {
+ const plan = JSON.parse(raw);
+ if (!plan || Array.isArray(plan) || typeof plan !== "object") throw new Error("root must be an object");
+ return plan;
+ } catch (error2) {
+ throw contractError(
+ "invalid_project_json",
+ "`project.json` is not valid JSON.",
+ "\u4FEE\u590D `project.json` JSON\uFF0C\u4F7F\u6839\u503C\u4E3A\u5BF9\u8C61\uFF1B\u4E0D\u8981\u91CD\u5199\u5DF2\u6709\u9875\u9762\u3002\u4FEE\u590D\u540E\u7EE7\u7EED\u5B8C\u6210\u5951\u7EA6\u3002",
+ { cause: String(error2?.message || error2) }
+ );
+ }
+}
+async function readProjectPlanWithRetry(readFile, options = {}) {
+ const { requireComplete = false } = options;
+ const result = await readVisibleFileWithRetry(readFile, "project.json", {
+ ...options,
+ accept: (raw) => {
+ if (!raw.trim()) return false;
+ try {
+ const parsed = JSON.parse(raw);
+ return Boolean(parsed) && !Array.isArray(parsed) && typeof parsed === "object" && (!requireComplete || parsed.status === "complete");
+ } catch {
+ return false;
+ }
+ }
+ });
+ if (typeof result === "string") return parseProjectJson(result);
+ if (!result.lastValue.trim()) {
+ throw contractError(
+ "missing_project_json",
+ "`project.json` is missing or empty.",
+ "\u5728\u5DE5\u4F5C\u533A\u6839\u76EE\u5F55\u521B\u5EFA `project.json`\uFF0C\u5148\u5199 status\u3001outline \u548C slide_order\uFF0C\u518D\u7EE7\u7EED\u8865\u5199\u9875\u9762\uFF1B\u4E0D\u8981\u91CD\u5199\u5DF2\u6709\u9875\u9762\u3002",
+ { cause: String(result.lastError?.message || result.lastError || "") }
+ );
+ }
+ return parseProjectJson(result.lastValue);
+}
+function validateCompletedPlan(plan) {
+ if (plan.status !== "complete") {
+ throw contractError(
+ "project_incomplete",
+ "`project.json` has not declared a complete deck.",
+ '\u7EE7\u7EED\u5F53\u524D\u8BA1\u5212\uFF1A\u5148\u5B8C\u6210 outline \u548C\u9875\u9762\u6587\u4EF6\uFF0C\u786E\u8BA4\u6240\u6709\u5F15\u7528\u9875\u9762\u5B58\u5728\u540E\uFF0C\u518D\u628A `project.json.status` \u8BBE\u4E3A `"complete"`\u3002'
+ );
+ }
+ if (!Array.isArray(plan.outline) || !plan.outline.length) {
+ throw contractError(
+ "invalid_project_contract",
+ "`outline` must be a non-empty array.",
+ "\u4FEE\u590D `project.json`\uFF1A\u5148\u5199\u975E\u7A7A `outline`\uFF0C\u6BCF\u9879\u63D0\u4F9B\u552F\u4E00 `slide_id`\uFF0C\u5E76\u8BA9 `slide_order` \u7CBE\u786E\u5BF9\u5E94\u8FD9\u4E9B ID\u3002"
+ );
+ }
+ if (!Array.isArray(plan.slide_order) || !plan.slide_order.length) {
+ throw contractError(
+ "invalid_project_contract",
+ "`slide_order` must be a non-empty array.",
+ "\u4FEE\u590D `project.json`\uFF1A\u8BA9 `slide_order` \u6309\u5C55\u793A\u987A\u5E8F\u5217\u51FA\u5168\u90E8 `outline[].slide_id`\u3002"
+ );
+ }
+ const outlineIds = [];
+ const outlineItemIds = /* @__PURE__ */ new Set();
+ for (const item of plan.outline) {
+ const requiredFields = [
+ ["id", typeof item?.id === "string" && Boolean(item.id.trim())],
+ ["title", typeof item?.title === "string" && Boolean(item.title.trim())],
+ ["bullets", Array.isArray(item?.bullets) && item.bullets.every((bullet) => typeof bullet === "string")]
+ ];
+ const invalidField = requiredFields.find(([, valid]) => !valid)?.[0];
+ if (invalidField) {
+ throw contractError(
+ "invalid_project_contract",
+ `Every outline item must have valid id, title, and bullets fields; invalid ${invalidField}.`,
+ `\u4FEE\u590D \`project.json\` \u7684 \`outline[].${invalidField}\`\uFF0C\u786E\u4FDD id/title \u4E3A\u975E\u7A7A\u5B57\u7B26\u4E32\u4E14 bullets \u4E3A\u5B57\u7B26\u4E32\u6570\u7EC4\uFF1B\u4E0D\u8981\u6539\u65E0\u5173\u9875\u9762\u3002`,
+ { invalidOutlineField: invalidField }
+ );
+ }
+ const itemId = item.id.trim();
+ if (outlineItemIds.has(itemId)) {
+ throw contractError(
+ "invalid_project_contract",
+ `Every outline item id must be unique; duplicate ${itemId}.`,
+ "\u4FEE\u590D `project.json` \u7684 `outline[].id`\uFF0C\u786E\u4FDD\u6BCF\u9879 id \u662F\u552F\u4E00\u975E\u7A7A\u5B57\u7B26\u4E32\uFF1B\u4E0D\u8981\u6539\u65E0\u5173\u9875\u9762\u3002",
+ { invalidOutlineField: "id" }
+ );
+ }
+ outlineItemIds.add(itemId);
+ const slideId = String(item.slide_id || "");
+ if (!/^slide-\d{2}$/.test(slideId)) {
+ throw contractError(
+ "invalid_project_contract",
+ "Every outline item must have a `slide-NN` slide_id.",
+ "\u4FEE\u590D `project.json` \u7684 `outline[].slide_id`\uFF0C\u7EDF\u4E00\u4F7F\u7528\u4E24\u4F4D\u6570 `slide-NN`\uFF0C\u5E76\u540C\u6B65 `slide_order`\uFF1B\u4E0D\u8981\u6539\u65E0\u5173\u9875\u9762\u3002"
+ );
+ }
+ outlineIds.push(slideId);
+ }
+ const orderedIds = plan.slide_order.map((value2) => String(value2 || ""));
+ const uniqueOutlineIds = new Set(outlineIds);
+ const uniqueOrderedIds = new Set(orderedIds);
+ const sameIds = outlineIds.length === orderedIds.length && uniqueOutlineIds.size === outlineIds.length && uniqueOrderedIds.size === orderedIds.length && outlineIds.every((id) => uniqueOrderedIds.has(id));
+ if (!sameIds) {
+ throw contractError(
+ "invalid_project_contract",
+ "`slide_order` and `outline[].slide_id` disagree.",
+ "\u4FEE\u590D `project.json`\uFF1A\u8BA9 `slide_order` \u4E0E `outline[].slide_id` \u4E00\u4E00\u5BF9\u5E94\u4E14\u65E0\u91CD\u590D\uFF1B\u53EA\u4FEE\u590D\u8BA1\u5212\u6216\u7F3A\u5931\u9875\u9762\uFF0C\u4E0D\u91CD\u5199\u5DF2\u6709\u9875\u9762\u3002",
+ { outlineSlideIds: outlineIds, slideOrder: orderedIds }
+ );
+ }
+ return orderedIds;
+}
+function isCompleteSlideHtml(raw) {
+ const match = String(raw || "").match(
+ /^\uFEFF?\s*(?:]*>\s*)?]*)?>[\s\S]*?]*)?>([\s\S]*?)<\/body>[\s\S]*?<\/html>\s*$/i
+ );
+ if (!match) return false;
+ return Boolean(match[1].replace(//g, "").trim());
+}
+async function readCompleteSlideWithRetry(readFile, relPath, options) {
+ const result = await readVisibleFileWithRetry(readFile, relPath, {
+ ...options,
+ accept: isCompleteSlideHtml
+ });
+ return typeof result === "string" ? result.trim() : null;
+}
+async function readDeckProjectContract(readFile, options = {}) {
+ const plan = await readProjectPlanWithRetry(readFile, { ...options, requireComplete: true });
+ const slideOrder = validateCompletedPlan(plan);
+ const outlineById = new Map(plan.outline.map((item) => [String(item.slide_id), item]));
+ const slides = [];
+ const missingPaths = [];
+ for (let index = 0; index < slideOrder.length; index += 1) {
+ const slideId = slideOrder[index];
+ const relPath = `slides/${slideId}.html`;
+ const html = await readCompleteSlideWithRetry(readFile, relPath, options);
+ if (!html) {
+ missingPaths.push(relPath);
+ continue;
+ }
+ slides.push({
+ slideId,
+ slideNumber: index + 1,
+ relPath,
+ outlineEntry: outlineById.get(slideId),
+ html
+ });
}
+ if (missingPaths.length) {
+ throw new DeckProjectContractError(missingSlideFilesDiagnostic(missingPaths));
+ }
+ return { plan, slides };
}
-// ui.js
+// src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/export-diagnostics.js
+var DIAGNOSTIC_REASONS = {
+ "en-US": {
+ active_content_removed: "Unsafe active content was removed.",
+ canvas_overflow: "Slide content exceeded the canvas; visual fallback was used.",
+ canvas_size: "Slide dimensions could not preserve native fidelity; visual fallback was used.",
+ text_out_of_bounds: "Text exceeded the slide boundary; visual fallback was used.",
+ bottom_safety_margin: "Text entered the bottom safety margin; visual fallback was used.",
+ css_gradient: "A CSS gradient required visual fallback.",
+ css_filter: "A CSS filter required visual fallback.",
+ generated_content: "Generated CSS content required visual fallback.",
+ page_visual_fallback: "A page visual fallback preserved the slide appearance.",
+ full_page_fallback: "A full-page visual fallback preserved the slide appearance.",
+ unreadable_document: "The slide document could not be read.",
+ unmeasurable_canvas: "The slide canvas could not be measured.",
+ pptx_serialization: "The slide could not be serialized to PPTX.",
+ full_page_raster_failed: "The final visual fallback could not be rendered."
+ },
+ "zh-CN": {
+ active_content_removed: "\u5DF2\u79FB\u9664\u4E0D\u5B89\u5168\u7684\u6D3B\u52A8\u5185\u5BB9\u3002",
+ canvas_overflow: "\u9875\u9762\u5185\u5BB9\u8D85\u51FA\u5E7B\u706F\u7247\u8FB9\u754C\uFF0C\u5DF2\u5207\u6362\u89C6\u89C9\u515C\u5E95\u3002",
+ canvas_size: "\u9875\u9762\u5C3A\u5BF8\u65E0\u6CD5\u4FDD\u8BC1\u539F\u751F\u4FDD\u771F\uFF0C\u5DF2\u5207\u6362\u89C6\u89C9\u515C\u5E95\u3002",
+ text_out_of_bounds: "\u6587\u5B57\u8D85\u51FA\u5E7B\u706F\u7247\u8FB9\u754C\uFF0C\u5DF2\u5207\u6362\u89C6\u89C9\u515C\u5E95\u3002",
+ bottom_safety_margin: "\u6587\u5B57\u8FDB\u5165\u5E95\u90E8\u5B89\u5168\u8FB9\u8DDD\uFF0C\u5DF2\u5207\u6362\u89C6\u89C9\u515C\u5E95\u3002",
+ css_gradient: "CSS \u6E10\u53D8\u5DF2\u4F7F\u7528\u89C6\u89C9\u515C\u5E95\u3002",
+ css_filter: "CSS \u6EE4\u955C\u5DF2\u4F7F\u7528\u89C6\u89C9\u515C\u5E95\u3002",
+ generated_content: "CSS \u751F\u6210\u5185\u5BB9\u5DF2\u4F7F\u7528\u89C6\u89C9\u515C\u5E95\u3002",
+ page_visual_fallback: "\u5DF2\u4F7F\u7528\u9875\u9762\u89C6\u89C9\u515C\u5E95\u4FDD\u7559\u5916\u89C2\u3002",
+ full_page_fallback: "\u5DF2\u4F7F\u7528\u6574\u9875\u89C6\u89C9\u515C\u5E95\u4FDD\u7559\u5916\u89C2\u3002",
+ unreadable_document: "\u65E0\u6CD5\u8BFB\u53D6\u5E7B\u706F\u7247\u6587\u6863\u3002",
+ unmeasurable_canvas: "\u65E0\u6CD5\u6D4B\u91CF\u5E7B\u706F\u7247\u753B\u5E03\u3002",
+ pptx_serialization: "\u65E0\u6CD5\u5C06\u5E7B\u706F\u7247\u5E8F\u5217\u5316\u4E3A PPTX\u3002",
+ full_page_raster_failed: "\u6700\u7EC8\u89C6\u89C9\u515C\u5E95\u6E32\u67D3\u5931\u8D25\u3002"
+ }
+};
+var UNKNOWN_REASON = {
+ "en-US": "Export encountered a protected internal error.",
+ "zh-CN": "\u5BFC\u51FA\u9047\u5230\u5DF2\u4FDD\u62A4\u7684\u5185\u90E8\u9519\u8BEF\u3002"
+};
+function sanitizeDiagnosticSourceId(value2) {
+ const safe = String(value2 || "").replace(/[^a-zA-Z0-9_-]/g, "").slice(0, 48);
+ return safe || null;
+}
+function formatLocalizedExportDiagnostic(diagnostic = {}, locale = "en-US") {
+ const resolvedLocale = locale === "zh-CN" ? locale : "en-US";
+ const reason = DIAGNOSTIC_REASONS[resolvedLocale][diagnostic.code] || UNKNOWN_REASON[resolvedLocale];
+ return {
+ slideNumber: Number.isFinite(diagnostic.slideNumber) ? diagnostic.slideNumber : diagnostic.slideNumber,
+ sourceId: sanitizeDiagnosticSourceId(diagnostic.sourceId),
+ phase: String(diagnostic.phase || "").replace(/[^a-z-]/g, "").slice(0, 32) || null,
+ severity: diagnostic.severity === "blocking" ? "blocking" : diagnostic.severity,
+ code: String(diagnostic.code || "unknown").replace(/[^a-z0-9_-]/gi, "").slice(0, 64),
+ reason: reason.slice(0, 120)
+ };
+}
+function localizeExportDiagnosticLocations(locations = [], locale = "en-US") {
+ return locations.map((location) => formatLocalizedExportDiagnostic(location, locale));
+}
+function summarizePptxExportDiagnostics(preparedSlides = []) {
+ const counts = {
+ repaired: 0,
+ svgImage: 0,
+ localPng: 0,
+ pageVisual: 0,
+ fullPage: 0,
+ blocking: 0
+ };
+ const locations = [];
+ const seen = /* @__PURE__ */ new Set();
+ const addLocation = (slideNumber, item, defaults = {}) => {
+ const location = {
+ slideNumber,
+ sourceId: item?.sourceId || defaults.sourceId || null,
+ phase: item?.phase || defaults.phase || null,
+ severity: item?.severity || defaults.severity || "fallback",
+ code: item?.code || defaults.code || null,
+ reason: item?.reason || item?.message || defaults.reason || null
+ };
+ const key = [
+ location.slideNumber,
+ location.sourceId,
+ location.phase,
+ location.severity,
+ location.code
+ ].join(":");
+ if (seen.has(key)) return;
+ seen.add(key);
+ locations.push(location);
+ };
+ preparedSlides.forEach((prepared, arrayIndex) => {
+ const slideNumber = (prepared?.index ?? arrayIndex) + 1;
+ const slideData = prepared?.slideData || {};
+ (slideData.diagnostics || []).forEach((diagnostic) => {
+ if (diagnostic.severity === "repaired") counts.repaired += 1;
+ if (diagnostic.severity === "blocking") counts.blocking += 1;
+ if (diagnostic.severity === "repaired" || diagnostic.severity === "blocking" || diagnostic.phase || diagnostic.sourceId) {
+ addLocation(slideNumber, diagnostic);
+ }
+ });
+ (slideData.fallbackLayers || []).forEach((layer) => {
+ if (layer.kind === "svg-image") counts.svgImage += 1;
+ if (layer.kind === "raster" && layer.phase === "local-visual") counts.localPng += 1;
+ if (layer.kind === "raster" && layer.phase === "page-visual") counts.pageVisual += 1;
+ addLocation(slideNumber, layer, {
+ severity: "fallback",
+ code: layer.kind === "svg-image" ? "svg_image_fallback" : "png_visual_fallback"
+ });
+ });
+ if (slideData.fullPageFallback) {
+ counts.fullPage += 1;
+ addLocation(slideNumber, slideData.fullPageFallback, {
+ severity: "fallback",
+ code: "full_page_fallback",
+ phase: "full-page"
+ });
+ }
+ });
+ return {
+ counts,
+ locations,
+ hasWarnings: counts.repaired + counts.svgImage + counts.localPng + counts.pageVisual + counts.fullPage > 0,
+ hasBlocking: counts.blocking > 0
+ };
+}
+
+// src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/ui.js
var state = createInitialState();
var busy = false;
var dragState = null;
@@ -35922,18 +36158,15 @@ async function readDeckProjectFile(project, relPath) {
if (!fs?.readFile) throw new Error("PPT Live fs API is unavailable");
return await fs.readFile(`${project.dir}/${relPath}`);
}
-async function tryReadDeckJsonFile(project, relPath) {
+async function tryReadDeckPlanFile(project) {
try {
- const raw = String(await readDeckProjectFile(project, relPath) || "");
- if (!raw.trim()) return null;
- return extractBackendJson(raw);
+ return await readProjectPlanWithRetry(
+ (relPath) => readDeckProjectFile(project, relPath)
+ );
} catch {
return null;
}
}
-async function tryReadDeckPlanFile(project) {
- return await tryReadDeckJsonFile(project, "project.json");
-}
async function tryReadDeckSlideFile(project, slideNumber) {
try {
const raw = String(await readDeckProjectFile(project, deckSlideFileName(slideNumber)) || "").trim();
@@ -35954,26 +36187,16 @@ async function tryReadDeckSlideFileWithRetry(project, slideNumber, maxAttempts =
return null;
}
async function readDeckFromProjectFiles(project) {
- const plan = await tryReadDeckPlanFile(project);
- if (!plan) throw new Error("PPT Live agent finished without a valid project.json");
- const slideOrder = Array.isArray(plan.slide_order) && plan.slide_order.length ? plan.slide_order : Array.isArray(plan.outline) ? plan.outline.map((_, index) => `slide-${String(index + 1).padStart(2, "0")}`) : [];
- const slides = [];
- for (let index = 0; index < slideOrder.length; index += 1) {
- const slideId = String(slideOrder[index] || `slide-${String(index + 1).padStart(2, "0")}`);
- const slideNumber = index + 1;
- const html = await tryReadDeckSlideFile(project, slideNumber);
- const outlineEntry = plan.outline?.[index];
- const title = typeof outlineEntry === "string" ? outlineEntry : outlineEntry?.title || `${translate("newSlideTitle")} ${slideNumber}`;
- if (html) {
- slides.push({
- id: `ppt-live-slide-${slideNumber}`,
- slideNumber,
- title,
- html
- });
- }
- }
- if (!slides.length) throw new Error("PPT Live agent did not produce any slide files");
+ const result = await readDeckProjectContract(
+ (relPath) => readDeckProjectFile(project, relPath)
+ );
+ const { plan } = result;
+ const slides = result.slides.map((slide) => ({
+ id: `ppt-live-slide-${slide.slideNumber}`,
+ slideNumber: slide.slideNumber,
+ title: slide.outlineEntry?.title || `${translate("newSlideTitle")} ${slide.slideNumber}`,
+ html: slide.html
+ }));
return {
title: resolveDeckTitle({ plan, slides }),
language: plan.language || "",
@@ -35985,32 +36208,35 @@ async function readDeckFromProjectFiles(project) {
}
async function seedDeckProjectFromState(project) {
const fs = runtime().fs;
- if (!project || !fs?.writeFile || !state.slides?.length) return;
+ if (!project || !fs?.writeFile || !fs?.mkdir) {
+ throw new DeckProjectContractError({
+ code: "seed_fs_unavailable",
+ summary: "Deck project filesystem is unavailable.",
+ continuationPrompt: "\u8BF7\u5728\u540C\u4E00\u4F1A\u8BDD\u4E2D\u91CD\u65B0\u521B\u5EFA deck \u9879\u76EE\u76EE\u5F55\u548C slides \u5B50\u76EE\u5F55\uFF0C\u7136\u540E\u7EE7\u7EED\u751F\u6210\u3002",
+ missingPaths: [`${project?.dir || "deck-project"}/slides`]
+ });
+ }
const hasExistingProject = await tryReadDeckPlanFile(project);
if (hasExistingProject) return;
try {
- const outline = state.slides.map((slide, index) => ({
- id: `slide-${String(index + 1).padStart(2, "0")}`,
- title: String(slide.title || ""),
- bullets: [],
- slide_id: `slide-${String(index + 1).padStart(2, "0")}`
- }));
- const projectJson = {
- title: state.title || "",
+ const seed = createDeckProjectSeed({
+ hasExistingDeck: hasUsableDeckForRevision(),
+ title: isEphemeralDeckTitle(state.title) ? "" : state.title,
language: getLocale(),
- outline,
- slide_order: outline.map((item) => item.slide_id),
- style: buildGenerationStyle()
- };
- await fs.writeFile(`${project.dir}/project.json`, `${JSON.stringify(projectJson, null, 2)}
-`);
- for (let index = 0; index < state.slides.length; index += 1) {
- const slide = state.slides[index];
- if (slide.html) {
- await fs.writeFile(`${project.dir}/${deckSlideFileName(index + 1)}`, slide.html);
- }
- }
- } catch {
+ style: buildGenerationStyle(),
+ slides: state.slides,
+ serializeElementSlide: buildElementSlideHtml
+ });
+ await persistDeckProjectSeed(fs, project.dir, seed);
+ } catch (error2) {
+ if (error2 instanceof DeckProjectContractError) throw error2;
+ throw new DeckProjectContractError({
+ code: "seed_fs_write_failed",
+ summary: "Deck project seed files could not be written.",
+ continuationPrompt: "\u8BF7\u5728\u540C\u4E00\u4F1A\u8BDD\u4E2D\u521B\u5EFA\u7F3A\u5931\u7684 slides \u76EE\u5F55\u5E76\u91CD\u5199\u5931\u8D25\u7684 seed \u6587\u4EF6\uFF0C\u7136\u540E\u7EE7\u7EED\u751F\u6210\u3002",
+ missingPaths: [`${project.dir}/slides`],
+ cause: String(error2?.message || error2)
+ });
}
}
async function pruneOldDeckProjects(currentRunId) {
@@ -36303,9 +36529,20 @@ async function runCoworkDeckGeneration(operation, instruction) {
prepareAgentGenerationSurface(operation, instruction);
let completed = false;
const project = backendUsesFileProtocol() ? currentDeckProject() || newDeckProject() : null;
+ let projectContractDiagnostic = null;
if (project && !state.agentSession?.workspaceSubdir) {
await pruneOldDeckProjects(project.runId);
- await seedDeckProjectFromState(project);
+ try {
+ await seedDeckProjectFromState(project);
+ } catch (error2) {
+ if (!(error2 instanceof DeckProjectContractError)) throw error2;
+ projectContractDiagnostic = error2.diagnostic;
+ addGenerationEvent({
+ title: translate("generationStageAudit"),
+ detail: error2.diagnostic.continuationPrompt,
+ kind: "start"
+ });
+ }
}
const retrySession = {
id: state.agentSession?.id || null,
@@ -36330,10 +36567,13 @@ async function runCoworkDeckGeneration(operation, instruction) {
setStatus(translate("generationRetrying", { attempt, max: PPT_BACKEND_MAX_ATTEMPTS }));
await new Promise((resolve) => setTimeout(resolve, retryDelayMs(lastError, attempt)));
}
- const requestInput = {
- ...buildBackendRequestBase(operation, instruction),
- ...retrySession?.id ? { continueAfterInterruption: true } : {}
- };
+ const requestInput = buildDeckRunRequestInput(
+ buildBackendRequestBase(operation, instruction),
+ {
+ sessionId: retrySession?.id,
+ projectContractDiagnostic
+ }
+ );
const hasExistingDeck = hasUsableDeckForRevision();
if (hasExistingDeck) {
requestInput.currentSlideIndex = getActiveIndex(state);
@@ -36440,6 +36680,7 @@ async function runCoworkDeckGeneration(operation, instruction) {
break;
} catch (error2) {
lastError = error2;
+ if (error2?.diagnostic) projectContractDiagnostic = error2.diagnostic;
if (isUnknownSessionBackendError(error2)) retrySession.id = null;
else if (error2?.pptLiveSessionId) retrySession.id = error2.pptLiveSessionId;
if (!isRetryableBackendError(error2) || attempt >= PPT_BACKEND_MAX_ATTEMPTS) throw error2;
@@ -37188,6 +37429,58 @@ function getExportLabels(format) {
};
return labels[format] || null;
}
+function formatExportDiagnostics(summary) {
+ if (!summary?.hasWarnings && !summary?.hasBlocking) return "";
+ const countLabels = [
+ ["repaired", "exportDiagnosticsRepaired"],
+ ["svgImage", "exportDiagnosticsSvg"],
+ ["localPng", "exportDiagnosticsLocalPng"],
+ ["pageVisual", "exportDiagnosticsPageVisual"],
+ ["fullPage", "exportDiagnosticsFullPage"],
+ ["blocking", "exportDiagnosticsBlocking"]
+ ].filter(([countKey]) => summary.counts?.[countKey] > 0).map(([countKey, labelKey]) => translate(labelKey, { count: summary.counts[countKey] }));
+ const phaseKeys = {
+ "local-svg": "exportDiagnosticsPhaseSvg",
+ "local-visual": "exportDiagnosticsPhaseLocalPng",
+ "page-visual": "exportDiagnosticsPhasePageVisual",
+ "full-page": "exportDiagnosticsPhaseFullPage"
+ };
+ const locations = localizeExportDiagnosticLocations(summary.locations || [], getLocale()).filter((location) => location.sourceId).slice(0, 3).map((location) => translate("exportDiagnosticsLocation", {
+ slide: location.slideNumber,
+ source: location.sourceId,
+ phase: translate(
+ phaseKeys[location.phase] || (location.severity === "blocking" ? "exportDiagnosticsPhaseBlocking" : "exportDiagnosticsPhaseRepair")
+ ),
+ reason: location.reason
+ }));
+ return translate("exportDiagnosticsSummary", {
+ counts: countLabels.join(", "),
+ locations: locations.join("; ")
+ });
+}
+function formatBlockingExportDiagnostics(diagnostics) {
+ const blocking = Array.isArray(diagnostics) ? diagnostics.filter((diagnostic) => diagnostic?.severity === "blocking") : [];
+ if (!blocking.length) return "";
+ return formatExportDiagnostics({
+ counts: {
+ repaired: 0,
+ svgImage: 0,
+ localPng: 0,
+ pageVisual: 0,
+ fullPage: 0,
+ blocking: blocking.length
+ },
+ locations: blocking.map((diagnostic) => ({
+ slideNumber: diagnostic.slideNumber || "?",
+ sourceId: diagnostic.sourceId || "?",
+ phase: diagnostic.phase || null,
+ severity: "blocking",
+ code: diagnostic.code
+ })),
+ hasWarnings: false,
+ hasBlocking: true
+ });
+}
function setExportRenderProgress(index, total, format) {
const labels = getExportLabels(format === "pptx" ? "pptx" : format);
if (!labels || total <= 0) return;
@@ -37250,6 +37543,7 @@ async function executeExport(format) {
onRasterProgress: (index) => setExportRenderProgress(index, slides.length, "pptx")
});
result = await exportPptxPrepared(deckPayload, preparedSlides);
+ result.exportSummary = summarizePptxExportDiagnostics(preparedSlides);
} else {
result = await exportPptxFromDeck(deckPayload);
}
@@ -37270,7 +37564,7 @@ async function executeExport(format) {
filename,
result.mimeType || "application/octet-stream"
);
- return { filename };
+ return { filename, exportSummary: result?.exportSummary || null };
}
var exportInFlight = false;
var handlers = {
@@ -37886,16 +38180,19 @@ async function confirmExportFromModal() {
const previewFrame = $("exportPreviewFrame");
const previewSnapshot = previewFrame?.innerHTML || "";
try {
- const { filename } = await executeExport(format);
+ const { filename, exportSummary } = await executeExport(format);
const savedMessage = translate("exportSavedTo", { path: filename });
+ const diagnosticMessage = formatExportDiagnostics(exportSummary);
+ const completedMessage = diagnosticMessage ? `${savedMessage} ${diagnosticMessage}` : savedMessage;
$("exportOverlay")?.classList.remove("is-exporting");
- setExportModalFeedback("success", savedMessage);
- setExportStatus(savedMessage);
+ setExportModalFeedback("success", completedMessage);
+ setExportStatus(completedMessage);
revealDownloadFolder();
await new Promise((resolve) => setTimeout(resolve, 1600));
closeExportModal();
} catch (error2) {
- const message = error2 instanceof Error ? error2.message : String(error2);
+ const localizedDiagnostics = formatBlockingExportDiagnostics(error2?.diagnostics);
+ const message = localizedDiagnostics || (error2 instanceof Error ? error2.message : String(error2));
runtime().log?.error?.(`PPT Live ${format} export failed`, { error: message });
$("exportOverlay")?.classList.remove("is-exporting");
setExportModalFeedback("error", `${labels.failed} ${message}`);
diff --git a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/meta.json b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/meta.json
index 76719d7e84..157246aec8 100644
--- a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/meta.json
+++ b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/meta.json
@@ -10,7 +10,7 @@
"ppt",
"ai"
],
- "version": 203,
+ "version": 217,
"created_at": 0,
"updated_at": 0,
"permissions": {
diff --git a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/agent-prompt.js b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/agent-prompt.js
new file mode 100644
index 0000000000..b95ebb753a
--- /dev/null
+++ b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/agent-prompt.js
@@ -0,0 +1,91 @@
+export const PPT_DESIGN_SKILL_KEY = 'user::bitfun-system::ppt-design';
+
+function serializeInput(input) {
+ try {
+ return JSON.stringify(input ?? {}, null, 2);
+ } catch {
+ return '{}';
+ }
+}
+
+function hasCurrentDeck(input) {
+ return Array.isArray(input?.currentDeck?.slides) && input.currentDeck.slides.length > 0;
+}
+
+function describeStyle(style = {}) {
+ const parts = [];
+ const font = style.fontFamily;
+ if (font === 'serif') parts.push('衬线字体');
+ else if (font === 'sans') parts.push('非衬线字体');
+
+ const density = style.density === 'loose' ? 'spacious' : style.density;
+ if (density === 'compact') parts.push('紧凑信息密度');
+ else if (density === 'spacious') parts.push('宽松留白');
+
+ const colorMode = style.colorMode || style.theme;
+ if (colorMode === 'dark') parts.push('深色主题');
+ if (style.stylePreset) parts.push(`风格预设: ${style.stylePreset}`);
+ return parts.length ? parts.join('、') : '';
+}
+
+function formatContractDiagnostic(diagnostic) {
+ if (!diagnostic) return '';
+ if (typeof diagnostic === 'string') return diagnostic.trim();
+ const code = String(diagnostic.code || 'unknown_contract_error');
+ const continuation = String(diagnostic.continuationPrompt || '').trim();
+ return [`诊断代码:${code}`, continuation].filter(Boolean).join('\n');
+}
+
+export function buildAgentPrompt(input) {
+ const hasDeck = hasCurrentDeck(input);
+ const styleLine = describeStyle(input?.style);
+ const instruction = input?.instruction || input?.userInput || '';
+ let prompt = hasDeck
+ ? `编辑现有 PPT。编辑指令:${instruction || '(见 currentDeck 上下文)'}。`
+ : `生成 PPT。用户需求:${instruction || '(见 input JSON)'}。`;
+
+ prompt = `先调用 Skill,并且 skill key 必须精确为 \`${PPT_DESIGN_SKILL_KEY}\`。\n${prompt}`;
+ if (styleLine) prompt += `\n样式偏好:${styleLine}。`;
+
+ prompt += `
+
+## 生成文件协议
+
+- 当前 agent 工作区根目录就是 deck 根目录;所有路径均相对该工作区根目录。
+- 先写工作区根目录下的 \`project.json\`,再写工作区根目录下的 \`slides/slide-NN.html\`。
+- 只有在 \`slide_order\` 引用的每一页都已有完整 HTML 后,才将 \`project.json\` 的 \`status\` 设为 \`"complete"\`。
+- 完成前做一次有界检查:核对 \`outline[].slide_id\`、\`slide_order\` 和对应页面文件;缺什么只补什么,检查后立即结束。
+
+## 约束
+
+- 用户只能看到 PPT Live UI,无法回答提问。如有歧义自行判断最优方案并记录假设。
+- 不要调用 AskUserQuestion、ControlHub、GenerativeUI、ComputerUse 等交互工具。
+- 研究用 WebSearch / WebFetch 即可。
+- **一次写对,禁止事后审计**:每页 HTML 在写入时就要满足所有约束(画布尺寸、四条 OOXML 硬约束、防溢出预算)。完成检查只核对生成文件协议,不逐页 Read→Edit 返工或 Grep 批量审计页面内容。
+`;
+
+ if (hasDeck) {
+ prompt += `
+## 编辑上下文
+
+- \`currentDeck\` 已提供。将用户指令视为对现有 deck 的增量编辑,除非指令明确要求全新生成。
+- \`currentDeck.slides[].slideNumber\` 是从 1 开始的页码,与用户口语一致。
+- 编辑时只重写变更的 \`slides/slide-NN.html\` 文件,不动其他页。
+`;
+ }
+
+ prompt += `
+Input JSON:
+\`\`\`json
+${serializeInput(input)}
+\`\`\``;
+
+ if (input?.continueAfterInterruption) {
+ const diagnostic = formatContractDiagnostic(input.projectContractDiagnostic);
+ prompt = `上一次生成被中断或未通过文件契约。请在同一会话中定向续跑,不要重写已完成页面。
+${diagnostic ? `\n${diagnostic}\n` : ''}
+检查 \`project.json\` 和已写的 \`slides/\` 文件,只修复诊断指出的内容;完成后执行一次有界检查。\n\n${prompt}`;
+ }
+
+ return prompt;
+}
diff --git a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/bitfun-backend-adapter.js b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/bitfun-backend-adapter.js
index fd7901f0d9..7d521d7a0f 100644
--- a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/bitfun-backend-adapter.js
+++ b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/bitfun-backend-adapter.js
@@ -2,17 +2,15 @@
//
// The MiniApp agent bridge (`app.agent.*`) is the only generation path. A
// single cowork agent turn loads BitFun's pinned built-in `ppt-design` skill
-// and produces the entire deck end to end — research, outline, design system,
-// and every slide HTML — following the skill's native project.json +
-// slides/slide-NN.html file protocol.
-//
-// Design principle: the prompt stays minimal. The ppt-design skill owns all
-// design rules, schemas, templates, and quality bars via progressive disclosure
-// (SKILL.md → references/*.md). The prompt only carries user intent + style
-// preferences + MiniApp headless constraints.
+// and produces the entire deck through its project.json + slides/ file protocol.
+
+import {
+ PPT_DESIGN_SKILL_KEY,
+ buildAgentPrompt,
+} from './agent-prompt.js';
const EVENT_LISTENERS = new Set();
-export const PPT_DESIGN_SKILL_KEY = 'user::bitfun-system::ppt-design';
+export { PPT_DESIGN_SKILL_KEY };
function emitEvent(event) {
EVENT_LISTENERS.forEach((listener) => {
@@ -24,111 +22,11 @@ function emitEvent(event) {
});
}
-// ─── Agent prompt builder (minimal — delegates to skill) ─────────────────────
-
-function serializeInput(input) {
- try {
- return JSON.stringify(input ?? {}, null, 2);
- } catch {
- return '{}';
- }
-}
-
-function hasCurrentDeck(input) {
- return Array.isArray(input?.currentDeck?.slides) && input.currentDeck.slides.length > 0;
-}
-
-/**
- * Describe the user's style preferences in one concise line, so the skill can
- * apply them without the prompt restating design rules the skill already owns.
- */
-function describeStyle(style = {}) {
- const parts = [];
- const font = style.fontFamily;
- if (font === 'serif') parts.push('衬线字体');
- else if (font === 'sans') parts.push('非衬线字体');
-
- const density = style.density === 'loose' ? 'spacious' : style.density;
- if (density === 'compact') parts.push('紧凑信息密度');
- else if (density === 'spacious') parts.push('宽松留白');
-
- const colorMode = style.colorMode || style.theme;
- if (colorMode === 'dark') parts.push('深色主题');
-
- if (style.stylePreset) parts.push(`风格预设: ${style.stylePreset}`);
-
- return parts.length ? parts.join('、') : '';
-}
-
-/**
- * Build the full agent user prompt for a `ppt.generate` run.
- *
- * Intentionally minimal: user intent + style line + "use ppt-design skill".
- * All design rules, file schemas, layout templates, quality bars, and
- * progressive-disclosure reference routing live inside the skill itself.
- * Restating them here creates prompt/skill duplication that confuses the model.
- */
-function buildAgentPrompt(input) {
- const hasDeck = hasCurrentDeck(input);
- const styleLine = describeStyle(input?.style);
- const instruction = input?.instruction || input?.userInput || '';
-
- // --- Core task (one sentence) ---
- let prompt = hasDeck
- ? `使用 PPT-Design skill 编辑现有 PPT。编辑指令:${instruction || '(见 currentDeck 上下文)'}。`
- : `使用 PPT-Design skill 生成 PPT。用户需求:${instruction || '(见 input JSON)'}。`;
-
- // --- Style preferences (one line) ---
- if (styleLine) {
- prompt += `\n样式偏好:${styleLine}。`;
- }
-
- // --- MiniApp headless constraints (only what the skill doesn't know) ---
- prompt += `
-
-## 约束
-
-- 用户只能看到 PPT Live UI,无法回答提问。如有歧义自行判断最优方案并记录假设。
-- 不要调用 AskUserQuestion、ControlHub、GenerativeUI、ComputerUse 等交互工具。
-- 研究用 WebSearch / WebFetch 即可。
-- **一次写对,禁止事后审计**:每页 HTML 在写入时就要满足所有约束(画布尺寸、四条 OOXML 硬约束、防溢出预算)。所有页面写完后不得再逐页 Read→Edit 返工或 Grep 批量检查。写完即结束。
-`;
-
- // --- Context for edit operations ---
- if (hasDeck) {
- prompt += `
-## 编辑上下文
-
-- \`currentDeck\` 已提供。将用户指令视为对现有 deck 的增量编辑,除非指令明确要求全新生成。
-- \`currentDeck.slides[].slideNumber\` 是从 1 开始的页码,与用户口语一致。
-- 编辑时只重写变更的 \`slides/slide-NN.html\` 文件,不动其他页。
-`;
- }
-
- // --- Full input JSON (for research context, outline hints, etc.) ---
- prompt += `
-Input JSON:
-\`\`\`json
-${serializeInput(input)}
-\`\``;
-
- // --- Interruption continuation prefix ---
- if (input?.continueAfterInterruption) {
- prompt = `上一次生成被中断了。请继续完成任务:检查 project.json 和已写的 slides/ 文件,只补写还没完成的页面,不要重写已有的页面。\n\n${prompt}`;
- }
-
- return prompt;
-}
-
-// ─── Agent-backed backend (primary path) ─────────────────────────────────────
-
function installAgentBackend(app) {
let agentEventsHooked = false;
const ensureAgentEvents = () => {
if (agentEventsHooked) return;
agentEventsHooked = true;
- // Host events already carry sessionId/turnId/sourceEvent/text/contentType/
- // toolEvent/error in the shape ui.js consumes; re-emit them as-is.
app.agent.onEvent((event) => {
if (!event || typeof event !== 'object') return;
emitEvent(event);
@@ -136,23 +34,16 @@ function installAgentBackend(app) {
};
app.backend = {
- // The agent delivers through project files written using the ppt-design
- // skill's native workflow; 'files' tells ui.js to read them back.
protocol: 'files',
async call(action, input, options = {}) {
if (action !== 'ppt.generate') {
throw new Error(`Unsupported PPT Live action: ${action}`);
}
ensureAgentEvents();
- const prompt = buildAgentPrompt(input);
- const result = await app.agent.run(prompt, {
+ const result = await app.agent.run(buildAgentPrompt(input), {
runId: options.idempotencyKey,
sessionName: 'PPT Live',
- // Reuse the session when the caller carries one so follow-up edits
- // resume with the loaded skill/preset/research context.
sessionId: options.sessionId,
- // The agent works inside a dedicated deck project directory under
- // the app's own appdata storage (never the user's workspace).
appDataWorkspace: options.appDataWorkspace,
});
if (!result?.sessionId || !result?.turnId) {
@@ -183,11 +74,7 @@ function installAgentBackend(app) {
};
}
-// ─── Install ─────────────────────────────────────────────────────────────────
-
export function installBitFunBackendAdapter(app = window.app) {
if (!app || app.backend?.call) return;
- if (app.agent?.run) {
- installAgentBackend(app);
- }
+ if (app.agent?.run) installAgentBackend(app);
}
diff --git a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/deck-project-contract.js b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/deck-project-contract.js
new file mode 100644
index 0000000000..158d562753
--- /dev/null
+++ b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/deck-project-contract.js
@@ -0,0 +1,327 @@
+export class DeckProjectContractError extends Error {
+ constructor(diagnostic) {
+ super(`[${diagnostic.code}] ${diagnostic.summary}`);
+ this.name = 'DeckProjectContractError';
+ this.diagnostic = diagnostic;
+ }
+}
+
+function contractError(code, summary, continuationPrompt, details = {}) {
+ return new DeckProjectContractError({
+ code,
+ summary,
+ continuationPrompt,
+ ...details,
+ });
+}
+
+function missingSlideFilesDiagnostic(missingPaths) {
+ return {
+ code: 'missing_slide_files',
+ summary: `Missing or incomplete slide files: ${missingPaths.join(', ')}`,
+ continuationPrompt: `只补写这些缺失或不完整页面:${missingPaths.join('、')}。保留其他页面不变;补齐后再把状态确认为 complete 并执行一次有界检查。`,
+ missingPaths,
+ };
+}
+
+const defaultSleep = (delayMs) => new Promise((resolve) => setTimeout(resolve, delayMs));
+
+async function readVisibleFileWithRetry(readFile, relPath, {
+ maxAttempts = 6,
+ delayMs = 120,
+ sleep = defaultSleep,
+ accept,
+} = {}) {
+ let lastValue = '';
+ let lastError = null;
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
+ try {
+ lastValue = String(await readFile(relPath) || '');
+ if (accept(lastValue)) return lastValue;
+ } catch (error) {
+ lastError = error;
+ }
+ if (attempt < maxAttempts) await sleep(delayMs);
+ }
+ return { lastValue, lastError };
+}
+
+export function createDeckProjectSkeleton({
+ title = '',
+ language = '',
+ style = {},
+} = {}) {
+ return {
+ status: 'planning',
+ title,
+ language,
+ outline: [],
+ slide_order: [],
+ style,
+ assumptions: [],
+ };
+}
+
+export function createDeckProjectSeed({
+ hasExistingDeck = false,
+ title = '',
+ language = '',
+ style = {},
+ slides = [],
+ serializeElementSlide = null,
+} = {}) {
+ if (!hasExistingDeck) {
+ return {
+ plan: createDeckProjectSkeleton({ title, language, style }),
+ slideFiles: [],
+ };
+ }
+ const outline = slides.map((slide, index) => {
+ const slideId = `slide-${String(index + 1).padStart(2, '0')}`;
+ return {
+ id: slideId,
+ title: String(slide?.title || ''),
+ bullets: [],
+ slide_id: slideId,
+ };
+ });
+ const slideFiles = [];
+ const missingPaths = [];
+ slides.forEach((slide, index) => {
+ const relPath = `slides/slide-${String(index + 1).padStart(2, '0')}.html`;
+ let html = String(slide?.html || '');
+ if (!isCompleteSlideHtml(html) && Array.isArray(slide?.elements) && serializeElementSlide) {
+ try {
+ html = String(serializeElementSlide(slide) || '');
+ } catch {
+ html = '';
+ }
+ }
+ if (isCompleteSlideHtml(html)) slideFiles.push({ relPath, html: html.trim() });
+ else missingPaths.push(relPath);
+ });
+ const diagnostic = missingPaths.length ? missingSlideFilesDiagnostic(missingPaths) : null;
+ return {
+ plan: {
+ status: diagnostic ? 'planning' : 'complete',
+ title,
+ language,
+ outline,
+ slide_order: outline.map((item) => item.slide_id),
+ style,
+ assumptions: [],
+ },
+ slideFiles,
+ diagnostic,
+ };
+}
+
+function seedPersistenceError(code, phase, missingPaths) {
+ return new DeckProjectContractError({
+ code,
+ phase,
+ summary: 'Deck project seed persistence failed.',
+ continuationPrompt: `请在同一会话中补写这些 deck 项目路径:${missingPaths.join('、')},保留已成功写入的文件并继续生成。`,
+ missingPaths,
+ });
+}
+
+export async function persistDeckProjectSeed(fs, projectDir, seed) {
+ try {
+ await fs.mkdir(`${projectDir}/slides`, { recursive: true });
+ } catch {
+ throw seedPersistenceError('seed_fs_mkdir_failed', 'mkdir', ['slides']);
+ }
+ try {
+ await fs.writeFile(`${projectDir}/project.json`, `${JSON.stringify(seed.plan, null, 2)}\n`);
+ } catch {
+ throw seedPersistenceError('seed_fs_write_failed', 'project-write', ['project.json']);
+ }
+ for (const slideFile of seed.slideFiles || []) {
+ try {
+ await fs.writeFile(`${projectDir}/${slideFile.relPath}`, slideFile.html);
+ } catch {
+ throw seedPersistenceError('seed_fs_write_failed', 'slide-write', [slideFile.relPath]);
+ }
+ }
+}
+
+export function buildDeckRunRequestInput(baseInput, {
+ sessionId = '',
+ projectContractDiagnostic = null,
+} = {}) {
+ return {
+ ...baseInput,
+ ...(sessionId ? { continueAfterInterruption: true } : {}),
+ ...(projectContractDiagnostic ? { projectContractDiagnostic } : {}),
+ };
+}
+
+function parseProjectJson(raw) {
+ try {
+ const plan = JSON.parse(raw);
+ if (!plan || Array.isArray(plan) || typeof plan !== 'object') throw new Error('root must be an object');
+ return plan;
+ } catch (error) {
+ throw contractError(
+ 'invalid_project_json',
+ '`project.json` is not valid JSON.',
+ '修复 `project.json` JSON,使根值为对象;不要重写已有页面。修复后继续完成契约。',
+ { cause: String(error?.message || error) },
+ );
+ }
+}
+
+export async function readProjectPlanWithRetry(readFile, options = {}) {
+ const { requireComplete = false } = options;
+ const result = await readVisibleFileWithRetry(readFile, 'project.json', {
+ ...options,
+ accept: (raw) => {
+ if (!raw.trim()) return false;
+ try {
+ const parsed = JSON.parse(raw);
+ return Boolean(parsed)
+ && !Array.isArray(parsed)
+ && typeof parsed === 'object'
+ && (!requireComplete || parsed.status === 'complete');
+ } catch {
+ return false;
+ }
+ },
+ });
+ if (typeof result === 'string') return parseProjectJson(result);
+ if (!result.lastValue.trim()) {
+ throw contractError(
+ 'missing_project_json',
+ '`project.json` is missing or empty.',
+ '在工作区根目录创建 `project.json`,先写 status、outline 和 slide_order,再继续补写页面;不要重写已有页面。',
+ { cause: String(result.lastError?.message || result.lastError || '') },
+ );
+ }
+ return parseProjectJson(result.lastValue);
+}
+
+function validateCompletedPlan(plan) {
+ if (plan.status !== 'complete') {
+ throw contractError(
+ 'project_incomplete',
+ '`project.json` has not declared a complete deck.',
+ '继续当前计划:先完成 outline 和页面文件,确认所有引用页面存在后,再把 `project.json.status` 设为 `"complete"`。',
+ );
+ }
+ if (!Array.isArray(plan.outline) || !plan.outline.length) {
+ throw contractError(
+ 'invalid_project_contract',
+ '`outline` must be a non-empty array.',
+ '修复 `project.json`:先写非空 `outline`,每项提供唯一 `slide_id`,并让 `slide_order` 精确对应这些 ID。',
+ );
+ }
+ if (!Array.isArray(plan.slide_order) || !plan.slide_order.length) {
+ throw contractError(
+ 'invalid_project_contract',
+ '`slide_order` must be a non-empty array.',
+ '修复 `project.json`:让 `slide_order` 按展示顺序列出全部 `outline[].slide_id`。',
+ );
+ }
+
+ const outlineIds = [];
+ const outlineItemIds = new Set();
+ for (const item of plan.outline) {
+ const requiredFields = [
+ ['id', typeof item?.id === 'string' && Boolean(item.id.trim())],
+ ['title', typeof item?.title === 'string' && Boolean(item.title.trim())],
+ ['bullets', Array.isArray(item?.bullets) && item.bullets.every((bullet) => typeof bullet === 'string')],
+ ];
+ const invalidField = requiredFields.find(([, valid]) => !valid)?.[0];
+ if (invalidField) {
+ throw contractError(
+ 'invalid_project_contract',
+ `Every outline item must have valid id, title, and bullets fields; invalid ${invalidField}.`,
+ `修复 \`project.json\` 的 \`outline[].${invalidField}\`,确保 id/title 为非空字符串且 bullets 为字符串数组;不要改无关页面。`,
+ { invalidOutlineField: invalidField },
+ );
+ }
+ const itemId = item.id.trim();
+ if (outlineItemIds.has(itemId)) {
+ throw contractError(
+ 'invalid_project_contract',
+ `Every outline item id must be unique; duplicate ${itemId}.`,
+ '修复 `project.json` 的 `outline[].id`,确保每项 id 是唯一非空字符串;不要改无关页面。',
+ { invalidOutlineField: 'id' },
+ );
+ }
+ outlineItemIds.add(itemId);
+ const slideId = String(item.slide_id || '');
+ if (!/^slide-\d{2}$/.test(slideId)) {
+ throw contractError(
+ 'invalid_project_contract',
+ 'Every outline item must have a `slide-NN` slide_id.',
+ '修复 `project.json` 的 `outline[].slide_id`,统一使用两位数 `slide-NN`,并同步 `slide_order`;不要改无关页面。',
+ );
+ }
+ outlineIds.push(slideId);
+ }
+
+ const orderedIds = plan.slide_order.map((value) => String(value || ''));
+ const uniqueOutlineIds = new Set(outlineIds);
+ const uniqueOrderedIds = new Set(orderedIds);
+ const sameIds = outlineIds.length === orderedIds.length
+ && uniqueOutlineIds.size === outlineIds.length
+ && uniqueOrderedIds.size === orderedIds.length
+ && outlineIds.every((id) => uniqueOrderedIds.has(id));
+ if (!sameIds) {
+ throw contractError(
+ 'invalid_project_contract',
+ '`slide_order` and `outline[].slide_id` disagree.',
+ '修复 `project.json`:让 `slide_order` 与 `outline[].slide_id` 一一对应且无重复;只修复计划或缺失页面,不重写已有页面。',
+ { outlineSlideIds: outlineIds, slideOrder: orderedIds },
+ );
+ }
+ return orderedIds;
+}
+
+function isCompleteSlideHtml(raw) {
+ const match = String(raw || '').match(
+ /^\uFEFF?\s*(?:]*>\s*)?]*)?>[\s\S]*?]*)?>([\s\S]*?)<\/body>[\s\S]*?<\/html>\s*$/i,
+ );
+ if (!match) return false;
+ return Boolean(match[1].replace(//g, '').trim());
+}
+
+async function readCompleteSlideWithRetry(readFile, relPath, options) {
+ const result = await readVisibleFileWithRetry(readFile, relPath, {
+ ...options,
+ accept: isCompleteSlideHtml,
+ });
+ return typeof result === 'string' ? result.trim() : null;
+}
+
+export async function readDeckProjectContract(readFile, options = {}) {
+ const plan = await readProjectPlanWithRetry(readFile, { ...options, requireComplete: true });
+ const slideOrder = validateCompletedPlan(plan);
+ const outlineById = new Map(plan.outline.map((item) => [String(item.slide_id), item]));
+ const slides = [];
+ const missingPaths = [];
+
+ for (let index = 0; index < slideOrder.length; index += 1) {
+ const slideId = slideOrder[index];
+ const relPath = `slides/${slideId}.html`;
+ const html = await readCompleteSlideWithRetry(readFile, relPath, options);
+ if (!html) {
+ missingPaths.push(relPath);
+ continue;
+ }
+ slides.push({
+ slideId,
+ slideNumber: index + 1,
+ relPath,
+ outlineEntry: outlineById.get(slideId),
+ html,
+ });
+ }
+
+ if (missingPaths.length) {
+ throw new DeckProjectContractError(missingSlideFilesDiagnostic(missingPaths));
+ }
+ return { plan, slides };
+}
diff --git a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/element-model-html.js b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/element-model-html.js
new file mode 100644
index 0000000000..87ac64e9f4
--- /dev/null
+++ b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/element-model-html.js
@@ -0,0 +1,163 @@
+const escapeHtml = (value) => String(value ?? '')
+ .replaceAll('&', '&')
+ .replaceAll('<', '<')
+ .replaceAll('>', '>')
+ .replaceAll('"', '"')
+ .replaceAll("'", ''');
+
+const DEFAULT_THEME = {
+ background: '#ffffff',
+ ink: '#111111',
+ muted: '#666666',
+ primary: '#0f766e',
+ accent: '#f97316',
+ panel: '#ffffff',
+};
+
+function colorMix(hex, alpha) {
+ const raw = String(hex || DEFAULT_THEME.primary).replace('#', '');
+ const normalized = raw.length === 3 ? raw.split('').map((value) => value + value).join('') : raw;
+ const parsed = Number.parseInt(normalized, 16);
+ if (!Number.isFinite(parsed)) return `rgba(15, 118, 110, ${alpha})`;
+ return `rgba(${(parsed >> 16) & 255}, ${(parsed >> 8) & 255}, ${parsed & 255}, ${alpha})`;
+}
+
+export function resolveElementColor(value, theme = {}) {
+ const palette = { ...DEFAULT_THEME, ...theme };
+ if (!value || value === 'transparent') return 'transparent';
+ if (value === 'soft') return colorMix(palette.primary, 0.1);
+ if (Object.hasOwn(palette, value)) return palette[value];
+ return String(value);
+}
+
+function editorFontSize(value) {
+ const size = Math.max(8, Number(value) || 24);
+ const cqw = Math.round((size / 10.2) * 1000) / 1000;
+ return `clamp(8px, ${cqw}cqw, ${size}px)`;
+}
+
+function elementStyle(element, theme, mode) {
+ const style = element?.style || {};
+ const fontSize = Math.max(8, Number(style.fontSize) || 24);
+ return [
+ `left:${Number(element?.x) || 0}%`,
+ `top:${Number(element?.y) || 0}%`,
+ `width:${Number(element?.w) || 0}%`,
+ `height:${Number(element?.h) || 0}%`,
+ `font-size:${mode === 'editor' ? editorFontSize(fontSize) : `${fontSize}px`}`,
+ `font-weight:${Number(style.fontWeight) || 600}`,
+ `color:${resolveElementColor(style.color || 'ink', theme)}`,
+ `text-align:${style.align || 'left'}`,
+ `background:${resolveElementColor(style.background || 'transparent', theme)}`,
+ `opacity:${Number.isFinite(Number(style.opacity)) ? Number(style.opacity) : 1}`,
+ `border-radius:${Math.max(0, Number(style.borderRadius) || 0)}px`,
+ ].join(';');
+}
+
+function semanticElementContent(element, mediaPlaceholder) {
+ const type = String(element?.type || 'text');
+ if (type === 'list') {
+ return `${(element.items || []).map((item) => `${escapeHtml(item)}
`).join('')}
`;
+ }
+ if (type === 'metric') {
+ return `${escapeHtml(element.text || '')}
${escapeHtml(element.label || '')}
`;
+ }
+ if (type === 'chart') {
+ const points = Array.isArray(element.data) ? element.data : [];
+ const max = Math.max(1, ...points.map((point) => Math.abs(Number(point?.value) || 0)));
+ return `${escapeHtml(element.text || '')}
${points.map((point) => {
+ const value = Number(point?.value) || 0;
+ const height = Math.max(8, Math.abs(value) / max * 100);
+ return `
${escapeHtml(point?.label || '')}
${escapeHtml(value)}
`;
+ }).join('')}
`;
+ }
+ if (type === 'media') {
+ const source = element.src || element.url || element.dataUrl || element.imageUrl || '';
+ const caption = element.text || element.label || mediaPlaceholder;
+ return `${source ? `
` : ''}${escapeHtml(caption)}
`;
+ }
+ const text = element.text || '';
+ return text ? `${escapeHtml(text)}
` : '';
+}
+
+function editorElementContent(element, mediaPlaceholder, editable) {
+ const type = String(element?.type || 'text');
+ if (type === 'list') {
+ return `${(element.items || []).map((item, index) => editable
+ ? `- ${escapeHtml(item)}
`
+ : `- ${escapeHtml(item)}
`).join('')}
`;
+ }
+ if (type === 'metric') {
+ return `${escapeHtml(element.text)}${escapeHtml(element.label)}`;
+ }
+ if (type === 'chart') {
+ const points = Array.isArray(element.data) ? element.data : [];
+ const max = Math.max(1, ...points.map((point) => Number(point?.value) || 0));
+ return `${escapeHtml(element.text)}${points.map((point) => `${escapeHtml(point?.label)}`).join('')}
`;
+ }
+ if (type === 'media') return `${escapeHtml(element.text || mediaPlaceholder)}`;
+ return editable
+ ? `${escapeHtml(element.text || '')}`
+ : escapeHtml(element.text || '');
+}
+
+export function elementModelElementHtml(element = {}, theme = {}, {
+ mode = 'semantic',
+ editable = false,
+ selectedId = '',
+ mediaPlaceholder = 'Media placeholder',
+} = {}) {
+ const type = String(element.type || 'text');
+ const selected = mode === 'editor' && editable && selectedId === element.id;
+ const content = mode === 'editor'
+ ? editorElementContent(element, mediaPlaceholder, editable)
+ : semanticElementContent(element, mediaPlaceholder);
+ return `${content}${selected ? '' : ''}
`;
+}
+
+export function buildElementSlideHtml(slide = {}) {
+ const theme = { ...DEFAULT_THEME, ...(slide.theme || {}) };
+ const elements = Array.isArray(slide.elements) ? slide.elements : [];
+ const fallback = elements.length
+ ? ''
+ : `${escapeHtml(slide.title || 'Slide')}
${slide.subtitle || slide.claim ? `
${escapeHtml(slide.subtitle || slide.claim)}
` : ''}
`;
+ return `
+
+
+
+
+${escapeHtml(slide.title || 'Slide')}
+
+
+
+ ${elements.map((element) => elementModelElementHtml(element, theme)).join('\n ')}
+ ${fallback}
+
+`;
+}
diff --git a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/export-diagnostics.js b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/export-diagnostics.js
new file mode 100644
index 0000000000..95c71005af
--- /dev/null
+++ b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/export-diagnostics.js
@@ -0,0 +1,133 @@
+const DIAGNOSTIC_REASONS = {
+ 'en-US': {
+ active_content_removed: 'Unsafe active content was removed.',
+ canvas_overflow: 'Slide content exceeded the canvas; visual fallback was used.',
+ canvas_size: 'Slide dimensions could not preserve native fidelity; visual fallback was used.',
+ text_out_of_bounds: 'Text exceeded the slide boundary; visual fallback was used.',
+ bottom_safety_margin: 'Text entered the bottom safety margin; visual fallback was used.',
+ css_gradient: 'A CSS gradient required visual fallback.',
+ css_filter: 'A CSS filter required visual fallback.',
+ generated_content: 'Generated CSS content required visual fallback.',
+ page_visual_fallback: 'A page visual fallback preserved the slide appearance.',
+ full_page_fallback: 'A full-page visual fallback preserved the slide appearance.',
+ unreadable_document: 'The slide document could not be read.',
+ unmeasurable_canvas: 'The slide canvas could not be measured.',
+ pptx_serialization: 'The slide could not be serialized to PPTX.',
+ full_page_raster_failed: 'The final visual fallback could not be rendered.',
+ },
+ 'zh-CN': {
+ active_content_removed: '已移除不安全的活动内容。',
+ canvas_overflow: '页面内容超出幻灯片边界,已切换视觉兜底。',
+ canvas_size: '页面尺寸无法保证原生保真,已切换视觉兜底。',
+ text_out_of_bounds: '文字超出幻灯片边界,已切换视觉兜底。',
+ bottom_safety_margin: '文字进入底部安全边距,已切换视觉兜底。',
+ css_gradient: 'CSS 渐变已使用视觉兜底。',
+ css_filter: 'CSS 滤镜已使用视觉兜底。',
+ generated_content: 'CSS 生成内容已使用视觉兜底。',
+ page_visual_fallback: '已使用页面视觉兜底保留外观。',
+ full_page_fallback: '已使用整页视觉兜底保留外观。',
+ unreadable_document: '无法读取幻灯片文档。',
+ unmeasurable_canvas: '无法测量幻灯片画布。',
+ pptx_serialization: '无法将幻灯片序列化为 PPTX。',
+ full_page_raster_failed: '最终视觉兜底渲染失败。',
+ },
+};
+
+const UNKNOWN_REASON = {
+ 'en-US': 'Export encountered a protected internal error.',
+ 'zh-CN': '导出遇到已保护的内部错误。',
+};
+
+export function sanitizeDiagnosticSourceId(value) {
+ const safe = String(value || '').replace(/[^a-zA-Z0-9_-]/g, '').slice(0, 48);
+ return safe || null;
+}
+
+export function formatLocalizedExportDiagnostic(diagnostic = {}, locale = 'en-US') {
+ const resolvedLocale = locale === 'zh-CN' ? locale : 'en-US';
+ const reason = DIAGNOSTIC_REASONS[resolvedLocale][diagnostic.code]
+ || UNKNOWN_REASON[resolvedLocale];
+ return {
+ slideNumber: Number.isFinite(diagnostic.slideNumber) ? diagnostic.slideNumber : diagnostic.slideNumber,
+ sourceId: sanitizeDiagnosticSourceId(diagnostic.sourceId),
+ phase: String(diagnostic.phase || '').replace(/[^a-z-]/g, '').slice(0, 32) || null,
+ severity: diagnostic.severity === 'blocking' ? 'blocking' : diagnostic.severity,
+ code: String(diagnostic.code || 'unknown').replace(/[^a-z0-9_-]/gi, '').slice(0, 64),
+ reason: reason.slice(0, 120),
+ };
+}
+
+export function localizeExportDiagnosticLocations(locations = [], locale = 'en-US') {
+ return locations.map((location) => formatLocalizedExportDiagnostic(location, locale));
+}
+
+export function summarizePptxExportDiagnostics(preparedSlides = []) {
+ const counts = {
+ repaired: 0,
+ svgImage: 0,
+ localPng: 0,
+ pageVisual: 0,
+ fullPage: 0,
+ blocking: 0,
+ };
+ const locations = [];
+ const seen = new Set();
+ const addLocation = (slideNumber, item, defaults = {}) => {
+ const location = {
+ slideNumber,
+ sourceId: item?.sourceId || defaults.sourceId || null,
+ phase: item?.phase || defaults.phase || null,
+ severity: item?.severity || defaults.severity || 'fallback',
+ code: item?.code || defaults.code || null,
+ reason: item?.reason || item?.message || defaults.reason || null,
+ };
+ const key = [
+ location.slideNumber,
+ location.sourceId,
+ location.phase,
+ location.severity,
+ location.code,
+ ].join(':');
+ if (seen.has(key)) return;
+ seen.add(key);
+ locations.push(location);
+ };
+
+ preparedSlides.forEach((prepared, arrayIndex) => {
+ const slideNumber = (prepared?.index ?? arrayIndex) + 1;
+ const slideData = prepared?.slideData || {};
+ (slideData.diagnostics || []).forEach((diagnostic) => {
+ if (diagnostic.severity === 'repaired') counts.repaired += 1;
+ if (diagnostic.severity === 'blocking') counts.blocking += 1;
+ if (diagnostic.severity === 'repaired' || diagnostic.severity === 'blocking'
+ || diagnostic.phase || diagnostic.sourceId) {
+ addLocation(slideNumber, diagnostic);
+ }
+ });
+ (slideData.fallbackLayers || []).forEach((layer) => {
+ if (layer.kind === 'svg-image') counts.svgImage += 1;
+ if (layer.kind === 'raster' && layer.phase === 'local-visual') counts.localPng += 1;
+ if (layer.kind === 'raster' && layer.phase === 'page-visual') counts.pageVisual += 1;
+ addLocation(slideNumber, layer, {
+ severity: 'fallback',
+ code: layer.kind === 'svg-image' ? 'svg_image_fallback' : 'png_visual_fallback',
+ });
+ });
+ if (slideData.fullPageFallback) {
+ counts.fullPage += 1;
+ addLocation(slideNumber, slideData.fullPageFallback, {
+ severity: 'fallback',
+ code: 'full_page_fallback',
+ phase: 'full-page',
+ });
+ }
+ });
+
+ return {
+ counts,
+ locations,
+ hasWarnings: counts.repaired + counts.svgImage + counts.localPng
+ + counts.pageVisual + counts.fullPage > 0,
+ hasBlocking: counts.blocking > 0,
+ };
+}
diff --git a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/export-html.js b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/export-html.js
index dec8af5c4b..deba9cfcba 100644
--- a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/export-html.js
+++ b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/export-html.js
@@ -1,6 +1,7 @@
import { escapeHtml } from './state.js';
import { getLocale } from './i18n.js';
import { normalizeSlideDocument, slideHtml } from './render.js';
+import { sanitizeSlideMarkup } from './sanitize-slide-markup.js';
export function buildHtmlDeck(state) {
if ((state.slides || []).some((slide) => slide.html)) {
@@ -28,7 +29,7 @@ ${deckCss()}
function buildSourceHtmlDeck(state) {
const slides = (state.slides || [])
.map((slide, index) => ``)
.join('\n');
return `
diff --git a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/export-slide-browser.js b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/export-slide-browser.js
index e992c30545..fc6b5679e0 100644
--- a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/export-slide-browser.js
+++ b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/export-slide-browser.js
@@ -5,18 +5,30 @@
// 1. Mount slide HTML in an off-screen shadow-DOM div (1280×720)
// 2. sanitizeSlideDocumentRoot() — normalize/repair the HTML for export
// 3. extractSlideDataFromDocument() — walk DOM → structured slideData
-// 4. Optionally rasterize a background layer (text hidden) for visual fidelity
+// 4. Rasterize isolated local visual layers when native/SVG mapping is unsafe
+// 5. Escalate failed local captures to page-visual, then full-page fallback
//
// The prepared slideData is then passed to export-deck-browser.js →
// pptx-html-build.js for the actual PPTX generation.
// ─────────────────────────────────────────────────────────────────────────────
import { normalizeSlideDocument, scopeSlideAuthorStyles } from './render.js';
+import { sanitizeSlideDocument, sanitizeSlideMarkup } from './sanitize-slide-markup.js';
import { sanitizeSlideDocumentRoot } from './sanitize-slide-html.js';
import { extractSlideDataFromDocument, measureBodyDimensions } from './html2pptx-dom-core.js';
+import { buildElementSlideHtml } from './element-model-html.js';
+import {
+ buildPageVisualFallbackRequest,
+ buildRasterFallbackRequests,
+ buildWholePageVisualFallbackRequest,
+ renderRasterFallbackPlan,
+} from './fallback-layer-render.js';
+
+export { buildElementSlideHtml };
export const EXPORT_VIEWPORT = { width: 1280, height: 720 };
const RASTER_TEXT_TYPES = new Set(['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'text', 'list', 'merged-text']);
+const EDITABLE_TEXT_TYPES = new Set([...RASTER_TEXT_TYPES, 'svg-text']);
export function countVectorTextElements(slideData) {
return (slideData?.elements || []).filter((el) => RASTER_TEXT_TYPES.has(el.type)).length;
@@ -94,11 +106,14 @@ function wrapExportDocument(root, body) {
body,
documentElement: root,
defaultView: window,
+ querySelector: (sel) => root.querySelector(sel),
querySelectorAll: (sel) => root.querySelectorAll(sel),
createElement: (tag) => document.createElement(tag),
+ createTreeWalker: (...args) => document.createTreeWalker(...args),
getElementById: (id) => root.querySelector(`#${id}`),
head: root.querySelector('style')?.parentElement || root,
_exportRoot: root,
+ _pptxSecurityDiagnostics: body._pptxSecurityDiagnostics || [],
};
}
@@ -141,7 +156,7 @@ async function waitForExportPaint() {
}
function mountMarkupOnRoot(root, markup) {
- const parsed = new DOMParser().parseFromString(markup, 'text/html');
+ const parsed = sanitizeSlideDocument(new DOMParser().parseFromString(markup, 'text/html'));
root.replaceChildren();
parsed.querySelectorAll('style').forEach((node) => {
@@ -151,6 +166,7 @@ function mountMarkupOnRoot(root, markup) {
});
const body = document.createElement('div');
+ body._pptxSecurityDiagnostics = parsed._pptxSecurityDiagnostics || [];
body.className = 'ppt-export-body';
if (parsed.body) {
for (const attr of parsed.body.attributes) {
@@ -206,16 +222,106 @@ function elementLabel(element) {
* these findings as repair requirements rather than silently rasterizing or
* flattening unsupported HTML.
*/
+export function analyzeMountedSlideForPptx(doc, source = '') {
+ if (!doc?.body) {
+ return {
+ valid: false,
+ issues: [{
+ severity: 'blocking',
+ kind: 'blocking',
+ code: 'unreadable_document',
+ message: 'The slide document could not be read.',
+ sourceId: 'slide-document',
+ }],
+ };
+ }
+ const issues = [...(doc._pptxSecurityDiagnostics || [])];
+ const seen = new Set(issues.map((item) => `${item.code}:${item.sourceId || ''}`));
+ const add = (code, message, element = null, severity = 'fallback') => {
+ const sourceId = element?.dataset?.pptxSourceId || element?.id || null;
+ const key = `${code}:${sourceId || ''}`;
+ if (seen.has(key)) return;
+ seen.add(key);
+ issues.push({
+ severity,
+ kind: severity === 'blocking' ? 'blocking' : undefined,
+ code,
+ message,
+ sourceId,
+ tag: element?.tagName?.toLowerCase?.() || null,
+ });
+ };
+ const body = doc.body;
+ if (body.querySelector('script,iframe,object,embed,base,meta[http-equiv="refresh" i],foreignObject,maction')) {
+ add('active_content_residual', 'Active content remained after sanitization.', body, 'blocking');
+ }
+ if (!String(source || '').trim() || !/<\/html>\s*$/i.test(String(source || '').trim())) {
+ add('incomplete_html', 'The slide document is incomplete.', body, 'blocking');
+ }
+ let bodyRect;
+ try {
+ bodyRect = body.getBoundingClientRect();
+ if (!(bodyRect.width > 0) || !(bodyRect.height > 0)) {
+ add('unmeasurable_canvas', 'The slide canvas could not be measured.', body, 'blocking');
+ }
+ } catch {
+ add('unmeasurable_canvas', 'The slide canvas could not be measured.', body, 'blocking');
+ }
+ if (bodyRect) {
+ if (Math.abs(bodyRect.width - EXPORT_VIEWPORT.width) > 2
+ || Math.abs(bodyRect.height - EXPORT_VIEWPORT.height) > 2) {
+ add('canvas_size', 'The slide canvas size requires page visual fallback.', body);
+ }
+ const dimensions = measureBodyDimensions(doc);
+ if (dimensions.errors?.length) {
+ add('canvas_overflow', 'Slide content exceeds the canvas.', body);
+ }
+ const view = doc.defaultView || window;
+ body.querySelectorAll('p,h1,h2,h3,h4,h5,h6,li').forEach((element) => {
+ const rect = element.getBoundingClientRect();
+ if (rect.width <= 0 || rect.height <= 0) return;
+ if (rect.left < bodyRect.left - 1 || rect.top < bodyRect.top - 1
+ || rect.right > bodyRect.right + 1 || rect.bottom > bodyRect.bottom + 1) {
+ add('text_out_of_bounds', 'Text extends outside the slide canvas.', element);
+ }
+ const computed = view.getComputedStyle(element);
+ if (parseFloat(computed.fontSize || 0) > 12 && rect.bottom > bodyRect.bottom - 48) {
+ add('bottom_safety_margin', 'Text enters the bottom safety margin.', element);
+ }
+ });
+ }
+ return { valid: issues.length === 0, issues: issues.slice(0, 32) };
+}
+
export async function validateSlideForPptxGeneration(html) {
+ let exportRoot = null;
+ try {
+ const doc = await loadHtmlInExportRoot(html);
+ exportRoot = doc._exportRoot;
+ sanitizeSlideDocumentRoot(doc);
+ await waitForExportPaint();
+ return analyzeMountedSlideForPptx(doc, html);
+ } finally {
+ if (exportRoot) removeExportRoot(exportRoot);
+ }
+}
+
+async function validateSlideForPptxGenerationLegacy(html) {
const source = String(html || '').trim();
const issues = [];
const seen = new Set();
- const add = (code, message, element = null) => {
+ const add = (code, message, element = null, severity = 'fallback') => {
const suffix = element ? ` (${elementLabel(element)})` : '';
const key = `${code}:${message}${suffix}`;
if (seen.has(key)) return;
seen.add(key);
- issues.push({ code, message: `${message}${suffix}` });
+ issues.push({
+ severity,
+ code,
+ message: `${message}${suffix}`,
+ sourceId: element?.dataset?.pptxSourceId || element?.id || null,
+ tag: element?.tagName?.toLowerCase?.() || null,
+ });
};
if (!source || !/<\/html>\s*$/i.test(source)) {
@@ -237,6 +343,19 @@ export async function validateSlideForPptxGeneration(html) {
exportRoot = doc._exportRoot;
const view = doc.defaultView || window;
const body = doc.body;
+ const sourceElements = [body, ...body.querySelectorAll('*')];
+ const usedSourceIds = new Set(
+ sourceElements.map((element) => element.dataset.pptxSourceId).filter(Boolean),
+ );
+ let sourceSequence = 1;
+ sourceElements.forEach((element) => {
+ if (element.dataset.pptxSourceId) return;
+ while (usedSourceIds.has(`pptx-source-${sourceSequence}`)) sourceSequence += 1;
+ const sourceId = `pptx-source-${sourceSequence}`;
+ element.dataset.pptxSourceId = sourceId;
+ usedSourceIds.add(sourceId);
+ sourceSequence += 1;
+ });
const bodyRect = body.getBoundingClientRect();
const bodyDimensions = measureBodyDimensions(doc);
bodyDimensions.errors.forEach((message) => add('canvas_overflow', message));
@@ -318,9 +437,19 @@ export async function validateSlideForPptxGeneration(html) {
try {
const slideData = extractSlideDataFromDocument(doc);
- (slideData.errors || []).forEach((message) => add('pptx_conversion', message));
+ (slideData.diagnostics || []).forEach((diagnostic) => {
+ const key = `${diagnostic.code}:${diagnostic.message}:${diagnostic.sourceId || ''}`;
+ if (seen.has(key)) return;
+ seen.add(key);
+ issues.push(diagnostic);
+ });
} catch (error) {
- add('pptx_conversion', String(error?.message || error || 'PPTX conversion validation failed.'));
+ add(
+ 'pptx_serialization',
+ String(error?.message || error || 'PPTX conversion validation failed.'),
+ null,
+ 'blocking',
+ );
}
} finally {
if (exportRoot) removeExportRoot(exportRoot);
@@ -337,23 +466,60 @@ async function prepareSlideOnce(html, aggressive, options = {}) {
try {
const doc = await loadHtmlInExportRoot(html);
exportRoot = doc._exportRoot;
- sanitizeSlideDocumentRoot(doc, aggressive);
+ const repairResult = sanitizeSlideDocumentRoot(doc, aggressive);
await waitForExportPaint();
const bodyDimensions = measureBodyDimensions(doc);
const slideData = extractSlideDataFromDocument(doc);
- // Content overflow must never block the export: clip/off-slide content is
- // preferable to a failed run. Demote overflow findings to warnings.
+ const analysis = analyzeMountedSlideForPptx(doc, html);
+ const mergedDiagnostics = [
+ ...(analysis.issues || []),
+ ...(repairResult?.diagnostics || []),
+ ...(slideData.diagnostics || []),
+ ];
+ const diagnosticKeys = new Set();
+ const diagnostics = mergedDiagnostics.filter((diagnostic) => {
+ const key = `${diagnostic.severity}:${diagnostic.code}:${diagnostic.sourceId || ''}`;
+ if (diagnosticKeys.has(key)) return false;
+ diagnosticKeys.add(key);
+ return true;
+ });
+ slideData.diagnostics = diagnostics;
+ const rasterRequests = buildRasterFallbackRequests(doc, diagnostics);
+ const pageFallbackCodes = new Set([
+ 'canvas_size', 'canvas_overflow', 'text_out_of_bounds', 'bottom_safety_margin',
+ ]);
+ const pageFallbackDiagnostics = diagnostics.filter((item) => pageFallbackCodes.has(item.code));
+ const nativeVisualSourceIds = [...new Set(
+ (slideData.elements || [])
+ .filter((element) => !EDITABLE_TEXT_TYPES.has(element.type))
+ .map((element) => element.sourceId)
+ .filter(Boolean),
+ )];
+ const pageVisualRequest = pageFallbackDiagnostics.length
+ ? buildWholePageVisualFallbackRequest(
+ doc,
+ pageFallbackDiagnostics,
+ nativeVisualSourceIds,
+ )
+ : buildPageVisualFallbackRequest(doc, rasterRequests);
const overflowWarnings = bodyDimensions.errors || [];
- if (overflowWarnings.length) {
- console.warn('[ppt-live-export] slide overflows canvas; exporting anyway:', overflowWarnings.join('; '));
- }
const safeBodyDimensions = { ...bodyDimensions, errors: [] };
- const errors = slideData.errors || [];
- if (!errors.length || options.allowValidationErrors) {
- return { slideData, bodyDimensions: safeBodyDimensions, aggressive, warnings: overflowWarnings };
+ const blocking = diagnostics.filter((diagnostic) => diagnostic.severity === 'blocking');
+ if (!blocking.length || options.allowValidationErrors) {
+ return {
+ slideData,
+ bodyDimensions: safeBodyDimensions,
+ diagnostics,
+ rasterRequests,
+ pageVisualRequest,
+ aggressive,
+ warnings: overflowWarnings,
+ };
}
- return { error: new Error(errors.join('\n')) };
+ const error = new Error(blocking.map((diagnostic) => diagnostic.message).join('\n'));
+ error.diagnostics = blocking;
+ return { error };
} finally {
if (exportRoot) removeExportRoot(exportRoot);
}
@@ -374,34 +540,46 @@ export async function prepareSlidesForPptxExport(slides, options = {}) {
for (const [index, slide] of slides.entries()) {
if (!slide?.html) continue;
const item = await prepareSlideForPptxExport(slide.html, options);
- let rasterBase64 = null;
- const vectorTextCount = countVectorTextElements(item.slideData);
- const rasterOnly = vectorTextCount === 0;
- // Always render a raster background when the host WebView is available.
- // For slides with vector text, slideHtmlForRasterBackdrop hides ALL text
- // via universal CSS — the raster contains only visual elements (backgrounds,
- // borders, images) and the vector layer overlays editable text. This
- // avoids text-overlap from tag-based selective hiding.
- if (typeof options.renderRaster === 'function') {
- try {
- if (typeof options.onRasterProgress === 'function') {
- options.onRasterProgress(index, slide);
- }
- const rasterHtml = rasterOnly
- ? slideExportHtml(slide)
- : slideHtmlForRasterBackdrop(slide.html);
- rasterBase64 = await options.renderRaster(rasterHtml, index);
- } catch {
- rasterBase64 = null;
- }
+ if (item.rasterRequests?.length && typeof options.onRasterProgress === 'function') {
+ options.onRasterProgress(index, slide);
+ }
+ const rasterResult = await renderRasterFallbackPlan({
+ localRequests: item.rasterRequests || [],
+ pageVisualRequest: item.pageVisualRequest,
+ fullPageRequest: {
+ sourceId: `slide-${index + 1}`,
+ zIndex: 0,
+ paintOrder: 0,
+ kind: 'raster',
+ phase: 'full-page',
+ bbox: { x: 0, y: 0, w: 13.333, h: 7.5 },
+ buildHtml: () => slideExportHtml(slide),
+ diagnostics: [],
+ },
+ }, options.renderRaster, index);
+ if (rasterResult.blocking) {
+ const error = new Error(
+ rasterResult.diagnostics.map((diagnostic) => diagnostic.message).join('\n')
+ || `Slide ${index + 1} fallback rendering failed`,
+ );
+ error.diagnostics = rasterResult.diagnostics;
+ throw error;
}
+ item.slideData.fallbackLayers = [
+ ...(item.slideData.fallbackLayers || []),
+ ...rasterResult.layers,
+ ];
+ item.slideData.fullPageFallback = rasterResult.fullPageFallback;
+ item.slideData.diagnostics = [
+ ...(item.slideData.diagnostics || []),
+ ...rasterResult.diagnostics,
+ ];
prepared.push({
index,
slideId: slide.id,
notes: slide,
...item,
- rasterBase64,
- rasterOnly: Boolean(rasterBase64 && rasterOnly),
+ fallbackDiagnostics: rasterResult.diagnostics,
});
}
return prepared;
@@ -410,45 +588,7 @@ export async function prepareSlidesForPptxExport(slides, options = {}) {
}
}
-export function buildElementSlideHtml(slide = {}) {
- const theme = slide.theme || {};
- const title = String(slide.title || 'Slide').replace(/[<>&]/g, (ch) => ({
- '<': '<', '>': '>', '&': '&',
- })[ch] || ch);
- const subtitle = String(slide.subtitle || slide.claim || '').replace(/[<>&]/g, (ch) => ({
- '<': '<', '>': '>', '&': '&',
- })[ch] || ch);
- const background = theme.background || '#ffffff';
- const ink = theme.ink || '#111111';
- const muted = theme.muted || '#666666';
- return `
-
-
-
-
-
-
- ${title}
- ${subtitle ? `${subtitle}
` : ''}
-
-`;
-}
-
export function slideExportHtml(slide) {
- if (slide?.html) return normalizeSlideDocument(slide.html);
- return buildElementSlideHtml(slide);
+ if (slide?.html) return sanitizeSlideMarkup(normalizeSlideDocument(slide.html));
+ return sanitizeSlideMarkup(buildElementSlideHtml(slide));
}
diff --git a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/fallback-layer-render.js b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/fallback-layer-render.js
new file mode 100644
index 0000000000..a1697bf577
--- /dev/null
+++ b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/fallback-layer-render.js
@@ -0,0 +1,516 @@
+import { buildDomPaintOrderMap } from './paint-order.js';
+
+const EXPORT_WIDTH = 1280;
+const EXPORT_HEIGHT = 720;
+const LOCAL_RASTER_CODES = new Set([
+ 'css_gradient',
+ 'css_filter',
+ 'computed_gradient',
+ 'generated_content',
+ 'container_background_image',
+ 'merge_background_image',
+ 'complex_svg_raster',
+]);
+
+function rasterLayerZIndex(element, view) {
+ let current = element;
+ while (current) {
+ const raw = view.getComputedStyle(current).zIndex;
+ if (raw && raw !== 'auto') {
+ const parsed = parseInt(raw, 10);
+ if (Number.isFinite(parsed)) return parsed;
+ }
+ current = current.parentElement;
+ }
+ return 0;
+}
+
+function escapedAttributeValue(value) {
+ return String(value).replace(/["\\]/g, '\\$&');
+}
+
+function serializeRasterTargetDocument(doc, targetSpecs) {
+ const targets = (Array.isArray(targetSpecs) ? targetSpecs : [targetSpecs]).map((target) => (
+ typeof target === 'string'
+ ? { sourceId: target, captureStrategy: 'visual-subtree' }
+ : target
+ ));
+ const sourceRoot = doc._exportRoot || doc.documentElement;
+ const sourceBody = doc.body;
+ const bodyClone = sourceBody.cloneNode(true);
+ let targetCount = 0;
+ targets.forEach(({ sourceId, captureStrategy }) => {
+ const target = bodyClone.querySelector(
+ `[data-pptx-source-id="${escapedAttributeValue(sourceId)}"]`,
+ ) || (bodyClone.dataset?.pptxSourceId === sourceId ? bodyClone : null);
+ if (!target) return;
+ target.setAttribute('data-pptx-raster-target', '1');
+ target.setAttribute('data-pptx-capture-strategy', captureStrategy);
+ targetCount += 1;
+ });
+ if (!targetCount) return null;
+
+ const authorStyles = [...sourceRoot.querySelectorAll('style')]
+ .map((style) => style.textContent || '')
+ .join('\n');
+ const bodyAttributes = [...bodyClone.attributes]
+ .filter((attribute) => !['class', 'style'].includes(attribute.name))
+ .map((attribute) => ` ${attribute.name}="${String(attribute.value).replace(/"/g, '"')}"`)
+ .join('');
+ const isolationCss = `
+html, body, .ppt-export-root, .ppt-export-body {
+ margin: 0 !important;
+ padding: 0 !important;
+ width: ${EXPORT_WIDTH}px !important;
+ height: ${EXPORT_HEIGHT}px !important;
+ overflow: hidden !important;
+ background: transparent !important;
+ background-color: transparent !important;
+}
+.ppt-export-body [data-pptx-source-id] {
+ visibility: hidden !important;
+}
+.ppt-export-body [data-pptx-raster-target="1"],
+.ppt-export-body [data-pptx-capture-strategy="visual-subtree"] * {
+ visibility: visible !important;
+}
+.ppt-export-body [data-pptx-capture-strategy="self-decoration"] > *,
+.ppt-export-body [data-pptx-capture-strategy="pseudo-only"] > * {
+ visibility: hidden !important;
+}
+.ppt-export-body [data-pptx-capture-strategy="pseudo-only"] {
+ background: none !important;
+ background-image: none !important;
+ border-color: transparent !important;
+ box-shadow: none !important;
+ filter: none !important;
+}
+.ppt-export-body [data-pptx-raster-target="1"] :is(p,h1,h2,h3,h4,h5,h6,li,span,a,small,label,code,b,strong,i,em,u,mark,sub,sup),
+.ppt-export-body [data-pptx-raster-target="1"]:is(p,h1,h2,h3,h4,h5,h6,li,span,a,small,label,code,b,strong,i,em,u,mark,sub,sup) {
+ color: transparent !important;
+ -webkit-text-fill-color: transparent !important;
+ text-shadow: none !important;
+}
+.ppt-export-body [data-pptx-raster-target="1"] ::marker {
+ color: transparent !important;
+ -webkit-text-fill-color: transparent !important;
+}
+.ppt-export-body [data-pptx-raster-target="1"] svg text,
+.ppt-export-body svg[data-pptx-raster-target="1"] text {
+ visibility: hidden !important;
+}
+`;
+ return ``;
+}
+
+export function buildRasterFallbackRequests(doc, diagnostics = []) {
+ const bodyRect = doc.body.getBoundingClientRect();
+ const view = doc.defaultView || globalThis.window;
+ const sourceOrder = buildDomPaintOrderMap(doc);
+ const grouped = new Map();
+ diagnostics.forEach((diagnostic) => {
+ if (!LOCAL_RASTER_CODES.has(diagnostic.code) || !diagnostic.sourceId) return;
+ const captureStrategy = diagnostic.code === 'generated_content'
+ ? 'pseudo-only'
+ : (['css_filter', 'complex_svg_raster'].includes(diagnostic.code)
+ ? 'visual-subtree'
+ : 'self-decoration');
+ const key = `${diagnostic.sourceId}:${captureStrategy}`;
+ if (!grouped.has(key)) {
+ grouped.set(key, {
+ sourceId: diagnostic.sourceId,
+ captureStrategy,
+ diagnostics: [],
+ });
+ }
+ grouped.get(key).diagnostics.push(diagnostic);
+ });
+ const requests = [];
+ grouped.forEach(({ sourceId, captureStrategy, diagnostics: sourceDiagnostics }) => {
+ const element = doc.body.dataset?.pptxSourceId === sourceId
+ ? doc.body
+ : doc.body.querySelector(
+ `[data-pptx-source-id="${escapedAttributeValue(sourceId)}"]`,
+ );
+ const failureRequest = (code, reason, details = {}) => ({
+ sourceId,
+ zIndex: details.zIndex ?? 0,
+ paintOrder: sourceOrder.get(sourceId) ?? requests.length,
+ subOrder: 0,
+ kind: 'raster',
+ phase: 'local-visual',
+ canvas: 'full-page',
+ captureStrategy,
+ suppressedNativeVisualIds: [],
+ bbox: details.bbox || { x: 0, y: 0, w: 0, h: 0 },
+ diagnostics: sourceDiagnostics,
+ buildFailure: {
+ code,
+ stage: 'request-build',
+ reason,
+ },
+ });
+ if (!element) {
+ requests.push(failureRequest(
+ 'local_raster_target_missing',
+ `Raster fallback source "${sourceId}" is not present in the export document.`,
+ ));
+ return;
+ }
+ let rect;
+ try {
+ rect = element.getBoundingClientRect();
+ } catch (error) {
+ requests.push(failureRequest(
+ 'local_raster_serialize_failed',
+ `Raster fallback source "${sourceId}" could not be measured: ${String(error?.message || error)}`,
+ { zIndex: rasterLayerZIndex(element, view) },
+ ));
+ return;
+ }
+ const bbox = {
+ x: (rect.left - bodyRect.left) / 96,
+ y: (rect.top - bodyRect.top) / 96,
+ w: rect.width / 96,
+ h: rect.height / 96,
+ };
+ if (!(rect.width > 0) || !(rect.height > 0)) {
+ requests.push(failureRequest(
+ 'local_raster_unmeasurable',
+ `Raster fallback source "${sourceId}" has no measurable width or height.`,
+ { zIndex: rasterLayerZIndex(element, view), bbox },
+ ));
+ return;
+ }
+ try {
+ if (!serializeRasterTargetDocument(doc, { sourceId, captureStrategy })) {
+ throw new Error('Raster target was not retained in the export document.');
+ }
+ } catch (error) {
+ requests.push(failureRequest(
+ 'local_raster_serialize_failed',
+ `Raster fallback source "${sourceId}" could not be serialized: ${String(error?.message || error)}`,
+ { zIndex: rasterLayerZIndex(element, view), bbox },
+ ));
+ return;
+ }
+ const suppressedNativeVisualIds = captureStrategy === 'pseudo-only'
+ ? []
+ : (captureStrategy === 'self-decoration'
+ ? [sourceId]
+ : [element, ...element.querySelectorAll('[data-pptx-source-id]')]
+ .map((item) => item.dataset?.pptxSourceId)
+ .filter(Boolean));
+ requests.push({
+ sourceId,
+ zIndex: rasterLayerZIndex(element, view),
+ paintOrder: sourceOrder.get(sourceId) ?? requests.length,
+ subOrder: 0,
+ kind: 'raster',
+ phase: 'local-visual',
+ canvas: 'full-page',
+ captureStrategy,
+ suppressedNativeVisualIds: [...new Set(suppressedNativeVisualIds)],
+ bbox,
+ buildHtml: () => serializeRasterTargetDocument(doc, { sourceId, captureStrategy }),
+ diagnostics: sourceDiagnostics,
+ });
+ });
+ return requests.sort((a, b) => a.zIndex - b.zIndex || a.paintOrder - b.paintOrder);
+}
+
+export function buildPageVisualFallbackRequest(doc, localRequests = []) {
+ const sourceIds = [...new Set(localRequests.map((request) => request.sourceId).filter(Boolean))];
+ if (!sourceIds.length) return null;
+ const targetSpecs = localRequests.map((request) => ({
+ sourceId: request.sourceId,
+ captureStrategy: request.captureStrategy,
+ }));
+ const request = {
+ sourceId: 'slide-visuals',
+ sourceIds,
+ captureStrategy: 'page-visual',
+ suppressedNativeVisualIds: [...new Set(
+ localRequests.flatMap((request) => request.suppressedNativeVisualIds || []),
+ )],
+ zIndex: Math.min(...localRequests.map((request) => request.zIndex ?? 0)),
+ paintOrder: Math.min(...localRequests.map((request) => request.paintOrder ?? 0)),
+ subOrder: Math.min(...localRequests.map((request) => request.subOrder ?? 0)),
+ kind: 'raster',
+ phase: 'page-visual',
+ canvas: 'full-page',
+ bbox: { x: 0, y: 0, w: 13.333, h: 7.5 },
+ diagnostics: localRequests.flatMap((request) => request.diagnostics || []),
+ };
+ const missingSourceIds = localRequests
+ .filter((localRequest) => localRequest.buildFailure?.code === 'local_raster_target_missing')
+ .map((localRequest) => localRequest.sourceId);
+ if (missingSourceIds.length) {
+ return {
+ ...request,
+ buildFailure: {
+ code: 'page_visual_target_missing',
+ stage: 'request-build',
+ reason: `Page-visual fallback cannot cover missing sources: ${missingSourceIds.join(', ')}.`,
+ },
+ };
+ }
+ try {
+ if (!serializeRasterTargetDocument(doc, targetSpecs)) {
+ return {
+ ...request,
+ buildFailure: {
+ code: 'page_visual_target_missing',
+ stage: 'request-build',
+ reason: `Page-visual fallback could not locate any requested sources: ${sourceIds.join(', ')}.`,
+ },
+ };
+ }
+ return {
+ ...request,
+ buildHtml: () => serializeRasterTargetDocument(doc, targetSpecs),
+ };
+ } catch (error) {
+ return {
+ ...request,
+ buildFailure: {
+ code: 'page_visual_serialize_failed',
+ stage: 'request-build',
+ reason: `Page-visual fallback could not be serialized: ${String(error?.message || error)}`,
+ },
+ };
+ }
+}
+
+export function buildWholePageVisualFallbackRequest(
+ doc,
+ diagnostics = [],
+ suppressedNativeVisualIds = [],
+) {
+ const bodySourceId = doc?.body?.dataset?.pptxSourceId;
+ if (!bodySourceId) {
+ return {
+ sourceId: 'slide-visuals',
+ phase: 'page-visual',
+ kind: 'raster',
+ bbox: { x: 0, y: 0, w: 13.333, h: 7.5 },
+ diagnostics,
+ suppressedNativeVisualIds: [...new Set(suppressedNativeVisualIds)],
+ buildFailure: {
+ code: 'page_visual_target_missing',
+ stage: 'request-build',
+ reason: 'Whole-page visual source is unavailable.',
+ },
+ };
+ }
+ const request = buildPageVisualFallbackRequest(doc, [{
+ sourceId: bodySourceId,
+ captureStrategy: 'visual-subtree',
+ suppressedNativeVisualIds: [...new Set(suppressedNativeVisualIds)],
+ zIndex: 0,
+ paintOrder: 0,
+ subOrder: 0,
+ diagnostics,
+ }]);
+ return {
+ ...request,
+ sourceId: 'slide-visuals',
+ sourceIds: [bodySourceId],
+ captureStrategy: 'whole-page-visual',
+ };
+}
+
+export async function renderRasterFallbackLayers(requests, renderRaster, slideIndex) {
+ const layers = [];
+ const failures = [];
+ if (typeof renderRaster !== 'function') {
+ return { layers, failures: [...requests] };
+ }
+ for (const request of requests) {
+ if (request.buildFailure) {
+ failures.push({
+ ...request,
+ error: request.buildFailure.reason,
+ });
+ continue;
+ }
+ try {
+ const html = typeof request.buildHtml === 'function' ? request.buildHtml() : request.html;
+ if (!html) throw new Error('Raster fallback HTML could not be generated');
+ const rendered = await renderRaster(html, slideIndex, {
+ sourceId: request.sourceId,
+ bbox: request.bbox,
+ zIndex: request.zIndex,
+ paintOrder: request.paintOrder,
+ subOrder: request.subOrder,
+ phase: request.phase,
+ captureStrategy: request.captureStrategy,
+ suppressedNativeVisualIds: request.suppressedNativeVisualIds || [],
+ });
+ const raw = String(rendered || '').replace(/^data:.*;base64,/, '');
+ if (!raw) throw new Error('Raster renderer returned no PNG data');
+ layers.push({
+ ...request,
+ buildHtml: undefined,
+ html: undefined,
+ data: `data:image/png;base64,${raw}`,
+ });
+ } catch (error) {
+ failures.push({ ...request, error: String(error?.message || error) });
+ }
+ }
+ return { layers, failures };
+}
+
+function failureDiagnostic(code, failure, slideIndex, severity = 'fallback') {
+ const reason = failure.buildFailure?.reason || failure.error;
+ return {
+ severity,
+ kind: severity === 'blocking' ? 'blocking' : undefined,
+ code: failure.buildFailure?.code || code,
+ message: `${failure.phase || 'raster'} fallback failed for ${failure.sourceId || 'slide visual'}: ${reason}`,
+ sourceId: failure.sourceId || null,
+ slideNumber: slideIndex + 1,
+ phase: failure.phase || null,
+ stage: failure.buildFailure?.stage || 'render',
+ reason,
+ };
+}
+
+export async function renderRasterFallbackPlan(plan, renderRaster, slideIndex) {
+ const diagnostics = [];
+ if (plan.pageVisualRequest?.captureStrategy === 'whole-page-visual') {
+ const pageResult = await renderRasterFallbackLayers(
+ [plan.pageVisualRequest],
+ renderRaster,
+ slideIndex,
+ );
+ if (pageResult.layers.length) {
+ return {
+ layers: pageResult.layers,
+ fullPageFallback: null,
+ diagnostics: [{
+ severity: 'fallback',
+ code: 'page_visual_fallback',
+ message: `Slide ${slideIndex + 1} used a page visual fallback.`,
+ sourceId: 'slide-visuals',
+ slideNumber: slideIndex + 1,
+ phase: 'page-visual',
+ }],
+ blocking: false,
+ };
+ }
+ diagnostics.push(...pageResult.failures.map((failure) => (
+ failureDiagnostic('page_visual_raster_failed', failure, slideIndex)
+ )));
+ const fullResult = await renderRasterFallbackLayers(
+ plan.fullPageRequest ? [plan.fullPageRequest] : [],
+ renderRaster,
+ slideIndex,
+ );
+ if (fullResult.layers.length) {
+ return {
+ layers: [],
+ fullPageFallback: fullResult.layers[0],
+ diagnostics: [
+ ...diagnostics,
+ {
+ severity: 'fallback',
+ code: 'full_page_fallback',
+ message: `Slide ${slideIndex + 1} used a full-page fallback.`,
+ sourceId: fullResult.layers[0].sourceId,
+ slideNumber: slideIndex + 1,
+ phase: 'full-page',
+ },
+ ],
+ blocking: false,
+ };
+ }
+ diagnostics.push(...fullResult.failures.map((failure) => (
+ failureDiagnostic('full_page_raster_failed', failure, slideIndex, 'blocking')
+ )));
+ return { layers: [], fullPageFallback: null, diagnostics, blocking: true };
+ }
+ const localResult = await renderRasterFallbackLayers(
+ plan.localRequests || [],
+ renderRaster,
+ slideIndex,
+ );
+ if (!localResult.failures.length) {
+ return {
+ layers: localResult.layers,
+ fullPageFallback: null,
+ diagnostics,
+ blocking: false,
+ };
+ }
+
+ diagnostics.push(...localResult.failures.map((failure) => (
+ failureDiagnostic('local_raster_failed', failure, slideIndex)
+ )));
+
+ if (plan.pageVisualRequest) {
+ const pageResult = await renderRasterFallbackLayers(
+ [plan.pageVisualRequest],
+ renderRaster,
+ slideIndex,
+ );
+ if (pageResult.layers.length) {
+ const layer = pageResult.layers[0];
+ diagnostics.push({
+ severity: 'fallback',
+ code: 'page_visual_fallback',
+ message: `Slide ${slideIndex + 1} used a transparent page visual fallback.`,
+ sourceId: layer.sourceId,
+ slideNumber: slideIndex + 1,
+ phase: 'page-visual',
+ reason: 'One or more local visual layers could not be rendered.',
+ });
+ return {
+ layers: [layer],
+ fullPageFallback: null,
+ diagnostics,
+ blocking: false,
+ };
+ }
+ diagnostics.push(...pageResult.failures.map((failure) => (
+ failureDiagnostic('page_visual_raster_failed', failure, slideIndex)
+ )));
+ }
+
+ if (plan.fullPageRequest) {
+ const fullResult = await renderRasterFallbackLayers(
+ [plan.fullPageRequest],
+ renderRaster,
+ slideIndex,
+ );
+ if (fullResult.layers.length) {
+ const fullPageFallback = fullResult.layers[0];
+ diagnostics.push({
+ severity: 'fallback',
+ code: 'full_page_fallback',
+ message: `Slide ${slideIndex + 1} was exported as a full-page PNG fallback.`,
+ sourceId: fullPageFallback.sourceId,
+ slideNumber: slideIndex + 1,
+ phase: 'full-page',
+ reason: 'Local and transparent page visual fallback rendering failed.',
+ });
+ return {
+ layers: [],
+ fullPageFallback,
+ diagnostics,
+ blocking: false,
+ };
+ }
+ diagnostics.push(...fullResult.failures.map((failure) => (
+ failureDiagnostic('full_page_raster_failed', failure, slideIndex, 'blocking')
+ )));
+ }
+
+ return {
+ layers: [],
+ fullPageFallback: null,
+ diagnostics,
+ blocking: true,
+ };
+}
diff --git a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/flat-select.js b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/flat-select.js
index 8cef403aa9..e56eaf678a 100644
--- a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/flat-select.js
+++ b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/flat-select.js
@@ -207,7 +207,7 @@ export function refreshFlatSelectLabels() {
});
}
-if (!window.__pptLiveFlatSelectBound) {
+if (typeof window !== 'undefined' && typeof document !== 'undefined' && !window.__pptLiveFlatSelectBound) {
window.__pptLiveFlatSelectBound = true;
document.addEventListener('click', handleOutsideClick);
document.addEventListener('keydown', (event) => {
diff --git a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/html2pptx-dom-core.js b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/html2pptx-dom-core.js
index 9eb0b9d976..369e58251e 100644
--- a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/html2pptx-dom-core.js
+++ b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/html2pptx-dom-core.js
@@ -13,6 +13,8 @@
// Unit conversions: 96 px = 1 inch, 1 px = 0.75 pt, PPTX uses inches/EMU.
// Slide canvas: 1280×720 px = 13.333"×7.5" (LAYOUT_WIDE).
// ─────────────────────────────────────────────────────────────────────────────
+import { buildDomPaintOrderMap } from './paint-order.js';
+
export const PT_PER_PX = 0.75;
export const PX_PER_IN = 96;
@@ -43,7 +45,57 @@ export function measureBodyDimensions(doc = document) {
export function extractSlideDataFromDocument(doc = document) {
const document = doc;
- const view = document.defaultView || window;
+ const view = document?.defaultView || globalThis.window;
+ const diagnostics = [];
+ const diagnosticKeys = new Set();
+
+ const addDiagnostic = (severity, code, message, element = null) => {
+ const sourceId = element?.dataset?.pptxSourceId || element?.id || null;
+ const key = `${severity}:${code}:${sourceId || ''}`;
+ if (diagnosticKeys.has(key)) return;
+ diagnosticKeys.add(key);
+ const diagnostic = {
+ severity,
+ kind: severity === 'blocking' ? 'blocking' : undefined,
+ code,
+ message,
+ sourceId,
+ tag: element?.tagName?.toLowerCase?.() || null,
+ };
+ try {
+ const rect = element?.getBoundingClientRect?.();
+ if (rect && rect.width > 0 && rect.height > 0) {
+ diagnostic.bbox = {
+ x: rect.left,
+ y: rect.top,
+ width: rect.width,
+ height: rect.height,
+ };
+ }
+ } catch {
+ // bbox is optional.
+ }
+ diagnostics.push(diagnostic);
+ };
+
+ if (!document?.body || !view?.getComputedStyle) {
+ addDiagnostic(
+ 'blocking',
+ 'unreadable_document',
+ 'Slide document has no readable body or computed-style view.',
+ document?.documentElement || null,
+ );
+ const unreadableDiagnostic = diagnostics[diagnostics.length - 1];
+ unreadableDiagnostic.sourceId = unreadableDiagnostic.sourceId || 'slide-document';
+ unreadableDiagnostic.tag = 'document';
+ return {
+ background: { type: 'color', value: 'FFFFFF' },
+ elements: [],
+ placeholders: [],
+ diagnostics,
+ errors: diagnostics.map((item) => item.message),
+ };
+ }
const PT_PER_PX = 0.75;
const PX_PER_IN = 96;
@@ -252,7 +304,7 @@ export function extractSlideDataFromDocument(doc = document) {
element.childNodes.forEach((node) => {
let textTransform = baseTextTransform;
- const isText = node.nodeType === Node.TEXT_NODE || node.tagName === 'BR';
+ const isText = node.nodeType === view.Node.TEXT_NODE || node.tagName === 'BR';
if (isText) {
const text = node.tagName === 'BR' ? '\n' : textTransform(node.textContent.replace(/\s+/g, ' '));
const prevRun = runs[runs.length - 1];
@@ -262,7 +314,7 @@ export function extractSlideDataFromDocument(doc = document) {
runs.push({ text, options: { ...baseOptions } });
}
- } else if (node.nodeType === Node.ELEMENT_NODE && node.textContent.trim()) {
+ } else if (node.nodeType === view.Node.ELEMENT_NODE && node.textContent.trim()) {
const options = { ...baseOptions };
const computed = view.getComputedStyle(node);
@@ -287,16 +339,16 @@ export function extractSlideDataFromDocument(doc = document) {
// Validate: Check for margins on inline elements
if (computed.marginLeft && parseFloat(computed.marginLeft) > 0) {
- errors.push(`Inline element <${node.tagName.toLowerCase()}> has margin-left which is not supported in PowerPoint. Remove margin from inline elements.`);
+ addDiagnostic('fallback', 'inline_margin', `Inline element <${node.tagName.toLowerCase()}> has margin-left; fallback layout may be needed.`, node);
}
if (computed.marginRight && parseFloat(computed.marginRight) > 0) {
- errors.push(`Inline element <${node.tagName.toLowerCase()}> has margin-right which is not supported in PowerPoint. Remove margin from inline elements.`);
+ addDiagnostic('fallback', 'inline_margin', `Inline element <${node.tagName.toLowerCase()}> has margin-right; fallback layout may be needed.`, node);
}
if (computed.marginTop && parseFloat(computed.marginTop) > 0) {
- errors.push(`Inline element <${node.tagName.toLowerCase()}> has margin-top which is not supported in PowerPoint. Remove margin from inline elements.`);
+ addDiagnostic('fallback', 'inline_margin', `Inline element <${node.tagName.toLowerCase()}> has margin-top; fallback layout may be needed.`, node);
}
if (computed.marginBottom && parseFloat(computed.marginBottom) > 0) {
- errors.push(`Inline element <${node.tagName.toLowerCase()}> has margin-bottom which is not supported in PowerPoint. Remove margin from inline elements.`);
+ addDiagnostic('fallback', 'inline_margin', `Inline element <${node.tagName.toLowerCase()}> has margin-bottom; fallback layout may be needed.`, node);
}
// Recursively process the child node. This will flatten nested spans into multiple runs.
@@ -328,7 +380,7 @@ export function extractSlideDataFromDocument(doc = document) {
const style = view.getComputedStyle(el);
const bgImage = style.backgroundImage || '';
if (bgImage.includes('linear-gradient') || bgImage.includes('radial-gradient')) {
- return { gradient: true };
+ return { gradient: true, element: el };
}
if (bgImage && bgImage !== 'none') {
const urlMatch = bgImage.match(/url\(["']?([^"')]+)["']?\)/);
@@ -345,6 +397,14 @@ export function extractSlideDataFromDocument(doc = document) {
// Extract background from body / slide root wrapper
const body = document.body;
const bodyRect = body.getBoundingClientRect();
+ if (!(bodyRect.width > 0) || !(bodyRect.height > 0)) {
+ addDiagnostic(
+ 'blocking',
+ 'unmeasurable_canvas',
+ 'Slide canvas has no measurable width or height.',
+ body,
+ );
+ }
const boxFor = (rect) => ({
left: rect.left - bodyRect.left,
top: rect.top - bodyRect.top,
@@ -390,14 +450,52 @@ export function extractSlideDataFromDocument(doc = document) {
};
const readZIndex = (el) => {
- const raw = view.getComputedStyle(el).zIndex;
- if (!raw || raw === 'auto') return 0;
- const parsed = parseInt(raw, 10);
- return Number.isFinite(parsed) ? parsed : 0;
+ let current = el;
+ while (current && current !== body) {
+ const raw = view.getComputedStyle(current).zIndex;
+ if (raw && raw !== 'auto') {
+ const parsed = parseInt(raw, 10);
+ if (Number.isFinite(parsed)) return parsed;
+ }
+ current = current.parentElement;
+ }
+ return 0;
};
+ const domPaintOrder = buildDomPaintOrderMap(document);
+ const sourceSubOrders = new Map();
+ let unmappedPaintOrder = Math.max(-1, ...domPaintOrder.values()) + 1;
+ const nextPaintMetadata = (el) => {
+ const sourceId = el?.dataset?.pptxSourceId || el?.id || null;
+ const subOrder = sourceSubOrders.get(sourceId) || 0;
+ sourceSubOrders.set(sourceId, subOrder + 1);
+ return {
+ sourceId,
+ paintOrder: domPaintOrder.get(sourceId) ?? unmappedPaintOrder++,
+ subOrder,
+ };
+ };
const pushElement = (entry, el) => {
- if (el) entry.zIndex = readZIndex(el);
+ const paintMetadata = nextPaintMetadata(el);
+ entry.kind = entry.kind || 'native';
+ entry.paintOrder = entry.paintOrder ?? paintMetadata.paintOrder;
+ entry.subOrder = entry.subOrder ?? paintMetadata.subOrder;
+ if (!entry.bbox) {
+ if (entry.position) {
+ entry.bbox = { ...entry.position };
+ } else if (entry.type === 'line') {
+ entry.bbox = {
+ x: Math.min(entry.x1, entry.x2),
+ y: Math.min(entry.y1, entry.y2),
+ w: Math.abs(entry.x2 - entry.x1),
+ h: Math.abs(entry.y2 - entry.y1),
+ };
+ }
+ }
+ if (el) {
+ entry.zIndex = readZIndex(el);
+ entry.sourceId = paintMetadata.sourceId;
+ }
elements.push(entry);
};
@@ -422,14 +520,13 @@ export function extractSlideDataFromDocument(doc = document) {
return null;
};
- // Collect validation errors
- const errors = [];
-
const bgResolved = resolveSlideBackground(body);
if (bgResolved.gradient) {
- errors.push(
- 'CSS gradients are not supported. Use Sharp to rasterize gradients as PNG images first, ' +
- 'then reference with background-image: url(\'gradient.png\')',
+ addDiagnostic(
+ 'fallback',
+ 'css_gradient',
+ 'CSS gradient requires fallback rendering; native gradient mapping is not available.',
+ bgResolved.element || body,
);
}
@@ -440,6 +537,7 @@ export function extractSlideDataFromDocument(doc = document) {
// Process all elements
const elements = [];
+ const slideDataFallbackLayers = [];
const placeholders = [];
const textTags = ['P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'UL', 'OL', 'LI'];
const genericInlineTextTags = new Set([
@@ -459,7 +557,7 @@ export function extractSlideDataFromDocument(doc = document) {
};
const hasDirectText = (el) => Array.from(el.childNodes).some(
- (node) => node.nodeType === Node.TEXT_NODE && node.textContent.replace(/\s+/g, ' ').trim(),
+ (node) => node.nodeType === view.Node.TEXT_NODE && node.textContent.replace(/\s+/g, ' ').trim(),
);
const isGenericTextElement = (el) => {
@@ -519,12 +617,13 @@ export function extractSlideDataFromDocument(doc = document) {
const text = el.textContent.replace(/\s+/g, ' ').trim();
if (rect.width === 0 || rect.height === 0 || !text) return false;
- if (type !== 'text' && el.tagName !== 'LI' && /^[•\-\*▪▸○●◆◇■□]\s/.test(text.trimStart())) {
- errors.push(
- `Text element <${el.tagName.toLowerCase()}> starts with bullet symbol "${text.substring(0, 20)}...". ` +
- 'Use or lists instead of manual bullet symbols.'
+ if (type !== 'text' && el.tagName !== 'LI' && /^[•●○▪‣·▸◆◇■□]\s/u.test(text.trimStart())) {
+ addDiagnostic(
+ 'fallback',
+ 'manual_bullet_unrepaired',
+ `Text element <${el.tagName.toLowerCase()}> still starts with a manual bullet; exporting as editable text.`,
+ el,
);
- return false;
}
const computed = view.getComputedStyle(el);
@@ -629,9 +728,495 @@ export function extractSlideDataFromDocument(doc = document) {
return true;
};
+ const svgColor = (value, fallback = null) => {
+ if (!value || value === 'none' || value === 'transparent') return fallback;
+ return rgbToHex(value);
+ };
+ const svgNumber = (value, fallback = 0) => {
+ const parsed = parseFloat(String(value || ''));
+ return Number.isFinite(parsed) ? parsed : fallback;
+ };
+ const identityMatrix = () => [1, 0, 0, 1, 0, 0];
+ const multiplyMatrix = (left, right) => [
+ left[0] * right[0] + left[2] * right[1],
+ left[1] * right[0] + left[3] * right[1],
+ left[0] * right[2] + left[2] * right[3],
+ left[1] * right[2] + left[3] * right[3],
+ left[0] * right[4] + left[2] * right[5] + left[4],
+ left[1] * right[4] + left[3] * right[5] + left[5],
+ ];
+ const parseSvgTransform = (value = '') => {
+ const raw = String(value || '').trim();
+ let matrix = identityMatrix();
+ let layoutMatrix = identityMatrix();
+ let rotation = 0;
+ if (!raw || raw === 'none') {
+ return { matrix, layoutMatrix, rotation, reliable: true };
+ }
+ if (/(?:skew|perspective|matrix3d)\s*\(/i.test(raw)) {
+ return { matrix, layoutMatrix, rotation, reliable: false };
+ }
+ const functions = raw.matchAll(/(matrix|translate|scale|rotate)\s*\(([^)]*)\)/gi);
+ let matched = false;
+ for (const match of functions) {
+ matched = true;
+ const name = match[1].toLowerCase();
+ const values = match[2].trim().split(/[\s,]+/).filter(Boolean).map((item) => parseFloat(item));
+ let next = identityMatrix();
+ let nextLayout = identityMatrix();
+ if (name === 'matrix' && values.length >= 6) {
+ if (Math.abs(values[1]) > 1e-6 || Math.abs(values[2]) > 1e-6) {
+ return { matrix, layoutMatrix, rotation, reliable: false };
+ }
+ next = values.slice(0, 6);
+ nextLayout = next;
+ } else if (name === 'translate') {
+ next = [1, 0, 0, 1, values[0] || 0, values[1] || 0];
+ nextLayout = next;
+ } else if (name === 'scale') {
+ const x = Number.isFinite(values[0]) ? values[0] : 1;
+ const y = Number.isFinite(values[1]) ? values[1] : x;
+ next = [x, 0, 0, y, 0, 0];
+ nextLayout = next;
+ } else if (name === 'rotate') {
+ const angle = values[0] || 0;
+ rotation += angle;
+ const radians = angle * Math.PI / 180;
+ const cos = Math.cos(radians);
+ const sin = Math.sin(radians);
+ const rotationMatrix = [cos, sin, -sin, cos, 0, 0];
+ if (Number.isFinite(values[1]) && Number.isFinite(values[2])) {
+ next = multiplyMatrix(
+ multiplyMatrix([1, 0, 0, 1, values[1], values[2]], rotationMatrix),
+ [1, 0, 0, 1, -values[1], -values[2]],
+ );
+ } else {
+ next = rotationMatrix;
+ }
+ }
+ matrix = multiplyMatrix(matrix, next);
+ layoutMatrix = multiplyMatrix(layoutMatrix, nextLayout);
+ }
+ return { matrix, layoutMatrix, rotation, reliable: matched };
+ };
+ const transformForSvgNode = (node, svg) => {
+ if (typeof node.getCTM === 'function') {
+ try {
+ const ctm = node.getCTM();
+ const matrix = ctm
+ ? [ctm.a, ctm.b, ctm.c, ctm.d, ctm.e, ctm.f].map(Number)
+ : null;
+ if (matrix?.every(Number.isFinite)) {
+ const scaleX = Math.hypot(matrix[0], matrix[1]);
+ const scaleY = Math.hypot(matrix[2], matrix[3]);
+ const orthogonality = scaleX > 0 && scaleY > 0
+ ? Math.abs((matrix[0] * matrix[2] + matrix[1] * matrix[3]) / (scaleX * scaleY))
+ : Infinity;
+ if (orthogonality > 1e-5 || matrix[0] * matrix[3] - matrix[1] * matrix[2] <= 0) {
+ return {
+ matrix,
+ layoutMatrix: identityMatrix(),
+ rotation: 0,
+ reliable: false,
+ coordinateSpace: 'viewport',
+ };
+ }
+ return {
+ matrix,
+ layoutMatrix: [scaleX, 0, 0, scaleY, matrix[4], matrix[5]],
+ rotation: Math.atan2(matrix[1], matrix[0]) * 180 / Math.PI,
+ reliable: true,
+ coordinateSpace: 'viewport',
+ };
+ }
+ } catch {
+ // Fall through to deterministic attribute/computed-style parsing.
+ }
+ }
+ const chain = [];
+ let current = node;
+ while (current && current !== svg) {
+ chain.unshift(current);
+ current = current.parentElement;
+ }
+ let matrix = identityMatrix();
+ let layoutMatrix = identityMatrix();
+ let rotation = 0;
+ for (const item of chain) {
+ const attributeTransform = parseSvgTransform(item.getAttribute('transform'));
+ if (!attributeTransform.reliable) {
+ return { matrix, layoutMatrix, rotation, reliable: false };
+ }
+ matrix = multiplyMatrix(matrix, attributeTransform.matrix);
+ layoutMatrix = multiplyMatrix(layoutMatrix, attributeTransform.layoutMatrix);
+ rotation += attributeTransform.rotation;
+
+ const computedTransformValue = view.getComputedStyle(item).transform;
+ if (computedTransformValue && computedTransformValue !== 'none'
+ && computedTransformValue !== item.getAttribute('transform')) {
+ const computedTransform = parseSvgTransform(computedTransformValue);
+ if (!computedTransform.reliable) {
+ return { matrix, layoutMatrix, rotation, reliable: false };
+ }
+ let computedMatrix = computedTransform.matrix;
+ const originValues = String(view.getComputedStyle(item).transformOrigin || '')
+ .split(/\s+/).map((value) => parseFloat(value));
+ if (Number.isFinite(originValues[0]) && Number.isFinite(originValues[1])
+ && computedTransform.rotation) {
+ computedMatrix = multiplyMatrix(
+ multiplyMatrix(
+ [1, 0, 0, 1, originValues[0], originValues[1]],
+ computedMatrix,
+ ),
+ [1, 0, 0, 1, -originValues[0], -originValues[1]],
+ );
+ }
+ matrix = multiplyMatrix(matrix, computedMatrix);
+ layoutMatrix = multiplyMatrix(layoutMatrix, computedTransform.layoutMatrix);
+ rotation += computedTransform.rotation;
+ }
+ }
+ return {
+ matrix,
+ layoutMatrix,
+ rotation,
+ reliable: true,
+ coordinateSpace: 'viewBox',
+ };
+ };
+ const transformPoint = (point, matrix) => ({
+ x: matrix[0] * point.x + matrix[2] * point.y + matrix[4],
+ y: matrix[1] * point.x + matrix[3] * point.y + matrix[5],
+ });
+ const parseSvgPoints = (value) => {
+ const numbers = String(value || '').trim().split(/[\s,]+/).filter(Boolean).map(Number);
+ const points = [];
+ for (let index = 0; index + 1 < numbers.length; index += 2) {
+ if (Number.isFinite(numbers[index]) && Number.isFinite(numbers[index + 1])) {
+ points.push({ x: numbers[index], y: numbers[index + 1] });
+ }
+ }
+ return points;
+ };
+ const svgStyleProperties = [
+ 'fill', 'fill-opacity', 'fill-rule',
+ 'stroke', 'stroke-width', 'stroke-opacity', 'stroke-linecap', 'stroke-linejoin',
+ 'stroke-dasharray', 'stroke-dashoffset',
+ 'opacity', 'color', 'vector-effect',
+ ];
+ const resolvedSvgStyle = (element, property) => {
+ let current = element;
+ while (current) {
+ const value = String(
+ view.getComputedStyle(current).getPropertyValue(property) || '',
+ ).trim();
+ if (value && !['inherit', 'unset', 'initial'].includes(value)) return value;
+ if (['opacity', 'vector-effect'].includes(property)) break;
+ current = current.parentElement;
+ }
+ return '';
+ };
+ const inlineComputedSvgStyles = (original, clone) => {
+ svgStyleProperties.forEach((property) => {
+ const value = resolvedSvgStyle(original, property);
+ if (value) clone.style.setProperty(property, value);
+ });
+ const originalChildren = [...original.children];
+ const cloneChildren = [...clone.children];
+ originalChildren.forEach((child, index) => {
+ if (cloneChildren[index]) inlineComputedSvgStyles(child, cloneChildren[index]);
+ });
+ };
+ const serializeSvgVisual = (svg, node) => {
+ const rootClone = svg.cloneNode(false);
+ rootClone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
+ if (!rootClone.getAttribute('width')) rootClone.setAttribute('width', '100%');
+ if (!rootClone.getAttribute('height')) rootClone.setAttribute('height', '100%');
+ inlineComputedSvgStyles(svg, rootClone);
+
+ [...svg.querySelectorAll('defs')].forEach((defs) => {
+ const defsClone = defs.cloneNode(true);
+ inlineComputedSvgStyles(defs, defsClone);
+ rootClone.appendChild(defsClone);
+ });
+
+ const ancestors = [];
+ let current = node.parentElement;
+ while (current && current !== svg) {
+ ancestors.unshift(current);
+ current = current.parentElement;
+ }
+ let targetParent = rootClone;
+ ancestors.forEach((ancestor) => {
+ const ancestorClone = ancestor.cloneNode(false);
+ inlineComputedSvgStyles(ancestor, ancestorClone);
+ targetParent.appendChild(ancestorClone);
+ targetParent = ancestorClone;
+ });
+ const targetClone = node.cloneNode(true);
+ inlineComputedSvgStyles(node, targetClone);
+ targetClone.querySelectorAll?.('text').forEach((text) => text.remove());
+ targetParent.appendChild(targetClone);
+ return `data:image/svg+xml,${encodeURIComponent(rootClone.outerHTML)}`;
+ };
+ const boundsOfPoints = (points) => {
+ const xs = points.map((point) => point.x);
+ const ys = points.map((point) => point.y);
+ return {
+ left: Math.min(...xs),
+ top: Math.min(...ys),
+ width: Math.max(...xs) - Math.min(...xs),
+ height: Math.max(...ys) - Math.min(...ys),
+ };
+ };
+ const isDiamondPoints = (points) => {
+ if (points.length !== 4) return false;
+ const bounds = boundsOfPoints(points);
+ const cx = bounds.left + bounds.width / 2;
+ const cy = bounds.top + bounds.height / 2;
+ const tolerance = Math.max(bounds.width, bounds.height) * 0.08 + 0.01;
+ return points.every((point) => (
+ Math.abs(point.x - cx) <= tolerance || Math.abs(point.y - cy) <= tolerance
+ ));
+ };
+ const emitNativeSvg = (svg) => {
+ const svgRect = rectFor(svg);
+ if (svgRect.width <= 0 || svgRect.height <= 0) return;
+ const viewBox = String(svg.getAttribute('viewBox') || `0 0 ${svgRect.width} ${svgRect.height}`)
+ .trim().split(/[\s,]+/).map(Number);
+ const [vbX = 0, vbY = 0, vbW = svgRect.width, vbH = svgRect.height] = viewBox;
+ const xScale = svgRect.width / (vbW || svgRect.width);
+ const yScale = svgRect.height / (vbH || svgRect.height);
+ const fallbackLayers = slideDataFallbackLayers;
+ const toSlidePoint = (point) => ({
+ x: svgRect.left + ((point.x - vbX) * xScale),
+ y: svgRect.top + ((point.y - vbY) * yScale),
+ });
+ const pushLine = (start, end, node, stroke, width, coordinateSpace = 'viewBox') => {
+ const toLinePoint = (point) => (
+ coordinateSpace === 'viewport'
+ ? { x: svgRect.left + point.x, y: svgRect.top + point.y }
+ : toSlidePoint(point)
+ );
+ const first = toLinePoint(start);
+ const second = toLinePoint(end);
+ pushElement({
+ type: 'line',
+ kind: 'native',
+ x1: pxToInch(first.x),
+ y1: pxToInch(first.y),
+ x2: pxToInch(second.x),
+ y2: pxToInch(second.y),
+ color: stroke || '000000',
+ width,
+ }, node);
+ };
+ const hasBrowserOnlySvg = Boolean(
+ svg.querySelector('filter,mask,foreignObject,use,pattern,textPath,clipPath,image'),
+ );
+ const canSerializeSvgVisual = (node) => (
+ !svg.querySelector('script')
+ && !/(?:href|src)\s*=\s*["']\s*(?:https?:)?\/\//i.test(node.outerHTML || '')
+ );
+ const pushSvgImageLayer = (node, code, message) => {
+ if (!canSerializeSvgVisual(node)) {
+ addDiagnostic(
+ 'fallback',
+ 'complex_svg_raster',
+ 'Complex SVG cannot be safely serialized and requires local browser raster rendering.',
+ svg,
+ );
+ return;
+ }
+ const paintMetadata = nextPaintMetadata(node);
+ fallbackLayers.push({
+ sourceId: paintMetadata.sourceId,
+ zIndex: readZIndex(node),
+ paintOrder: paintMetadata.paintOrder,
+ subOrder: paintMetadata.subOrder,
+ kind: 'svg-image',
+ captureStrategy: 'local-svg',
+ bbox: {
+ x: pxToInch(svgRect.left),
+ y: pxToInch(svgRect.top),
+ w: pxToInch(svgRect.width),
+ h: pxToInch(svgRect.height),
+ },
+ data: serializeSvgVisual(svg, node),
+ diagnostics: [{
+ severity: 'fallback',
+ code,
+ message,
+ sourceId: node.dataset?.pptxSourceId || node.id || null,
+ }],
+ });
+ };
+ svg.querySelectorAll('rect,circle,ellipse,line,polyline,polygon,text,path').forEach((node) => {
+ const tag = node.tagName.toLowerCase();
+ if (tag === 'path') {
+ if (!hasBrowserOnlySvg) {
+ pushSvgImageLayer(
+ node,
+ 'complex_svg_vector',
+ 'Complex SVG geometry is preserved as a local movable vector image.',
+ );
+ }
+ return;
+ }
+ const transform = transformForSvgNode(node, svg);
+ if (!transform.reliable) {
+ pushSvgImageLayer(
+ node,
+ 'svg_transform_vector',
+ 'SVG transform cannot be represented reliably as a native shape; preserving it as SVG.',
+ );
+ return;
+ }
+ const transformedToSlidePoint = (transformed) => (
+ transform.coordinateSpace === 'viewport'
+ ? { x: svgRect.left + transformed.x, y: svgRect.top + transformed.y }
+ : toSlidePoint(transformed)
+ );
+ const transformToSlidePoint = (point, matrix) => (
+ transformedToSlidePoint(transformPoint(point, matrix))
+ );
+ const mapPoint = (x, y) => transformToSlidePoint(
+ { x: svgNumber(x), y: svgNumber(y) },
+ transform.matrix,
+ );
+ const mapLayoutPoint = (x, y) => transformToSlidePoint(
+ { x: svgNumber(x), y: svgNumber(y) },
+ transform.layoutMatrix,
+ );
+ const fill = svgColor(node.getAttribute('fill') || view.getComputedStyle(node).fill);
+ const stroke = svgColor(node.getAttribute('stroke') || view.getComputedStyle(node).stroke);
+ const opacity = svgNumber(node.getAttribute('opacity') || view.getComputedStyle(node).opacity, 1);
+ const lineWidth = svgNumber(node.getAttribute('stroke-width'), 1) * 0.75;
+ const common = {
+ type: tag === 'text' ? 'svg-text' : 'svg-shape',
+ kind: 'native',
+ svgType: tag,
+ position: null,
+ shape: { fill, line: stroke ? { color: stroke, width: lineWidth } : null, transparency: Math.round((1 - opacity) * 100), rectRadius: 0 },
+ };
+ if (tag === 'rect') {
+ const x = svgNumber(node.getAttribute('x'));
+ const y = svgNumber(node.getAttribute('y'));
+ const width = svgNumber(node.getAttribute('width'));
+ const height = svgNumber(node.getAttribute('height'));
+ const points = [
+ mapPoint(x, y), mapPoint(x + width, y), mapPoint(x + width, y + height), mapPoint(x, y + height),
+ ];
+ const bounds = boundsOfPoints(points);
+ const layoutBounds = boundsOfPoints([
+ mapLayoutPoint(x, y),
+ mapLayoutPoint(x + width, y),
+ mapLayoutPoint(x + width, y + height),
+ mapLayoutPoint(x, y + height),
+ ]);
+ common.position = { x: pxToInch(layoutBounds.left), y: pxToInch(layoutBounds.top), w: pxToInch(layoutBounds.width), h: pxToInch(layoutBounds.height) };
+ common.bbox = { x: pxToInch(bounds.left), y: pxToInch(bounds.top), w: pxToInch(bounds.width), h: pxToInch(bounds.height) };
+ if (transform.rotation) common.shape.rotate = transform.rotation;
+ } else if (tag === 'circle' || tag === 'ellipse') {
+ const rx = tag === 'circle' ? svgNumber(node.getAttribute('r')) : svgNumber(node.getAttribute('rx'));
+ const ry = tag === 'circle' ? rx : svgNumber(node.getAttribute('ry'));
+ const cx = svgNumber(node.getAttribute('cx'));
+ const cy = svgNumber(node.getAttribute('cy'));
+ const points = [
+ mapPoint(cx - rx, cy), mapPoint(cx + rx, cy), mapPoint(cx, cy - ry), mapPoint(cx, cy + ry),
+ ];
+ const bounds = boundsOfPoints(points);
+ const layoutBounds = boundsOfPoints([
+ mapLayoutPoint(cx - rx, cy), mapLayoutPoint(cx + rx, cy),
+ mapLayoutPoint(cx, cy - ry), mapLayoutPoint(cx, cy + ry),
+ ]);
+ common.position = { x: pxToInch(layoutBounds.left), y: pxToInch(layoutBounds.top), w: pxToInch(layoutBounds.width), h: pxToInch(layoutBounds.height) };
+ common.bbox = { x: pxToInch(bounds.left), y: pxToInch(bounds.top), w: pxToInch(bounds.width), h: pxToInch(bounds.height) };
+ if (transform.rotation) common.shape.rotate = transform.rotation;
+ } else if (tag === 'line') {
+ pushLine(
+ transformPoint({ x: svgNumber(node.getAttribute('x1')), y: svgNumber(node.getAttribute('y1')) }, transform.matrix),
+ transformPoint({ x: svgNumber(node.getAttribute('x2')), y: svgNumber(node.getAttribute('y2')) }, transform.matrix),
+ node,
+ stroke,
+ lineWidth,
+ transform.coordinateSpace,
+ );
+ return;
+ } else if (tag === 'text') {
+ common.text = node.textContent || '';
+ const fontSize = svgNumber(node.getAttribute('font-size'), 16);
+ const origin = mapPoint(node.getAttribute('x'), svgNumber(node.getAttribute('y')) - fontSize);
+ common.position = { x: pxToInch(origin.x), y: pxToInch(origin.y), w: pxToInch(Math.max(1, svgRect.width)), h: pxToInch(Math.max(1, fontSize * yScale * 1.3)) };
+ common.style = { fontSize: svgNumber(node.getAttribute('font-size'), 16) * 0.75, fontFace: 'Arial', color: fill || '000000', align: 'left' };
+ if (transform.rotation) common.style.rotate = transform.rotation;
+ } else {
+ const points = parseSvgPoints(node.getAttribute('points'))
+ .map((point) => transformPoint(point, transform.matrix));
+ if (tag === 'polygon' && (points.length === 3 || isDiamondPoints(points))) {
+ const slidePoints = points.map(transformedToSlidePoint);
+ const bounds = boundsOfPoints(slidePoints);
+ common.svgType = points.length === 3 ? 'triangle' : 'diamond';
+ common.position = {
+ x: pxToInch(bounds.left),
+ y: pxToInch(bounds.top),
+ w: pxToInch(bounds.width),
+ h: pxToInch(bounds.height),
+ };
+ common.bbox = { ...common.position };
+ if (transform.rotation) common.shape.rotate = transform.rotation;
+ } else {
+ const closed = tag === 'polygon' && points.length > 2 ? [...points, points[0]] : points;
+ for (let index = 0; index + 1 < closed.length; index += 1) {
+ pushLine(
+ closed[index],
+ closed[index + 1],
+ node,
+ stroke || fill,
+ lineWidth,
+ transform.coordinateSpace,
+ );
+ }
+ if (tag === 'polygon' && fill && points.length > 2) {
+ const sourceId = node.dataset?.pptxSourceId || node.id || null;
+ fallbackLayers.push({
+ sourceId,
+ zIndex: readZIndex(node),
+ paintOrder: domPaintOrder.get(sourceId) ?? unmappedPaintOrder++,
+ subOrder: -1,
+ kind: 'svg-image',
+ bbox: {
+ x: pxToInch(svgRect.left),
+ y: pxToInch(svgRect.top),
+ w: pxToInch(svgRect.width),
+ h: pxToInch(svgRect.height),
+ },
+ data: serializeSvgVisual(svg, node),
+ diagnostics: [{
+ severity: 'fallback',
+ code: 'svg_polygon_fill',
+ message: 'Polygon fill is preserved as a local SVG layer over an editable outline.',
+ sourceId: node.dataset?.pptxSourceId || node.id || null,
+ }],
+ });
+ }
+ return;
+ }
+ }
+ if (common.position || common.type === 'line') pushElement(common, node);
+ });
+ processed.add(svg);
+ svg.querySelectorAll('*').forEach((node) => processed.add(node));
+ };
+
document.querySelectorAll('*').forEach((el) => {
if (processed.has(el)) return;
+ if (el.tagName === 'svg') {
+ emitNativeSvg(el);
+ return;
+ }
+
// [data-pptx-merge="true"] — opt-in: merge all /
- descendants
// into ONE PowerPoint text frame (single editable text box).
// Each child paragraph becomes a run with breakLine:true at the end;
@@ -646,9 +1231,11 @@ export function extractSlideDataFromDocument(doc = document) {
// Reject nested merge containers — undefined behavior.
if (el.querySelector('[data-pptx-merge="true"]')) {
- errors.push(
- `data-pptx-merge container cannot contain another data-pptx-merge container. ` +
- 'Nested merge is not supported.'
+ addDiagnostic(
+ 'fallback',
+ 'nested_merge_container',
+ 'Nested data-pptx-merge containers require fallback handling.',
+ el,
);
processed.add(el);
return;
@@ -658,11 +1245,12 @@ export function extractSlideDataFromDocument(doc = document) {
// Container background image — same restriction as regular divs.
if (mergeComputed.backgroundImage && mergeComputed.backgroundImage !== 'none') {
- errors.push(
- 'Background images on data-pptx-merge container are not supported. ' +
- 'Use solid colors or borders, or layer images via slide.addImage().'
+ addDiagnostic(
+ 'fallback',
+ 'merge_background_image',
+ 'Background image on data-pptx-merge requires fallback rendering.',
+ el,
);
- return;
}
// Emit a shape for the container's bg/uniform-border (mirrors the regular div branch).
@@ -677,7 +1265,7 @@ export function extractSlideDataFromDocument(doc = document) {
const mHasUniformBorder = mHasBorder && mBorders.every(b => b === mBorders[0]);
if (mHasBg || mHasUniformBorder) {
- elements.push({
+ pushElement({
type: 'shape',
text: '',
position: {
@@ -707,15 +1295,17 @@ export function extractSlideDataFromDocument(doc = document) {
})(),
shadow: parseBoxShadow(mergeComputed.boxShadow)
}
- });
+ }, el);
}
// Collect
/ descendants in document order.
const textDescendants = Array.from(el.querySelectorAll('p, h1, h2, h3, h4, h5, h6'));
if (textDescendants.length === 0) {
- errors.push(
- `data-pptx-merge container has no / children to merge. ` +
- 'Remove the data-pptx-merge attribute or add text elements.'
+ addDiagnostic(
+ 'fallback',
+ 'empty_merge_container',
+ 'data-pptx-merge container has no semantic text children.',
+ el,
);
processed.add(el);
return;
@@ -794,7 +1384,7 @@ export function extractSlideDataFromDocument(doc = document) {
return;
}
- elements.push({
+ pushElement({
type: 'merged-text',
items: mergedRuns,
position: {
@@ -804,7 +1394,7 @@ export function extractSlideDataFromDocument(doc = document) {
h: pxToInch(containerRect.height)
},
style: baseStyle
- });
+ }, el);
processed.add(el);
return;
@@ -881,8 +1471,11 @@ export function extractSlideDataFromDocument(doc = document) {
if (el.classList && el.classList.contains('placeholder')) {
const rect = rectFor(el);
if (rect.width === 0 || rect.height === 0) {
- errors.push(
- `Placeholder "${el.id || 'unnamed'}" has ${rect.width === 0 ? 'width: 0' : 'height: 0'}. Check the layout CSS.`
+ addDiagnostic(
+ 'fallback',
+ 'unmeasurable_placeholder',
+ `Placeholder "${el.id || 'unnamed'}" has ${rect.width === 0 ? 'width: 0' : 'height: 0'}.`,
+ el,
);
} else {
placeholders.push({
@@ -901,7 +1494,7 @@ export function extractSlideDataFromDocument(doc = document) {
if (el.tagName === 'IMG') {
const rect = rectFor(el);
if (rect.width > 0 && rect.height > 0) {
- elements.push({
+ pushElement({
type: 'image',
src: el.src,
position: {
@@ -910,7 +1503,52 @@ export function extractSlideDataFromDocument(doc = document) {
w: pxToInch(rect.width),
h: pxToInch(rect.height)
}
- });
+ }, el);
+ processed.add(el);
+ return;
+ }
+ }
+
+ // Common CSS arrow: a zero-sized box with one opaque border and
+ // transparent side borders maps cleanly to an editable PPT triangle.
+ if (el.tagName === 'DIV') {
+ const computed = view.getComputedStyle(el);
+ const isZeroBox = svgNumber(computed.width) === 0 && svgNumber(computed.height) === 0;
+ const sides = ['Top', 'Right', 'Bottom', 'Left'].map((side) => ({
+ side,
+ width: svgNumber(computed[`border${side}Width`]),
+ color: computed[`border${side}Color`],
+ }));
+ const opaqueSides = sides.filter((side) => (
+ side.width > 0 && !isTransparentBg(side.color)
+ ));
+ if (isZeroBox && opaqueSides.length === 1 && sides.filter((side) => side.width > 0).length >= 3) {
+ const active = opaqueSides[0];
+ const rect = rectFor(el);
+ const horizontal = sides.find((side) => side.side === 'Left').width
+ + sides.find((side) => side.side === 'Right').width;
+ const vertical = sides.find((side) => side.side === 'Top').width
+ + sides.find((side) => side.side === 'Bottom').width;
+ const rotations = { Bottom: 0, Left: 90, Top: 180, Right: 270 };
+ pushElement({
+ type: 'svg-shape',
+ svgType: 'triangle',
+ kind: 'native',
+ text: '',
+ position: {
+ x: pxToInch(rect.left - sides.find((side) => side.side === 'Left').width),
+ y: pxToInch(rect.top - sides.find((side) => side.side === 'Top').width),
+ w: pxToInch(Math.max(1, horizontal)),
+ h: pxToInch(Math.max(1, vertical)),
+ },
+ shape: {
+ fill: rgbToHex(active.color),
+ line: null,
+ transparency: extractAlpha(active.color),
+ rectRadius: 0,
+ rotate: rotations[active.side],
+ },
+ }, el);
processed.add(el);
return;
}
@@ -925,11 +1563,12 @@ export function extractSlideDataFromDocument(doc = document) {
// Check for background images on shapes
const bgImage = computed.backgroundImage;
if (bgImage && bgImage !== 'none') {
- errors.push(
- 'Background images on DIV elements are not supported. ' +
- 'Use solid colors or borders for shapes, or use slide.addImage() in PptxGenJS to layer images.'
+ addDiagnostic(
+ 'fallback',
+ 'container_background_image',
+ 'Container background image requires fallback rendering.',
+ el,
);
- return;
}
// Check for borders - both uniform and partial
@@ -1154,18 +1793,60 @@ export function extractSlideDataFromDocument(doc = document) {
emitTextElement(el);
});
- const paintRank = (type) => {
- if (type === 'shape') return 0;
- if (type === 'line') return 1;
- if (type === 'image') return 2;
- return 3;
- };
elements.sort((a, b) => {
const z = (a.zIndex ?? 0) - (b.zIndex ?? 0);
if (z !== 0) return z;
- return paintRank(a.type) - paintRank(b.type);
+ const paint = (a.paintOrder ?? 0) - (b.paintOrder ?? 0);
+ if (paint !== 0) return paint;
+ const sub = (a.subOrder ?? 0) - (b.subOrder ?? 0);
+ if (sub !== 0) return sub;
+ return (a.stableOrder ?? 0) - (b.stableOrder ?? 0);
});
- return { background, elements, placeholders, errors };
+ document.querySelectorAll('*').forEach((element) => {
+ const computed = view.getComputedStyle(element);
+ const filter = String(computed.filter || element.style?.filter || '');
+ if (filter && filter !== 'none') {
+ addDiagnostic('fallback', 'css_filter', 'CSS filter requires fallback rendering.', element);
+ }
+ if (String(element.tagName).toUpperCase() === 'SVG') {
+ if (element.querySelector('filter,mask,foreignObject,use,pattern,textPath,clipPath,image')) {
+ addDiagnostic(
+ 'fallback',
+ 'complex_svg_raster',
+ 'SVG filter, mask, or foreignObject requires local browser raster rendering.',
+ element,
+ );
+ } else if (element.querySelector('path')) {
+ addDiagnostic(
+ 'fallback',
+ 'complex_svg_vector',
+ 'Complex SVG geometry is preserved as a local SVG image.',
+ element,
+ );
+ }
+ }
+ });
+
+ const blockingErrors = diagnostics
+ .filter((diagnostic) => diagnostic.severity === 'blocking')
+ .map((diagnostic) => diagnostic.message);
+ slideDataFallbackLayers.sort((a, b) => {
+ const z = (a.zIndex ?? 0) - (b.zIndex ?? 0);
+ if (z !== 0) return z;
+ const order = (a.paintOrder ?? 0) - (b.paintOrder ?? 0);
+ if (order !== 0) return order;
+ const sub = (a.subOrder ?? 0) - (b.subOrder ?? 0);
+ if (sub !== 0) return sub;
+ return (a.stableOrder ?? 0) - (b.stableOrder ?? 0);
+ });
+ return {
+ background,
+ elements,
+ fallbackLayers: slideDataFallbackLayers,
+ placeholders,
+ diagnostics,
+ errors: blockingErrors,
+ };
}
diff --git a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/i18n.js b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/i18n.js
index 5054aeea7c..9037409775 100644
--- a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/i18n.js
+++ b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/i18n.js
@@ -258,6 +258,20 @@ export const STRINGS = {
exportPptxWorking: 'Rendering editable PPTX...',
exportPptxDone: 'Editable PPTX downloaded.',
exportPptxFailed: 'PPTX export failed:',
+ exportDiagnosticsSummary: 'Exported with visual adjustments: {{counts}}. {{locations}}',
+ exportDiagnosticsRepaired: '{{count}} automatic repairs',
+ exportDiagnosticsSvg: '{{count}} local SVG layers',
+ exportDiagnosticsLocalPng: '{{count}} local PNG layers',
+ exportDiagnosticsPageVisual: '{{count}} page visual layers',
+ exportDiagnosticsFullPage: '{{count}} full-page fallbacks',
+ exportDiagnosticsBlocking: '{{count}} blocking failures',
+ exportDiagnosticsLocation: 'Slide {{slide}}, element {{source}} ({{phase}}): {{reason}}',
+ exportDiagnosticsPhaseRepair: 'automatic repair',
+ exportDiagnosticsPhaseSvg: 'local SVG',
+ exportDiagnosticsPhaseLocalPng: 'local PNG',
+ exportDiagnosticsPhasePageVisual: 'page visual fallback',
+ exportDiagnosticsPhaseFullPage: 'full-page fallback',
+ exportDiagnosticsPhaseBlocking: 'blocking failure',
exportPdfWorking: 'Rendering PDF...',
exportPdfDone: 'PDF downloaded.',
exportPdfFailed: 'PDF export failed:',
@@ -634,6 +648,20 @@ export const STRINGS = {
exportPptxWorking: '正在渲染可编辑 PPTX...',
exportPptxDone: '可编辑 PPTX 已下载。',
exportPptxFailed: 'PPTX 导出失败:',
+ exportDiagnosticsSummary: '已完成导出,并进行了视觉调整:{{counts}}。{{locations}}',
+ exportDiagnosticsRepaired: '自动修复 {{count}} 处',
+ exportDiagnosticsSvg: '局部 SVG 层 {{count}} 个',
+ exportDiagnosticsLocalPng: '局部 PNG 层 {{count}} 个',
+ exportDiagnosticsPageVisual: '页面视觉层 {{count}} 个',
+ exportDiagnosticsFullPage: '整页兜底 {{count}} 页',
+ exportDiagnosticsBlocking: '阻断失败 {{count}} 处',
+ exportDiagnosticsLocation: '第 {{slide}} 页,元素 {{source}}({{phase}}):{{reason}}',
+ exportDiagnosticsPhaseRepair: '自动修复',
+ exportDiagnosticsPhaseSvg: '局部 SVG',
+ exportDiagnosticsPhaseLocalPng: '局部 PNG',
+ exportDiagnosticsPhasePageVisual: '页面视觉兜底',
+ exportDiagnosticsPhaseFullPage: '整页兜底',
+ exportDiagnosticsPhaseBlocking: '阻断失败',
exportPdfWorking: '正在渲染 PDF...',
exportPdfDone: 'PDF 已下载。',
exportPdfFailed: 'PDF 导出失败:',
diff --git a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/paint-order.js b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/paint-order.js
new file mode 100644
index 0000000000..040a5a361b
--- /dev/null
+++ b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/paint-order.js
@@ -0,0 +1,9 @@
+export function buildDomPaintOrderMap(doc) {
+ const map = new Map();
+ if (!doc?.body) return map;
+ [doc.body, ...doc.body.querySelectorAll('*')].forEach((element, index) => {
+ const sourceId = element.dataset?.pptxSourceId || element.id || null;
+ if (sourceId && !map.has(sourceId)) map.set(sourceId, index);
+ });
+ return map;
+}
diff --git a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/pptx-html-build.js b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/pptx-html-build.js
index d171d9013e..6f55d7b20f 100644
--- a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/pptx-html-build.js
+++ b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/pptx-html-build.js
@@ -19,6 +19,9 @@ const PX_PER_IN = 96;
const EMU_PER_IN = 914400;
const SLIDE_W_IN = 13.333;
const SLIDE_H_IN = 7.5;
+const EDITABLE_TEXT_TYPES = new Set([
+ 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'text', 'svg-text', 'list', 'merged-text',
+]);
// PowerPoint and browsers render the same font at the same point size with
// measurably different glyph widths (different font metric tables / hinting).
@@ -129,7 +132,68 @@ async function addBackground(slideData, targetSlide) {
}
function addElements(slideData, targetSlide, pres) {
- for (const el of slideData.elements) {
+ if (slideData.fullPageFallback) {
+ const payload = toImagePayload(
+ slideData.fullPageFallback.data || slideData.fullPageFallback.src,
+ );
+ if (!payload) throw new Error('Full-page fallback has no image payload');
+ targetSlide.addImage({
+ ...payload,
+ x: 0,
+ y: 0,
+ w: SLIDE_W_IN,
+ h: SLIDE_H_IN,
+ });
+ return;
+ }
+ const suppressedNativeVisualIds = new Set([
+ ...(slideData.suppressedNativeVisualIds || []),
+ ...(slideData.fallbackLayers || [])
+ .flatMap((layer) => layer.suppressedNativeVisualIds || []),
+ ]);
+ const paintItems = [
+ ...(slideData.elements || []).map((element, order) => ({
+ type: 'element',
+ item: element,
+ zIndex: element.zIndex ?? 0,
+ order: element.paintOrder ?? order,
+ subOrder: element.subOrder ?? 0,
+ stableOrder: order,
+ })),
+ ...(slideData.fallbackLayers || []).map((layer, order) => ({
+ type: 'fallback',
+ item: layer,
+ zIndex: layer.zIndex ?? 0,
+ order: layer.paintOrder ?? ((slideData.elements || []).length + order),
+ subOrder: layer.subOrder ?? 0,
+ stableOrder: (slideData.elements || []).length + order,
+ })),
+ ].sort((a, b) => (
+ a.zIndex - b.zIndex
+ || a.order - b.order
+ || a.subOrder - b.subOrder
+ || a.stableOrder - b.stableOrder
+ ));
+ for (const paintItem of paintItems) {
+ if (paintItem.type === 'fallback') {
+ const layer = paintItem.item;
+ const payload = toImagePayload(layer.data || layer.src);
+ const bbox = layer.bbox || {};
+ if (!payload) continue;
+ const fullPageCanvas = layer.canvas === 'full-page';
+ targetSlide.addImage({
+ ...payload,
+ x: fullPageCanvas ? 0 : (bbox.x ?? 0),
+ y: fullPageCanvas ? 0 : (bbox.y ?? 0),
+ w: fullPageCanvas ? SLIDE_W_IN : (bbox.w ?? SLIDE_W_IN),
+ h: fullPageCanvas ? SLIDE_H_IN : (bbox.h ?? SLIDE_H_IN),
+ });
+ continue;
+ }
+ const el = paintItem.item;
+ if (suppressedNativeVisualIds.has(el.sourceId) && !EDITABLE_TEXT_TYPES.has(el.type)) {
+ continue;
+ }
if (el.type === 'image') {
const payload = toImagePayload(el.src);
if (!payload) continue;
@@ -148,14 +212,22 @@ function addElements(slideData, targetSlide, pres) {
h: el.y2 - el.y1,
line: { color: el.color, width: el.width },
});
- } else if (el.type === 'shape') {
+ } else if (el.type === 'shape' || el.type === 'svg-shape') {
const shapeOptions = {
x: el.position.x,
y: el.position.y,
w: el.position.w,
h: el.position.h,
- shape: el.shape.rectRadius > 0 ? pres.ShapeType.roundRect : pres.ShapeType.rect,
};
+ const nativeShapeType = {
+ circle: pres.ShapeType.ellipse,
+ ellipse: pres.ShapeType.ellipse,
+ triangle: pres.ShapeType.triangle,
+ diamond: pres.ShapeType.diamond,
+ rect: pres.ShapeType.rect,
+ }[el.svgType];
+ shapeOptions.shape = nativeShapeType
+ || (el.shape.rectRadius > 0 ? pres.ShapeType.roundRect : pres.ShapeType.rect);
if (el.shape.fill) {
shapeOptions.fill = { color: el.shape.fill };
if (el.shape.transparency != null) shapeOptions.fill.transparency = el.shape.transparency;
@@ -163,7 +235,12 @@ function addElements(slideData, targetSlide, pres) {
if (el.shape.line) shapeOptions.line = el.shape.line;
if (el.shape.rectRadius > 0) shapeOptions.rectRadius = el.shape.rectRadius;
if (el.shape.shadow) shapeOptions.shadow = el.shape.shadow;
- targetSlide.addText(el.text || '', shapeOptions);
+ if (el.shape.rotate != null) shapeOptions.rotate = el.shape.rotate;
+ if (el.type === 'svg-shape') {
+ targetSlide.addShape(shapeOptions.shape, shapeOptions);
+ } else {
+ targetSlide.addText(el.text || '', shapeOptions);
+ }
} else if (el.type === 'list' || el.type === 'merged-text') {
const { x: boxX, w: boxW } = safeTextBoxGeometry(el.position.x, el.position.w, el.style.align, false);
const listOptions = {
@@ -233,10 +310,38 @@ export async function buildSlideFromExtracted(slideData, bodyDimensions, pres, o
if (validationWarnings.length) {
console.warn('[ppt-live-export] slide validation warnings (export continues):', validationWarnings.join('; '));
}
+ const diagnostics = [
+ ...(slideData?.diagnostics || []),
+ ...validationWarnings.map((message) => ({
+ severity: 'fallback',
+ code: 'pptx_layout_warning',
+ message,
+ sourceId: null,
+ tag: null,
+ })),
+ ];
const targetSlide = options.slide || pres.addSlide();
- await addBackground(slideData, targetSlide);
- addElements(slideData, targetSlide, pres);
- return { slide: targetSlide, placeholders: slideData.placeholders || [] };
+ try {
+ await addBackground(slideData, targetSlide);
+ addElements(slideData, targetSlide, pres);
+ } catch (error) {
+ const diagnostic = {
+ severity: 'blocking',
+ kind: 'blocking',
+ code: 'pptx_serialization',
+ message: String(error?.message || error || 'PPTX serialization failed.'),
+ sourceId: null,
+ tag: null,
+ };
+ error.diagnostic = diagnostic;
+ error.diagnostics = [...diagnostics, diagnostic];
+ throw error;
+ }
+ return {
+ slide: targetSlide,
+ placeholders: slideData.placeholders || [],
+ diagnostics,
+ };
}
export function createPptxDeck(deck = {}) {
diff --git a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/render.js b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/render.js
index 942a40f9d6..d58244f936 100644
--- a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/render.js
+++ b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/render.js
@@ -2,6 +2,13 @@ import { escapeHtml, extractHtmlSlideBackground, getActiveIndex, getActiveSlide,
import { translate as t, getLocale } from './i18n.js';
import { refreshFlatSelect } from './flat-select.js';
import { DEFAULT_STYLE_PRESET } from './style-presets.js';
+import {
+ elementModelElementHtml,
+ resolveElementColor,
+} from './element-model-html.js';
+import { sanitizeSlideMarkup } from './sanitize-slide-markup.js';
+
+export { resolveElementColor as resolveColor };
export function applyI18n() {
document.documentElement.lang = getLocale();
@@ -174,21 +181,23 @@ const HTML_SLIDE_PREVIEW_HOST_CLASS = 'html-slide-preview-host';
const HTML_SLIDE_PREVIEW_SCALER_CLASS = 'html-slide-preview-scaler';
const DEFAULT_SLIDE_DESIGN = { width: 1280, height: 720 };
-function writeSandboxIframeDocument(frame, html) {
+function writeSandboxIframeDocument(frame, sanitizedHtml) {
const doc = frame.contentDocument;
if (!doc) return false;
doc.open();
- doc.write(normalizeSlideDocument(html));
+ doc.write(sanitizedHtml);
doc.close();
return true;
}
function mountSandboxIframeHtml(frame, html, onMounted) {
+ const sanitizedHtml = sanitizeSlideMarkup(normalizeSlideDocument(html));
frame.setAttribute('sandbox', 'allow-same-origin');
+ frame.srcdoc = sanitizedHtml;
frame.src = 'about:blank';
const mount = () => {
- if (!writeSandboxIframeDocument(frame, html)) return false;
+ if (!writeSandboxIframeDocument(frame, sanitizedHtml)) return false;
onMounted?.();
return true;
};
@@ -368,22 +377,6 @@ export function fitHtmlSlidePreviewSurface(host) {
const SLIDE_SHADOW_ROOT_CLASS = 'ppt-slide-shadow-root';
const SLIDE_SHADOW_BODY_CLASS = 'ppt-slide-shadow-body';
-/** Strip active content (scripts, inline handlers, javascript: URLs) from a parsed slide document. */
-function sanitizeParsedSlideDocument(parsed) {
- parsed.querySelectorAll('script, iframe, object, embed, meta[http-equiv="refresh" i]').forEach((node) => node.remove());
- parsed.querySelectorAll('*').forEach((node) => {
- for (const attr of [...node.attributes]) {
- const name = attr.name.toLowerCase();
- if (name.startsWith('on')) {
- node.removeAttribute(attr.name);
- } else if ((name === 'href' || name === 'src' || name === 'xlink:href') && /^\s*javascript:/i.test(attr.value)) {
- node.removeAttribute(attr.name);
- }
- }
- });
- return parsed;
-}
-
/**
* Build the in-document editable slide stage. The PPT Live app document
* itself lives in a sandboxed host iframe without `allow-same-origin`
@@ -401,9 +394,8 @@ function createEditableSlideStage(html, frameClass) {
const designW = Number(stage.dataset.designW);
const designH = Number(stage.dataset.designH);
- const parsed = sanitizeParsedSlideDocument(
- new DOMParser().parseFromString(normalizeSlideDocument(html), 'text/html'),
- );
+ const sanitizedMarkup = sanitizeSlideMarkup(normalizeSlideDocument(html));
+ const parsed = new DOMParser().parseFromString(sanitizedMarkup, 'text/html');
const shadow = stage.attachShadow({ mode: 'open' });
const rootEl = document.createElement('div');
@@ -459,7 +451,7 @@ function createEditableSlideStage(html, frameClass) {
rootEl.appendChild(bodyEl);
shadow.appendChild(rootEl);
- stage._pptLiveSourceHtml = String(html || '');
+ stage._pptLiveSourceHtml = sanitizedMarkup;
return stage;
}
@@ -1271,7 +1263,7 @@ export function hydrateHtmlSlideIframes(root = document) {
export function slideHtml(slide, options = {}) {
if (slide?.html) {
const mountId = `slide-${++pendingSlideHtmlMountSeq}`;
- pendingSlideHtmlMounts.set(mountId, normalizeSlideDocument(slide.html));
+ pendingSlideHtmlMounts.set(mountId, sanitizeSlideMarkup(normalizeSlideDocument(slide.html)));
return ``;
}
const editable = Boolean(options.editable);
@@ -1288,7 +1280,12 @@ export function slideHtml(slide, options = {}) {
${slide.kicker ? `${escapeHtml(slide.kicker)}
` : ''}
${slide.proofObject ? `${escapeHtml(slide.proofObject)}
` : ''}
${slideQualityBadge(slide)}
- ${(slide.elements || []).map((element) => elementHtml(element, slide.theme, editable, selectedId)).join('')}
+ ${(slide.elements || []).map((element) => elementModelElementHtml(element, slide.theme, {
+ mode: 'editor',
+ editable,
+ selectedId,
+ mediaPlaceholder: t('mediaPlaceholder'),
+ })).join('')}
${slide.sourceNote ? `${escapeHtml(slide.sourceNote)}
` : ''}
`;
}
@@ -1398,68 +1395,6 @@ export function normalizeSlideDocument(html) {
return `${source}`;
}
-function elementHtml(element, theme, editable, selectedId) {
- const selected = editable && selectedId === element.id;
- const style = [
- `left:${element.x}%`,
- `top:${element.y}%`,
- `width:${element.w}%`,
- `height:${element.h}%`,
- `font-size:${fontSizeCss(element.style.fontSize)}`,
- `font-weight:${element.style.fontWeight}`,
- `color:${resolveColor(element.style.color, theme)}`,
- `text-align:${element.style.align || 'left'}`,
- `background:${resolveColor(element.style.background, theme)}`,
- `opacity:${element.style.opacity}`,
- `border-radius:${element.style.borderRadius}px`,
- ].join(';');
- let content = '';
- if (element.type === 'list') {
- content = `${(element.items || []).map((item, index) => editable
- ? `- ${escapeHtml(item)}
`
- : `- ${escapeHtml(item)}
`).join('')}
`;
- } else if (element.type === 'metric') {
- content = `${escapeHtml(element.text)}${escapeHtml(element.label)}`;
- } else if (element.type === 'chart') {
- const max = Math.max(1, ...(element.data || []).map((point) => Number(point.value) || 0));
- content = `${escapeHtml(element.text)}${(element.data || []).map((point) => `${escapeHtml(point.label)}`).join('')}
`;
- } else if (element.type === 'media') {
- content = `${escapeHtml(element.text || t('mediaPlaceholder'))}`;
- } else {
- content = editable
- ? `${escapeHtml(element.text || '')}`
- : escapeHtml(element.text || '');
- }
- return `${content}${selected ? '' : ''}
`;
-}
-
-export function resolveColor(value, theme) {
- if (!value || value === 'transparent') return 'transparent';
- if (value === 'ink') return theme.ink;
- if (value === 'muted') return theme.muted;
- if (value === 'primary') return theme.primary;
- if (value === 'accent') return theme.accent;
- if (value === 'panel') return theme.panel || '#ffffff';
- if (value === 'soft') return colorMix(theme.primary, 0.1);
- if (value === 'background') return theme.background;
- return value;
-}
-
-function colorMix(hex, alpha) {
- const raw = String(hex || '#0f766e').replace('#', '');
- const int = parseInt(raw.length === 3 ? raw.split('').map((x) => x + x).join('') : raw, 16);
- const r = (int >> 16) & 255;
- const g = (int >> 8) & 255;
- const b = int & 255;
- return `rgba(${r}, ${g}, ${b}, ${alpha})`;
-}
-
-function fontSizeCss(value) {
- const size = Math.max(8, Number(value) || 24);
- const cqw = Math.round((size / 10.2) * 1000) / 1000;
- return `clamp(8px, ${cqw}cqw, ${size}px)`;
-}
-
function byId(id) {
return document.getElementById(id);
}
diff --git a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/sanitize-slide-html.js b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/sanitize-slide-html.js
index 04f634b854..c67e743b42 100644
--- a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/sanitize-slide-html.js
+++ b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/sanitize-slide-html.js
@@ -1,10 +1,104 @@
export function sanitizeSlideDocumentRoot(doc = document, aggressive = false) {
const document = doc;
const view = document.defaultView || window;
+ const diagnostics = [];
+ const seenDiagnostics = new Set();
const skipTags = new Set(['SCRIPT', 'STYLE', 'PRE', 'CODE', 'SVG', 'TEXTAREA']);
const inlineSelector = 'strong,b,em,i,u,span,a,small,mark,sub,sup,code';
const textSelector = 'p,h1,h2,h3,h4,h5,h6,li';
+ const textContainerSelector = 'p,h1,h2,h3,h4,h5,h6,li';
+ const manualBulletPattern = /^(\s*)([•●○▪‣·▸◆◇■□–—*-])\s+/u;
+ const ambiguousBulletSymbols = new Set(['-', '*', '–', '—']);
+
+ function sourceIdOf(element) {
+ return element?.dataset?.pptxSourceId || element?.id || null;
+ }
+
+ function assignSourceIds() {
+ const body = document.body;
+ if (!body) return;
+ const elements = [body, ...body.querySelectorAll('*')];
+ const reserved = new Set(
+ elements.map((element) => element.dataset.pptxSourceId?.trim()).filter(Boolean),
+ );
+ const used = new Set();
+ elements.forEach((element) => {
+ const sourceId = element.dataset.pptxSourceId?.trim();
+ if (!sourceId) return;
+ if (!used.has(sourceId)) {
+ element.dataset.pptxSourceId = sourceId;
+ used.add(sourceId);
+ return;
+ }
+ let suffix = 2;
+ let candidate = `${sourceId}-${suffix}`;
+ while (used.has(candidate) || reserved.has(candidate)) {
+ suffix += 1;
+ candidate = `${sourceId}-${suffix}`;
+ }
+ element.dataset.pptxSourceId = candidate;
+ used.add(candidate);
+ reserved.add(candidate);
+ addDiagnostic(
+ 'repaired',
+ 'duplicate_source_id_repaired',
+ `Duplicate source element id "${sourceId}" was reassigned to "${candidate}".`,
+ element,
+ );
+ });
+ let sequence = 1;
+ elements.forEach((element) => {
+ if (element.dataset?.pptxSourceId) return;
+ while (used.has(`pptx-source-${sequence}`) || reserved.has(`pptx-source-${sequence}`)) {
+ sequence += 1;
+ }
+ const sourceId = `pptx-source-${sequence}`;
+ sequence += 1;
+ element.dataset.pptxSourceId = sourceId;
+ used.add(sourceId);
+ });
+ }
+
+ function derivedSourceId(element, suffix) {
+ const base = sourceIdOf(element) || 'pptx-source';
+ let candidate = `${base}-${suffix}`;
+ let sequence = 2;
+ while (document.querySelector(`[data-pptx-source-id="${candidate}"]`)) {
+ candidate = `${base}-${suffix}-${sequence}`;
+ sequence += 1;
+ }
+ return candidate;
+ }
+
+ function addDiagnostic(severity, code, message, element = null) {
+ const sourceId = sourceIdOf(element);
+ const key = `${severity}:${code}:${sourceId || ''}`;
+ if (seenDiagnostics.has(key)) return;
+ seenDiagnostics.add(key);
+ const diagnostic = {
+ severity,
+ kind: severity === 'blocking' ? 'blocking' : undefined,
+ code,
+ message,
+ sourceId,
+ tag: element?.tagName?.toLowerCase?.() || null,
+ };
+ try {
+ const rect = element?.getBoundingClientRect?.();
+ if (rect && rect.width > 0 && rect.height > 0) {
+ diagnostic.bbox = {
+ x: rect.left,
+ y: rect.top,
+ width: rect.width,
+ height: rect.height,
+ };
+ }
+ } catch {
+ // Diagnostics remain useful without optional geometry.
+ }
+ diagnostics.push(diagnostic);
+ }
function inferBlockTag(node) {
const cls = String(node.className || '').toLowerCase();
@@ -43,19 +137,89 @@ export function sanitizeSlideDocumentRoot(doc = document, aggressive = false) {
}
}
+ function repairNestedParagraphs(root) {
+ root.querySelectorAll(textContainerSelector).forEach((outer) => {
+ const nested = [...outer.children].filter((child) => child.matches(textContainerSelector));
+ if (!nested.length || !outer.parentNode) return;
+ const fragments = [];
+ let current = document.createElement(outer.tagName.toLowerCase());
+ const copyOuterAttributes = (target) => {
+ [...outer.attributes].forEach((attribute) => {
+ if (attribute.name !== 'data-pptx-source-id') target.setAttribute(attribute.name, attribute.value);
+ });
+ };
+ copyOuterAttributes(current);
+ current.dataset.pptxSourceId = sourceIdOf(outer);
+ [...outer.childNodes].forEach((node) => {
+ if (node.nodeType === view.Node.ELEMENT_NODE && node.matches(textContainerSelector)) {
+ if (current.textContent.trim() || current.children.length) fragments.push(current);
+ fragments.push(node);
+ current = document.createElement(outer.tagName.toLowerCase());
+ copyOuterAttributes(current);
+ current.dataset.pptxSourceId = derivedSourceId(outer, `split-${fragments.length + 1}`);
+ } else {
+ current.appendChild(node);
+ }
+ });
+ if (current.textContent.trim() || current.children.length) fragments.push(current);
+ outer.replaceWith(...fragments);
+ addDiagnostic(
+ 'repaired',
+ 'nested_paragraph_repaired',
+ 'Nested paragraph structure was split into ordered sibling paragraphs.',
+ fragments[0] || nested[0],
+ );
+ });
+ }
+
function wrapDirectTextNodes(root) {
root.querySelectorAll('div').forEach((div) => {
if (skipTags.has(div.tagName)) return;
- [...div.childNodes].forEach((node) => {
- if (node.nodeType !== Node.TEXT_NODE) return;
- const text = node.textContent.replace(/\s+/g, ' ').trim();
- if (!text) {
- node.remove();
- return;
+ let sequence = 1;
+ let nodes = [...div.childNodes];
+ while (nodes.length) {
+ const firstDirectText = nodes.findIndex(
+ (node) => node.nodeType === view.Node.TEXT_NODE && node.textContent.trim(),
+ );
+ if (firstDirectText < 0) break;
+ let start = firstDirectText;
+ while (start > 0) {
+ const previous = nodes[start - 1];
+ if (previous.nodeType === view.Node.ELEMENT_NODE
+ && !previous.matches(inlineSelector)
+ && previous.tagName !== 'BR') break;
+ start -= 1;
+ }
+ let end = firstDirectText;
+ while (end + 1 < nodes.length) {
+ const next = nodes[end + 1];
+ if (next.nodeType === view.Node.ELEMENT_NODE
+ && !next.matches(inlineSelector)
+ && next.tagName !== 'BR') break;
+ end += 1;
}
+ const group = nodes.slice(start, end + 1);
const block = document.createElement(inferBlockTag(div));
- block.textContent = text;
- div.replaceChild(block, node);
+ block.dataset.pptxSourceId = derivedSourceId(div, `text-${sequence}`);
+ sequence += 1;
+ group[0].before(block);
+ group.forEach((node) => {
+ if (node.nodeType === view.Node.TEXT_NODE) {
+ node.textContent = node.textContent.replace(/\s+/g, ' ');
+ }
+ block.appendChild(node);
+ });
+ block.normalize();
+ addDiagnostic(
+ 'repaired',
+ 'direct_text_wrapped',
+ 'Direct container text was wrapped in a semantic text block.',
+ block,
+ );
+ nodes = [...div.childNodes];
+ }
+ [...div.childNodes].forEach((node) => {
+ if (node.nodeType === view.Node.TEXT_NODE && !node.textContent.trim()) node.remove();
});
});
}
@@ -66,11 +230,96 @@ export function sanitizeSlideDocumentRoot(doc = document, aggressive = false) {
const hasBg = computed.backgroundColor && computed.backgroundColor !== 'rgba(0, 0, 0, 0)';
const hasBorder = hasVisibleBorder(computed);
if (!hasBg && !hasBorder) return;
+ if (span.closest(textContainerSelector)) return;
const block = document.createElement('p');
if (span.className) block.className = span.className;
if (span.getAttribute('style')) block.setAttribute('style', span.getAttribute('style'));
- block.textContent = span.textContent;
+ block.dataset.pptxSourceId = sourceIdOf(span);
+ while (span.firstChild) block.appendChild(span.firstChild);
span.replaceWith(block);
+ addDiagnostic(
+ 'repaired',
+ 'decorated_inline_promoted',
+ 'Decorated inline text was promoted to a block that can be exported as shape plus text.',
+ block,
+ );
+ });
+ }
+
+ function removeManualBullet(element) {
+ const symbol = element.textContent.match(manualBulletPattern)?.[2];
+ if (!symbol) return;
+ const walker = document.createTreeWalker(element, view.NodeFilter.SHOW_TEXT);
+ let textNode = walker.nextNode();
+ let removed = false;
+ while (textNode) {
+ if (!removed) {
+ const symbolIndex = textNode.textContent.search(/\S/u);
+ if (symbolIndex >= 0) {
+ if (textNode.textContent.slice(symbolIndex).startsWith(symbol)) {
+ textNode.textContent = textNode.textContent.slice(symbolIndex + symbol.length).replace(/^\s+/u, '');
+ removed = true;
+ if (textNode.textContent) return;
+ } else {
+ return;
+ }
+ }
+ } else if (textNode.textContent) {
+ textNode.textContent = textNode.textContent.replace(/^\s+/u, '');
+ return;
+ }
+ textNode = walker.nextNode();
+ }
+ }
+
+ function normalizeManualBulletBlocks(root) {
+ root.querySelectorAll('body, div, section, article, aside, main, td, th').forEach((parent) => {
+ let group = [];
+ const flush = () => {
+ if (!group.length) return;
+ const first = group[0];
+ const firstSymbol = first.textContent.match(manualBulletPattern)?.[2];
+ if (group.length === 1 && ambiguousBulletSymbols.has(firstSymbol)) {
+ group = [];
+ return;
+ }
+ const list = document.createElement('ul');
+ list.dataset.pptxSourceId = derivedSourceId(first, 'list');
+ const firstComputed = view.getComputedStyle(first);
+ const authoredIndent = parseFloat(firstComputed.marginLeft || first.style.marginLeft || '0') || 0;
+ list.style.margin = '0';
+ list.style.paddingLeft = `${Math.max(24, authoredIndent + 24)}px`;
+ first.parentNode.insertBefore(list, first);
+ group.forEach((block) => {
+ const item = document.createElement('li');
+ [...block.attributes].forEach((attribute) => {
+ if (!['id', 'data-pptx-source-id'].includes(attribute.name)) {
+ item.setAttribute(attribute.name, attribute.value);
+ }
+ });
+ item.dataset.pptxSourceId = sourceIdOf(block);
+ while (block.firstChild) item.appendChild(block.firstChild);
+ removeManualBullet(item);
+ list.appendChild(item);
+ block.remove();
+ });
+ addDiagnostic(
+ 'repaired',
+ 'manual_bullet_list',
+ `${group.length} consecutive manual bullet paragraph(s) were converted to a semantic list.`,
+ list,
+ );
+ group = [];
+ };
+ [...parent.children].forEach((child) => {
+ const isTextBlock = /^(P|H[1-6])$/.test(child.tagName);
+ if (isTextBlock && manualBulletPattern.test(child.textContent || '')) {
+ group.push(child);
+ } else {
+ flush();
+ }
+ });
+ flush();
});
}
@@ -93,6 +342,75 @@ export function sanitizeSlideDocumentRoot(doc = document, aggressive = false) {
});
}
+ function collectFallbackDiagnostics(root) {
+ root.querySelectorAll('*').forEach((element) => {
+ const computed = view.getComputedStyle(element);
+ const backgroundImage = String(computed.backgroundImage || element.style?.backgroundImage || '');
+ if (backgroundImage.includes('gradient')) {
+ addDiagnostic(
+ 'fallback',
+ 'css_gradient',
+ 'CSS gradient requires fallback rendering; native gradient mapping is not available.',
+ element,
+ );
+ }
+ const filter = String(computed.filter || element.style?.filter || '');
+ if (filter && filter !== 'none') {
+ addDiagnostic(
+ 'fallback',
+ 'css_filter',
+ 'CSS filter requires fallback rendering.',
+ element,
+ );
+ }
+ });
+ root.querySelectorAll('svg').forEach((svg) => {
+ if (svg.querySelector('filter,mask,foreignObject,use,pattern,textPath,clipPath,image')) {
+ addDiagnostic(
+ 'fallback',
+ 'complex_svg_raster',
+ 'SVG filter, mask, or foreignObject requires local browser raster rendering.',
+ svg,
+ );
+ } else if (svg.querySelector('path')) {
+ addDiagnostic(
+ 'fallback',
+ 'complex_svg_vector',
+ 'Complex SVG geometry will be preserved as a local SVG image.',
+ svg,
+ );
+ }
+ });
+ root.querySelectorAll('style').forEach((style) => {
+ let rules = [];
+ try {
+ rules = [...(style.sheet?.cssRules || [])];
+ } catch {
+ return;
+ }
+ rules.forEach((rule) => {
+ const selector = rule.selectorText || '';
+ const content = rule.style?.content;
+ if (!/::(before|after)/.test(selector)
+ || !content
+ || ['none', 'normal', '""', "''"].includes(content)) return;
+ const baseSelector = selector.replace(/::(before|after)/g, '').trim();
+ let matches = [];
+ try {
+ matches = [...root.querySelectorAll(baseSelector)];
+ } catch {
+ return;
+ }
+ matches.forEach((element) => addDiagnostic(
+ 'fallback',
+ 'generated_content',
+ 'Pseudo-element generated content requires fallback rendering.',
+ element,
+ ));
+ });
+ });
+ }
+
function hasVisibleBorder(computed) {
return ['Top', 'Right', 'Bottom', 'Left'].some((side) => parseFloat(computed[`border${side}Width`] || 0) > 0);
}
@@ -273,14 +591,18 @@ export function sanitizeSlideDocumentRoot(doc = document, aggressive = false) {
(root.head || root.documentElement).appendChild(style);
}
+ assignSourceIds();
+ collectFallbackDiagnostics(document);
ensureExportCanvas();
- wrapDirectTextNodes(document);
+ repairNestedParagraphs(document);
promoteDecoratedSpans(document);
+ wrapDirectTextNodes(document);
+ normalizeManualBulletBlocks(document);
normalizeInlineLists(document);
- flattenGradients(document);
- stripUnsupportedDivBackgrounds(document);
if (aggressive) {
+ flattenGradients(document);
+ stripUnsupportedDivBackgrounds(document);
hoistTextDecorations(document);
resetInlineBoxModel(document);
enforceInlineElementsSafe(document);
@@ -297,4 +619,5 @@ export function sanitizeSlideDocumentRoot(doc = document, aggressive = false) {
// Preserve author layout/CSS; snapshot computed styles for a stable second paint.
inlineSnapshotLayoutStyles(document);
}
+ return { diagnostics };
}
diff --git a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/sanitize-slide-markup.js b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/sanitize-slide-markup.js
new file mode 100644
index 0000000000..5b6a19c023
--- /dev/null
+++ b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/src/sanitize-slide-markup.js
@@ -0,0 +1,260 @@
+const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
+const DELETE_WITH_CONTENT_TAGS = new Set([
+ 'script', 'iframe', 'object', 'embed', 'base', 'meta', 'link', 'template',
+ 'frame', 'frameset', 'portal',
+]);
+
+export const HTML_ALLOWED_TAGS = new Set([
+ 'html', 'head', 'body', 'title', 'style',
+ 'div', 'section', 'article', 'main', 'header', 'footer', 'nav', 'aside',
+ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'span', 'strong', 'b', 'em', 'i',
+ 'u', 's', 'small', 'mark', 'sub', 'sup', 'code', 'pre', 'blockquote',
+ 'ul', 'ol', 'li', 'dl', 'dt', 'dd', 'br', 'hr', 'a',
+ 'table', 'caption', 'colgroup', 'col', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td',
+ 'figure', 'figcaption', 'img', 'label', 'time',
+]);
+
+export const SVG_ALLOWED_TAGS = new Set([
+ 'svg', 'g', 'defs', 'desc', 'title',
+ 'rect', 'circle', 'ellipse', 'line', 'polyline', 'polygon', 'path',
+ 'text', 'tspan', 'textpath', 'use', 'foreignobject', 'symbol', 'marker',
+ 'lineargradient', 'radialgradient', 'stop', 'pattern', 'clippath', 'mask', 'filter',
+ 'feblend', 'fecolormatrix', 'fecomponenttransfer', 'fecomposite', 'feconvolvematrix',
+ 'fediffuselighting', 'fedisplacementmap', 'fedistantlight', 'fedropshadow',
+ 'feflood', 'fefunca', 'fefuncb', 'fefuncg', 'fefuncr', 'fegaussianblur',
+ 'feimage', 'femerge', 'femergenode', 'femorphology', 'feoffset',
+ 'fepointlight', 'fespecularlighting', 'fespotlight', 'fetile', 'feturbulence',
+]);
+
+export const GLOBAL_ALLOWED_ATTRIBUTES = new Set([
+ 'id', 'class', 'style', 'title', 'role', 'lang', 'dir', 'hidden',
+ 'tabindex', 'draggable', 'spellcheck', 'contenteditable',
+]);
+
+export const TAG_ALLOWED_ATTRIBUTES = Object.freeze({
+ img: new Set(['src', 'alt', 'width', 'height', 'loading', 'decoding']),
+ ol: new Set(['start', 'reversed', 'type']),
+ li: new Set(['value']),
+ col: new Set(['span']),
+ th: new Set(['colspan', 'rowspan', 'scope', 'headers', 'abbr']),
+ td: new Set(['colspan', 'rowspan', 'headers']),
+ label: new Set(['for']),
+ time: new Set(['datetime']),
+});
+
+export const SVG_GLOBAL_ALLOWED_ATTRIBUTES = new Set([
+ 'id', 'class', 'style', 'transform', 'fill', 'fill-opacity', 'fill-rule',
+ 'stroke', 'stroke-width', 'stroke-opacity', 'stroke-linecap', 'stroke-linejoin',
+ 'stroke-dasharray', 'stroke-dashoffset', 'opacity', 'filter', 'clip-path',
+ 'clip-rule', 'mask', 'color', 'color-interpolation', 'color-interpolation-filters',
+ 'visibility', 'display', 'font-family', 'font-size', 'font-style', 'font-weight',
+ 'text-anchor', 'dominant-baseline', 'pointer-events', 'vector-effect',
+ 'paint-order', 'shape-rendering', 'text-rendering', 'stop-color', 'stop-opacity',
+ 'flood-color', 'flood-opacity', 'lighting-color',
+ 'marker-start', 'marker-mid', 'marker-end',
+]);
+const SVG_RESOURCE_PRESENTATION_ATTRIBUTES = new Set([
+ 'fill', 'stroke', 'filter', 'clip-path', 'mask',
+ 'marker-start', 'marker-mid', 'marker-end',
+]);
+
+const SVG_GEOMETRY_ATTRIBUTES = new Set([
+ 'x', 'y', 'x1', 'y1', 'x2', 'y2', 'cx', 'cy', 'r', 'rx', 'ry',
+ 'width', 'height', 'd', 'points', 'pathlength',
+]);
+const SVG_FILTER_ATTRIBUTES = new Set([
+ 'in', 'in2', 'result', 'operator', 'k1', 'k2', 'k3', 'k4', 'mode', 'type',
+ 'values', 'tablevalues', 'slope', 'intercept', 'amplitude', 'exponent', 'offset',
+ 'stddeviation', 'edgemode', 'kernelmatrix', 'kernelunitlength', 'targetx', 'targety',
+ 'order', 'preservealpha', 'surfacescale', 'diffuseconstant', 'specularconstant',
+ 'specularexponent', 'limitingconeangle', 'azimuth', 'elevation',
+ 'pointsatx', 'pointsaty', 'pointsatz', 'basefrequency', 'numoctaves', 'seed',
+ 'stitchtiles', 'scale', 'xchannelselector', 'ychannelselector', 'radius', 'dx', 'dy',
+]);
+export const SVG_TAG_ALLOWED_ATTRIBUTES = Object.freeze({
+ svg: new Set(['viewbox', 'width', 'height', 'x', 'y', 'preserveaspectratio', 'xmlns', 'xmlns:xlink']),
+ symbol: new Set(['viewbox', 'x', 'y', 'width', 'height', 'preserveaspectratio']),
+ marker: new Set(['viewbox', 'markerwidth', 'markerheight', 'markerunits', 'orient', 'preserveaspectratio', 'refx', 'refy']),
+ rect: new Set([...SVG_GEOMETRY_ATTRIBUTES]),
+ circle: new Set([...SVG_GEOMETRY_ATTRIBUTES]),
+ ellipse: new Set([...SVG_GEOMETRY_ATTRIBUTES]),
+ line: new Set([...SVG_GEOMETRY_ATTRIBUTES]),
+ polyline: new Set([...SVG_GEOMETRY_ATTRIBUTES]),
+ polygon: new Set([...SVG_GEOMETRY_ATTRIBUTES]),
+ path: new Set([...SVG_GEOMETRY_ATTRIBUTES]),
+ text: new Set(['x', 'y', 'dx', 'dy', 'rotate', 'textlength', 'lengthadjust']),
+ tspan: new Set(['x', 'y', 'dx', 'dy', 'rotate', 'textlength', 'lengthadjust']),
+ textpath: new Set(['href', 'xlink:href', 'startoffset', 'method', 'spacing', 'side', 'textlength', 'lengthadjust']),
+ use: new Set(['x', 'y', 'width', 'height', 'href', 'xlink:href']),
+ foreignobject: new Set(['x', 'y', 'width', 'height']),
+ lineargradient: new Set(['x1', 'y1', 'x2', 'y2', 'gradientunits', 'gradienttransform', 'spreadmethod', 'href', 'xlink:href']),
+ radialgradient: new Set(['cx', 'cy', 'r', 'fx', 'fy', 'fr', 'gradientunits', 'gradienttransform', 'spreadmethod', 'href', 'xlink:href']),
+ stop: new Set(['offset']),
+ pattern: new Set(['x', 'y', 'width', 'height', 'patternunits', 'patterncontentunits', 'patterntransform', 'viewbox', 'preserveaspectratio', 'href', 'xlink:href']),
+ clippath: new Set(['clippathunits']),
+ mask: new Set(['x', 'y', 'width', 'height', 'maskunits', 'maskcontentunits']),
+ filter: new Set(['x', 'y', 'width', 'height', 'filterunits', 'primitiveunits']),
+});
+
+function canonicalizeCssEscapes(value) {
+ let current = String(value || '');
+ for (let pass = 0; pass < 4; pass += 1) {
+ const decoded = current
+ .replace(/\\([0-9a-f]{1,6})(?:\r\n|[ \n\r\t\f])?/gi, (_match, hex) => (
+ String.fromCodePoint(Number.parseInt(hex, 16) || 0)
+ ))
+ .replace(/\\([^\n\r\f0-9a-f])/gi, '$1');
+ if (decoded === current) break;
+ current = decoded;
+ }
+ return current;
+}
+
+function hasUnsafeCss(value) {
+ // Agent-authored CSS is untrusted. Decode CSS escapes first, then remove the
+ // complete declaration container instead of attempting a partial CSS parser.
+ const css = canonicalizeCssEscapes(value).toLowerCase();
+ return /url\s*\(|@import\b|@font-face\b|expression\s*\(|behavior\s*:|-moz-binding\b|(?:-webkit-)?image-set\s*\(|(?:image|cross-fade|element|src)\s*\(/.test(css);
+}
+
+function isSafeRasterDataImage(value) {
+ const candidate = String(value || '').trim();
+ const match = candidate.match(/^data:image\/(png|jpeg|gif|webp)((?:;[a-z0-9._-]+=[a-z0-9._-]+)*)(;base64)?,([\s\S]*)$/i);
+ if (!match) return false;
+ const payload = match[4];
+ if (!payload) return false;
+ return match[3]
+ ? /^[a-z0-9+/=\s]+$/i.test(payload)
+ : !/[<>"'`]/.test(payload) && /^(?:%[0-9a-f]{2}|[a-z0-9!$&()*+,\-./:;=?@_~\s])+$/i.test(payload);
+}
+
+function isAllowedResource(node, name, value) {
+ const localName = String(node.localName || '').toLowerCase();
+ const isSvg = node.namespaceURI === SVG_NAMESPACE || node.closest?.('svg');
+ if ((name === 'href' || name === 'xlink:href') && isSvg) {
+ return /^#[a-z_][\w:.-]*$/i.test(canonicalizeCssEscapes(value).trim());
+ }
+ if (name === 'src') return localName === 'img' && isSafeRasterDataImage(value);
+ return false;
+}
+
+function normalizedLocalPaintServer(value) {
+ const canonical = canonicalizeCssEscapes(value).trim();
+ const match = canonical.match(/^url\(\s*(#[a-z_][\w:.-]*)\s*\)$/i);
+ return match ? `url(${match[1]})` : null;
+}
+
+function normalizedSvgResourceAttribute(name, value) {
+ const canonical = canonicalizeCssEscapes(value).trim();
+ const lower = canonical.toLowerCase();
+ if (name === 'href' || name === 'xlink:href') {
+ return /^#[a-z_][\w:.-]*$/i.test(canonical) ? canonical : null;
+ }
+ if (!SVG_RESOURCE_PRESENTATION_ATTRIBUTES.has(name)) return canonical;
+ if (/\burl\s*\(/i.test(canonical)) return normalizedLocalPaintServer(canonical);
+ if (name === 'fill' || name === 'stroke') {
+ if (/(?:javascript|vbscript|file|data|blob|https?):|(?:^|[\s"'(])\/\//i.test(lower)) return null;
+ if (/(?:-webkit-)?image-set\s*\(|(?:image|cross-fade|element|src)\s*\(/i.test(lower)) return null;
+ return canonical || null;
+ }
+ return /^(?:none|inherit|initial|unset)$/i.test(canonical) ? canonical : null;
+}
+
+function hasSafeCustomAttributeValue(value) {
+ const canonical = canonicalizeCssEscapes(value).trim().toLowerCase();
+ return !/(?:javascript|vbscript|file|data|blob|https?):|(?:^|[\s"'(])\/\//.test(canonical)
+ && !hasUnsafeCss(canonical);
+}
+
+function isSvgElement(node) {
+ return node.namespaceURI === SVG_NAMESPACE;
+}
+
+function allowedAttributeName(node, name) {
+ const tag = String(node.localName || '').toLowerCase();
+ if (name.startsWith('data-') || name.startsWith('aria-')) return true;
+ if (isSvgElement(node)) {
+ if (GLOBAL_ALLOWED_ATTRIBUTES.has(name) || SVG_GLOBAL_ALLOWED_ATTRIBUTES.has(name)) return true;
+ if (SVG_TAG_ALLOWED_ATTRIBUTES[tag]?.has(name)) return true;
+ return tag.startsWith('fe') && (
+ SVG_GEOMETRY_ATTRIBUTES.has(name) || SVG_FILTER_ATTRIBUTES.has(name)
+ );
+ }
+ return GLOBAL_ALLOWED_ATTRIBUTES.has(name) || Boolean(TAG_ALLOWED_ATTRIBUTES[tag]?.has(name));
+}
+
+export function isAllowedSanitizedAttribute(node, rawName, value) {
+ const name = String(rawName || '').toLowerCase();
+ if (!name || name.startsWith('on') || !allowedAttributeName(node, name)) return false;
+ if (name.startsWith('data-') || name.startsWith('aria-')) {
+ return hasSafeCustomAttributeValue(value);
+ }
+ if (name === 'style') return !hasUnsafeCss(value);
+ if (name === 'src' || name === 'href' || name === 'xlink:href') {
+ return isAllowedResource(node, name, value);
+ }
+ if (isSvgElement(node) && SVG_RESOURCE_PRESENTATION_ATTRIBUTES.has(name)) {
+ return normalizedSvgResourceAttribute(name, value) !== null;
+ }
+ if (name === 'xmlns') return value === SVG_NAMESPACE;
+ if (name === 'xmlns:xlink') return value === 'http://www.w3.org/1999/xlink';
+ return true;
+}
+
+function unwrapNode(node) {
+ node.replaceWith(...node.childNodes);
+}
+
+export function sanitizeSlideDocument(parsed) {
+ let repaired = false;
+ [...parsed.querySelectorAll('*')].forEach((node) => {
+ const tag = String(node.localName || node.tagName || '').toLowerCase();
+ const allowedTags = isSvgElement(node) ? SVG_ALLOWED_TAGS : HTML_ALLOWED_TAGS;
+ if (DELETE_WITH_CONTENT_TAGS.has(tag)) {
+ repaired = true;
+ node.remove();
+ } else if (!allowedTags.has(tag)) {
+ repaired = true;
+ unwrapNode(node);
+ }
+ });
+ parsed.querySelectorAll('style').forEach((style) => {
+ if (hasUnsafeCss(style.textContent)) {
+ repaired = true;
+ style.remove();
+ }
+ });
+ parsed.querySelectorAll('*').forEach((node) => {
+ for (const attribute of [...node.attributes]) {
+ if (!isAllowedSanitizedAttribute(node, attribute.name, attribute.value)) {
+ repaired = true;
+ node.removeAttribute(attribute.name);
+ } else if (isSvgElement(node)) {
+ const name = attribute.name.toLowerCase();
+ if (SVG_RESOURCE_PRESENTATION_ATTRIBUTES.has(name)
+ || name === 'href'
+ || name === 'xlink:href') {
+ const normalized = normalizedSvgResourceAttribute(name, attribute.value);
+ if (normalized !== attribute.value) {
+ repaired = true;
+ node.setAttribute(attribute.name, normalized);
+ }
+ }
+ }
+ }
+ });
+ parsed._pptxSecurityDiagnostics = repaired ? [{
+ severity: 'repaired',
+ code: 'active_content_removed',
+ message: 'Unsafe active content and resource references were removed.',
+ sourceId: 'slide-document',
+ phase: 'security-repair',
+ }] : [];
+ return parsed;
+}
+
+export function sanitizeSlideMarkup(markup) {
+ const parsed = new DOMParser().parseFromString(String(markup || ''), 'text/html');
+ sanitizeSlideDocument(parsed);
+ return `${parsed.documentElement.outerHTML}`;
+}
diff --git a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/test/dom-repair-diagnostics.test.mjs b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/test/dom-repair-diagnostics.test.mjs
new file mode 100644
index 0000000000..4eedb24bef
--- /dev/null
+++ b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/test/dom-repair-diagnostics.test.mjs
@@ -0,0 +1,1777 @@
+import assert from 'node:assert/strict';
+import { createRequire } from 'node:module';
+import test from 'node:test';
+
+import { extractSlideDataFromDocument } from '../src/html2pptx-dom-core.js';
+import { buildSlideFromExtracted } from '../src/pptx-html-build.js';
+import { sanitizeSlideDocumentRoot } from '../src/sanitize-slide-html.js';
+import {
+ HTML_ALLOWED_TAGS,
+ SVG_ALLOWED_TAGS,
+ isAllowedSanitizedAttribute,
+ sanitizeSlideDocument,
+} from '../src/sanitize-slide-markup.js';
+import {
+ buildPageVisualFallbackRequest,
+ buildRasterFallbackRequests,
+ buildWholePageVisualFallbackRequest,
+ renderRasterFallbackPlan,
+ renderRasterFallbackLayers,
+} from '../src/fallback-layer-render.js';
+import {
+ formatLocalizedExportDiagnostic,
+ sanitizeDiagnosticSourceId,
+ summarizePptxExportDiagnostics,
+} from '../src/export-diagnostics.js';
+import { STRINGS } from '../src/i18n.js';
+
+const requireFromWebUi = createRequire(
+ new URL('../../../../../../../../../web-ui/package.json', import.meta.url),
+);
+const { JSDOM, VirtualConsole } = requireFromWebUi('jsdom');
+
+function createSilentDom(markup, options = {}) {
+ return new JSDOM(markup, {
+ ...options,
+ virtualConsole: new VirtualConsole(),
+ });
+}
+
+function createDocument(bodyHtml, css = '') {
+ const dom = createSilentDom(`${bodyHtml}`, {
+ pretendToBeVisual: true,
+ });
+ installMeasurableLayout(dom.window.document);
+ return dom.window.document;
+}
+
+function installMeasurableLayout(doc) {
+ const rect = (left, top, width, height) => ({
+ x: left,
+ y: top,
+ left,
+ top,
+ width,
+ height,
+ right: left + width,
+ bottom: top + height,
+ toJSON() {
+ return { left, top, width, height };
+ },
+ });
+ Object.defineProperties(doc.body, {
+ scrollWidth: { configurable: true, value: 1280 },
+ scrollHeight: { configurable: true, value: 720 },
+ });
+ doc.body.getBoundingClientRect = () => rect(0, 0, 1280, 720);
+ [...doc.body.querySelectorAll('*')].forEach((element, index) => {
+ element.getBoundingClientRect = () => rect(40, 30 + index * 36, 640, 30);
+ Object.defineProperties(element, {
+ offsetWidth: { configurable: true, value: 640 },
+ offsetHeight: { configurable: true, value: 30 },
+ scrollHeight: { configurable: true, value: 30 },
+ });
+ });
+ doc.createRange = () => ({
+ selectNodeContents(element) {
+ this.element = element;
+ },
+ getBoundingClientRect() {
+ return this.element?.getBoundingClientRect() || rect(0, 0, 0, 0);
+ },
+ detach() {},
+ });
+}
+
+test('shared markup sanitizer removes active content and unsafe resource URLs for every export surface', () => {
+ const dom = createSilentDom(`
+
+
+
+ link
+ 
+
+
+ `);
+ const sanitized = sanitizeSlideDocument(dom.window.document).documentElement.outerHTML;
+
+ for (const unsafe of ['',
+ sourceId: '../../secret/
',
+ }, 'en-US');
+ assert.equal(unknown.reason, 'Export encountered a protected internal error.');
+ assert.doesNotMatch(JSON.stringify(unknown), /Users|https?:|script|[<>/]|\.\./i);
+ assert.equal(unknown.sourceId, sanitizeDiagnosticSourceId('../../secret/
'));
+ assert.ok(unknown.reason.length <= 120);
+});
+
+test('routes filter mask and foreignObject SVG visuals to local PNG without dropping editable text', () => {
+ const doc = createDocument(`
+
+
+ `);
+ const repair = sanitizeSlideDocumentRoot(doc);
+ installMeasurableLayout(doc);
+ const slideData = extractSlideDataFromDocument(doc);
+ const diagnostics = [...repair.diagnostics, ...slideData.diagnostics];
+ const request = buildRasterFallbackRequests(doc, diagnostics)
+ .find((item) => item.sourceId === 'unsafe-svg');
+ const unsafeReferenceRequest = buildRasterFallbackRequests(doc, diagnostics)
+ .find((item) => item.sourceId === 'unsafe-reference');
+
+ assert.ok(diagnostics.some((item) => item.code === 'complex_svg_raster'));
+ assert.equal(request?.captureStrategy, 'visual-subtree');
+ assert.ok(request?.suppressedNativeVisualIds.includes('unsafe-svg'));
+ assert.ok(request?.suppressedNativeVisualIds.includes('filtered-path'));
+ assert.equal(unsafeReferenceRequest?.captureStrategy, 'visual-subtree');
+ assert.ok(slideData.elements.some((element) => (
+ element.type === 'svg-text' && element.text === 'Editable label'
+ )));
+});
+
+test('assigns decoration subtree and pseudo capture strategies with exact native suppression scopes', () => {
+ const doc = createDocument(`
+
+

+
Gradient text
+
+
+

+
Filtered text
+
+
+ `);
+ sanitizeSlideDocumentRoot(doc);
+ installMeasurableLayout(doc);
+ const requests = buildRasterFallbackRequests(doc, [
+ { severity: 'fallback', code: 'css_gradient', sourceId: 'gradient' },
+ { severity: 'fallback', code: 'css_filter', sourceId: 'filtered' },
+ { severity: 'fallback', code: 'generated_content', sourceId: 'pseudo' },
+ ]);
+ const bySource = new Map(requests.map((request) => [request.sourceId, request]));
+
+ assert.equal(bySource.get('gradient').captureStrategy, 'self-decoration');
+ assert.deepEqual(bySource.get('gradient').suppressedNativeVisualIds, ['gradient']);
+ assert.equal(bySource.get('gradient').html, undefined);
+ assert.match(bySource.get('gradient').buildHtml(), /data-pptx-capture-strategy="self-decoration"/);
+ assert.match(bySource.get('gradient').buildHtml(), /self-decoration[^}]*>\s*\*/s);
+
+ assert.equal(bySource.get('filtered').captureStrategy, 'visual-subtree');
+ assert.ok(bySource.get('filtered').suppressedNativeVisualIds.includes('filtered-child'));
+ assert.ok(bySource.get('filtered').suppressedNativeVisualIds.includes('filtered'));
+ assert.match(bySource.get('filtered').buildHtml(), /data-pptx-capture-strategy="visual-subtree"/);
+
+ assert.equal(bySource.get('pseudo').captureStrategy, 'pseudo-only');
+ assert.deepEqual(bySource.get('pseudo').suppressedNativeVisualIds, []);
+ assert.match(bySource.get('pseudo').buildHtml(), /data-pptx-capture-strategy="pseudo-only"/);
+ assert.match(bySource.get('pseudo').buildHtml(), /background:\s*none\s*!important/);
+});
+
+test('suppresses duplicated native visuals for raster captures while retaining editable text and unrelated children', async () => {
+ const calls = [];
+ const slide = {
+ addText(value) { calls.push({ op: 'text', value }); },
+ addImage(options) { calls.push({ op: 'image', data: options.data, path: options.path }); },
+ addShape(type) { calls.push({ op: 'shape', type }); },
+ };
+ await buildSlideFromExtracted({
+ background: { type: 'color', value: 'FFFFFF' },
+ elements: [
+ {
+ type: 'shape', sourceId: 'gradient', kind: 'native', zIndex: 1, paintOrder: 1,
+ text: '', position: { x: 1, y: 1, w: 3, h: 2 },
+ shape: { fill: 'FF0000', line: null, rectRadius: 0 },
+ },
+ {
+ type: 'image', sourceId: 'gradient-child', kind: 'native', zIndex: 1, paintOrder: 2,
+ src: 'data:image/png;base64,native-child', position: { x: 1, y: 1, w: 1, h: 1 },
+ },
+ {
+ type: 'image', sourceId: 'filtered-child', kind: 'native', zIndex: 2, paintOrder: 4,
+ src: 'data:image/png;base64,duplicate-filtered-child', position: { x: 4, y: 1, w: 1, h: 1 },
+ },
+ {
+ type: 'p', sourceId: 'filtered-text', kind: 'native', zIndex: 2, paintOrder: 5,
+ text: 'Editable filtered text', position: { x: 4, y: 1, w: 2, h: 1 },
+ style: { fontSize: 16, fontFace: 'Arial', color: '111111', align: 'left' },
+ },
+ ],
+ fallbackLayers: [
+ {
+ sourceId: 'gradient', kind: 'raster', phase: 'local-visual', zIndex: 1, paintOrder: 0,
+ captureStrategy: 'self-decoration', suppressedNativeVisualIds: ['gradient'],
+ canvas: 'full-page', bbox: { x: 1, y: 1, w: 3, h: 2 },
+ data: 'data:image/png;base64,gradient-layer',
+ },
+ {
+ sourceId: 'filtered', kind: 'raster', phase: 'local-visual', zIndex: 2, paintOrder: 3,
+ captureStrategy: 'visual-subtree',
+ suppressedNativeVisualIds: ['filtered', 'filtered-child', 'filtered-text'],
+ canvas: 'full-page', bbox: { x: 4, y: 1, w: 3, h: 2 },
+ data: 'data:image/png;base64,filter-layer',
+ },
+ ],
+ placeholders: [],
+ diagnostics: [],
+ errors: [],
+ }, { width: 1280, height: 720, errors: [] }, {
+ addSlide: () => slide,
+ ShapeType: { rect: 'rect', roundRect: 'roundRect', line: 'line' },
+ });
+
+ assert.deepEqual(calls.map((call) => call.op), ['image', 'image', 'image', 'text']);
+ assert.ok(calls.some((call) => call.data?.includes('gradient-layer')));
+ assert.ok(calls.some((call) => call.data?.includes('native-child')));
+ assert.ok(calls.some((call) => call.data?.includes('filter-layer')));
+ assert.ok(!calls.some((call) => call.data?.includes('duplicate-filtered-child')));
+ assert.equal(calls.filter((call) => call.value === 'Editable filtered text').length, 1);
+});
+
+test('preserves actual SVG DOM paint order between paths and native primitives', () => {
+ const extractOrder = (markup) => {
+ const doc = createDocument(markup);
+ sanitizeSlideDocumentRoot(doc);
+ installMeasurableLayout(doc);
+ const slideData = extractSlideDataFromDocument(doc);
+ return [...slideData.elements, ...(slideData.fallbackLayers || [])]
+ .filter((item) => ['ordered-path', 'ordered-rect'].includes(item.sourceId))
+ .sort((left, right) => left.paintOrder - right.paintOrder)
+ .map((item) => item.sourceId);
+ };
+
+ assert.deepEqual(extractOrder(`
+
+ `), ['ordered-path', 'ordered-rect']);
+ assert.deepEqual(extractOrder(`
+
+ `), ['ordered-rect', 'ordered-path']);
+});
+
+test('uses attribute and computed CSS SVG transforms and falls back when transform cannot be represented', () => {
+ const doc = createDocument(`
+
+ `);
+ sanitizeSlideDocumentRoot(doc);
+ installMeasurableLayout(doc);
+ doc.querySelector('[data-pptx-source-id="ctm-transform"]').getCTM = () => ({
+ a: 1, b: 0, c: 0, d: 1, e: 30, f: 10,
+ });
+ const slideData = extractSlideDataFromDocument(doc);
+ const attr = slideData.elements.find((item) => item.sourceId === 'attr-transform');
+ const css = slideData.elements.find((item) => item.sourceId === 'css-transform');
+ const unsafeNative = slideData.elements.find((item) => item.sourceId === 'unsafe-transform');
+ const unsafeFallback = slideData.fallbackLayers?.find((item) => item.sourceId === 'unsafe-transform');
+ const ctm = slideData.elements.find((item) => item.sourceId === 'ctm-transform');
+
+ assert.equal(attr?.kind, 'native');
+ assert.ok(attr.bbox.w > 0 && attr.bbox.h > 0);
+ assert.equal(css?.kind, 'native');
+ assert.equal(Number(css?.shape?.rotate?.toFixed(1)), 30);
+ assert.ok(css.bbox.w > css.position.w);
+ assert.equal(unsafeNative, undefined);
+ assert.equal(unsafeFallback?.kind, 'svg-image');
+ const svgRect = doc.querySelector('svg').getBoundingClientRect();
+ assert.equal(Number(ctm?.bbox?.x.toFixed(4)), Number(((svgRect.left + 40) / 96).toFixed(4)));
+ assert.equal(Number(ctm?.bbox?.y.toFixed(4)), Number(((svgRect.top + 20) / 96).toFixed(4)));
+});
+
+test('attaches bbox sourceId zIndex and native kind metadata to every native object', () => {
+ const doc = createDocument(`
+
+
Editable copy
+

+
+
+ `);
+ sanitizeSlideDocumentRoot(doc);
+ installMeasurableLayout(doc);
+ const native = extractSlideDataFromDocument(doc).elements;
+
+ assert.ok(native.length >= 3);
+ native.forEach((element) => {
+ assert.equal(element.kind, 'native');
+ assert.ok(element.sourceId);
+ assert.ok(Number.isFinite(element.zIndex));
+ assert.ok(element.bbox);
+ assert.ok(Number.isFinite(element.bbox.x));
+ assert.ok(Number.isFinite(element.bbox.y));
+ assert.ok(Number.isFinite(element.bbox.w));
+ assert.ok(Number.isFinite(element.bbox.h));
+ });
+});
+
+test('does not apply viewBox scaling twice to getCTM polygon coordinates', () => {
+ const doc = createDocument(`
+
+ `);
+ sanitizeSlideDocumentRoot(doc);
+ installMeasurableLayout(doc);
+ for (const id of ['ctm-triangle', 'ctm-diamond']) {
+ doc.querySelector(`[data-pptx-source-id="${id}"]`).getCTM = () => ({
+ a: 2, b: 0, c: 0, d: 3, e: 30, f: 15,
+ });
+ }
+ const svgRect = doc.querySelector('svg').getBoundingClientRect();
+ const slideData = extractSlideDataFromDocument(doc);
+ const triangle = slideData.elements.find((item) => item.sourceId === 'ctm-triangle');
+ const diamond = slideData.elements.find((item) => item.sourceId === 'ctm-diamond');
+
+ assert.equal(Number(triangle.position.x.toFixed(4)), Number(((svgRect.left + 30) / 96).toFixed(4)));
+ assert.equal(Number(triangle.position.w.toFixed(4)), Number((40 / 96).toFixed(4)));
+ assert.equal(Number(diamond.position.y.toFixed(4)), Number(((svgRect.top + 15) / 96).toFixed(4)));
+ assert.equal(Number(diamond.position.h.toFixed(4)), Number((60 / 96).toFixed(4)));
+});
+
+test('shares one full-DOM paint order domain across HTML native and raster fallback siblings', () => {
+ const wrappers = Array.from({ length: 30 }, (_, index) => (
+ `
`
+ )).join('');
+ const doc = createDocument(`
+
+ ${wrappers}
+
+
+ `);
+ const repair = sanitizeSlideDocumentRoot(doc);
+ installMeasurableLayout(doc);
+ const slideData = extractSlideDataFromDocument(doc);
+ const raster = buildRasterFallbackRequests(doc, repair.diagnostics)
+ .find((item) => item.sourceId === 'gradient-middle');
+ const before = slideData.elements.find((item) => item.sourceId === 'native-before');
+ const after = slideData.elements.find((item) => item.sourceId === 'native-after');
+
+ assert.ok(before.paintOrder < raster.paintOrder);
+ assert.ok(raster.paintOrder < after.paintOrder);
+ assert.equal(before.subOrder, 0);
+ assert.equal(raster.subOrder, 0);
+ assert.equal(after.subOrder, 0);
+});
+
+test('orders decomposed objects by shared paintOrder and explicit subOrder in Stage 2', async () => {
+ const calls = [];
+ const slide = {
+ addText() {},
+ addImage(options) { calls.push(options.data); },
+ addShape(_type, options) { calls.push(options.line?.color); },
+ };
+ await buildSlideFromExtracted({
+ background: { type: 'color', value: 'FFFFFF' },
+ elements: [
+ {
+ type: 'line', sourceId: 'poly', kind: 'native', zIndex: 0, paintOrder: 8, subOrder: 2,
+ x1: 0, y1: 0, x2: 1, y2: 1, color: '222222', width: 1,
+ },
+ {
+ type: 'line', sourceId: 'poly', kind: 'native', zIndex: 0, paintOrder: 8, subOrder: 1,
+ x1: 0, y1: 0, x2: 1, y2: 1, color: '111111', width: 1,
+ },
+ ],
+ fallbackLayers: [{
+ sourceId: 'middle', kind: 'svg-image', zIndex: 0, paintOrder: 8, subOrder: 1.5,
+ bbox: { x: 0, y: 0, w: 1, h: 1 }, data: 'data:image/svg+xml,mid',
+ }],
+ placeholders: [],
+ diagnostics: [],
+ errors: [],
+ }, { width: 1280, height: 720, errors: [] }, {
+ addSlide: () => slide,
+ ShapeType: { line: 'line', rect: 'rect', roundRect: 'roundRect' },
+ });
+
+ assert.deepEqual(calls, ['111111', 'data:image/svg+xml,mid', '222222']);
+});
+
+test('local SVG image preserves class and inherited styles plus ancestor transforms', () => {
+ const doc = createDocument(`
+
+ `, '.accent { fill: inherit; stroke-width: 5; }');
+ sanitizeSlideDocumentRoot(doc);
+ installMeasurableLayout(doc);
+ const slideData = extractSlideDataFromDocument(doc);
+ const layer = slideData.fallbackLayers.find((item) => item.sourceId === 'styled-path');
+ const markup = decodeURIComponent(layer.data.replace('data:image/svg+xml,', ''));
+
+ assert.match(markup, /viewBox="0 0 300 150"/);
+ assert.match(markup, /transform="translate\(40 20\)"/);
+ assert.match(markup, /fill:\s*rgb\(10,\s*20,\s*30\)/);
+ assert.match(markup, /stroke:\s*rgb\(40,\s*50,\s*60\)/);
+ assert.match(markup, /stroke-width:\s*5(?:px)?/);
+ assert.match(markup, /opacity:\s*0\.6/);
+ assert.equal(layer.bbox.w, 640 / 96);
+ assert.equal(layer.bbox.h, 30 / 96);
+});
diff --git a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/test/generated-file-protocol.test.mjs b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/test/generated-file-protocol.test.mjs
new file mode 100644
index 0000000000..ffff744bfd
--- /dev/null
+++ b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/test/generated-file-protocol.test.mjs
@@ -0,0 +1,706 @@
+import assert from 'node:assert/strict';
+import { readFile } from 'node:fs/promises';
+import test from 'node:test';
+
+import {
+ PPT_DESIGN_SKILL_KEY,
+ buildAgentPrompt,
+} from '../src/agent-prompt.js';
+import {
+ DeckProjectContractError,
+ createDeckProjectSkeleton,
+ persistDeckProjectSeed,
+ readDeckProjectContract,
+ readProjectPlanWithRetry,
+} from '../src/deck-project-contract.js';
+import * as deckProjectContract from '../src/deck-project-contract.js';
+import {
+ buildElementSlideHtml as buildPureElementSlideHtml,
+ elementModelElementHtml,
+} from '../src/element-model-html.js';
+
+const validPlan = {
+ status: 'complete',
+ title: '可靠生成协议',
+ language: 'zh-CN',
+ outline: [
+ { id: 'intro', title: '协议先于页面', bullets: [], slide_id: 'slide-01' },
+ { id: 'finish', title: '完成必须可验证', bullets: [], slide_id: 'slide-02' },
+ ],
+ slide_order: ['slide-01', 'slide-02'],
+ style: {},
+ assumptions: [],
+};
+
+const completeSlide = (title) => `${title}
`;
+
+test('shared pure element-model serializer module is available', async () => {
+ const serializer = await import('../src/element-model-html.js').catch(() => null);
+
+ assert.ok(serializer, 'element-model serializer module should exist');
+ assert.equal(typeof serializer.buildElementSlideHtml, 'function');
+ assert.equal(typeof serializer.elementModelElementHtml, 'function');
+});
+
+test('element-model serializer preserves six element types, semantics, geometry, and theme', async () => {
+ const baseStyle = {
+ fontSize: 24,
+ fontWeight: 600,
+ color: 'ink',
+ background: 'panel',
+ opacity: 0.9,
+ borderRadius: 8,
+ align: 'left',
+ };
+ const slide = {
+ title: 'Element deck',
+ theme: {
+ background: '#fefefe',
+ ink: '#101010',
+ muted: '#606060',
+ primary: '#0055aa',
+ accent: '#ee5500',
+ panel: '#ffffff',
+ },
+ elements: [
+ { id: 'text-1', type: 'text', x: 1, y: 2, w: 30, h: 10, text: '正文不可丢', style: { ...baseStyle, fontSize: 30 } },
+ { id: 'list-1', type: 'list', x: 3, y: 14, w: 35, h: 25, items: ['第一项', '第二项'], style: baseStyle },
+ { id: 'metric-1', type: 'metric', x: 40, y: 5, w: 20, h: 18, text: '42%', label: '转化率', style: { ...baseStyle, color: 'primary' } },
+ {
+ id: 'chart-1',
+ type: 'chart',
+ x: 42,
+ y: 28,
+ w: 40,
+ h: 35,
+ text: '季度趋势',
+ data: [{ label: 'Q1', value: 10 }, { label: 'Q2', value: 24 }],
+ style: baseStyle,
+ },
+ {
+ id: 'media-1',
+ type: 'media',
+ x: 5,
+ y: 52,
+ w: 28,
+ h: 38,
+ text: '产品截图',
+ src: 'https://example.com/product.png',
+ style: baseStyle,
+ },
+ { id: 'shape-1', type: 'shape', x: 70, y: 68, w: 20, h: 20, text: '形状标签', style: { ...baseStyle, background: 'accent' } },
+ ],
+ };
+
+ const html = buildPureElementSlideHtml(slide);
+
+ assert.match(html, /width:\s*1280px/);
+ assert.match(html, /height:\s*720px/);
+ assert.match(html, /background:\s*#fefefe/);
+ assert.match(html, /data-element-type="text"[^>]*style="[^"]*left:1%;top:2%;width:30%;height:10%/);
+ assert.match(html, /font-size:30px/);
+ assert.match(html, /color:#101010/);
+ assert.match(html, /]*>正文不可丢<\/p>/);
+ assert.match(html, /
第一项<\/p><\/li>/);
+ assert.match(html, /
第二项<\/p><\/li>/);
+ assert.match(html, /
]*>42%<\/p>/);
+ assert.match(html, /
]*>转化率<\/p>/);
+ assert.match(html, /季度趋势/);
+ assert.match(html, /Q1/);
+ assert.match(html, />10);
+ assert.match(html, /Q2/);
+ assert.match(html, />24);
+ assert.match(html, /
]*src="https:\/\/example\.com\/product\.png"/);
+ assert.match(html, /产品截图/);
+ assert.match(html, /data-element-type="shape"/);
+ assert.match(html, /background:#ee5500/);
+ assert.match(html, /形状标签/);
+
+ const seed = deckProjectContract.createDeckProjectSeed({
+ hasExistingDeck: true,
+ title: 'Element deck',
+ slides: [{ ...slide, html: '' }],
+ serializeElementSlide: buildPureElementSlideHtml,
+ });
+ const files = new Map([
+ ['project.json', JSON.stringify(seed.plan)],
+ ...seed.slideFiles.map((file) => [file.relPath, file.html]),
+ ]);
+ const deck = await readDeckProjectContract(async (relPath) => files.get(relPath), { maxAttempts: 1 });
+ assert.equal(seed.plan.status, 'complete');
+ assert.equal(deck.slides[0].html, html);
+});
+
+test('shared element helper preserves editor interaction markup', () => {
+ const theme = { ink: '#111111', primary: '#0055aa', muted: '#666666', panel: '#ffffff' };
+ const style = {
+ fontSize: 24,
+ fontWeight: 600,
+ color: 'ink',
+ background: 'transparent',
+ opacity: 1,
+ borderRadius: 0,
+ align: 'left',
+ };
+ const text = elementModelElementHtml(
+ { id: 'text-1', type: 'text', x: 1, y: 2, w: 30, h: 10, text: '可编辑正文', style },
+ theme,
+ { mode: 'editor', editable: true, selectedId: 'text-1' },
+ );
+ const list = elementModelElementHtml(
+ { id: 'list-1', type: 'list', x: 1, y: 2, w: 30, h: 10, items: ['A'], style },
+ theme,
+ { mode: 'editor', editable: true },
+ );
+
+ assert.match(text, /class="slide-element element-text is-selected"/);
+ assert.match(text, /data-edit-text="text-1"/);
+ assert.match(text, /contenteditable="true"/);
+ assert.match(text, /class="resize-handle"/);
+ assert.match(text, /font-size:clamp\(8px,/);
+ assert.match(list, /data-edit-list="list-1"/);
+ assert.match(list, /data-item-index="0"/);
+});
+
+test('prompt pins the stable skill key and workspace-relative delivery contract', () => {
+ const prompt = buildAgentPrompt({ instruction: '生成两页协议说明' });
+
+ assert.equal(PPT_DESIGN_SKILL_KEY, 'user::bitfun-system::ppt-design');
+ assert.match(prompt, /user::bitfun-system::ppt-design/);
+ assert.match(prompt, /工作区根目录下的 `project\.json`/);
+ assert.match(prompt, /工作区根目录下的 `slides\/slide-NN\.html`/);
+ assert.match(prompt, /`project\.json` 的 `status` 设为 `"complete"`/);
+ assert.match(prompt, /`slide_order`.*每一页.*完整 HTML/s);
+});
+
+test('prompt carries a targeted contract diagnostic into same-session continuation', () => {
+ const prompt = buildAgentPrompt({
+ instruction: '继续生成',
+ continueAfterInterruption: true,
+ projectContractDiagnostic: {
+ code: 'missing_slide_files',
+ continuationPrompt: '只补写 slides/slide-02.html,然后重新完成有界检查。',
+ },
+ });
+
+ assert.match(prompt, /同一会话/);
+ assert.match(prompt, /missing_slide_files/);
+ assert.match(prompt, /只补写 slides\/slide-02\.html/);
+});
+
+test('project.json read retries transient visibility like slide reads', async () => {
+ let attempts = 0;
+ const sleeps = [];
+ const plan = await readProjectPlanWithRetry(async (relPath) => {
+ assert.equal(relPath, 'project.json');
+ attempts += 1;
+ if (attempts === 1) throw new Error('not visible yet');
+ if (attempts === 2) return '{"status":"complete"';
+ return JSON.stringify(validPlan);
+ }, {
+ maxAttempts: 3,
+ delayMs: 7,
+ sleep: async (delay) => sleeps.push(delay),
+ });
+
+ assert.deepEqual(plan, validPlan);
+ assert.equal(attempts, 3);
+ assert.deepEqual(sleeps, [7, 7]);
+});
+
+test('project contract waits for a planning skeleton to become complete in the same run', async () => {
+ const skeleton = createDeckProjectSkeleton({ title: '逐步写入' });
+ let projectReads = 0;
+ const sleeps = [];
+
+ const deck = await readDeckProjectContract(async (relPath) => {
+ if (relPath === 'project.json') {
+ projectReads += 1;
+ return JSON.stringify(projectReads === 1 ? skeleton : {
+ ...validPlan,
+ outline: [validPlan.outline[0]],
+ slide_order: ['slide-01'],
+ });
+ }
+ return completeSlide('恢复后的第一页');
+ }, {
+ maxAttempts: 2,
+ delayMs: 5,
+ sleep: async (delay) => sleeps.push(delay),
+ });
+
+ assert.equal(projectReads, 2);
+ assert.deepEqual(sleeps, [5]);
+ assert.equal(deck.plan.status, 'complete');
+ assert.equal(deck.slides[0].outlineEntry.title, '协议先于页面');
+ assert.match(deck.slides[0].html, /恢复后的第一页/);
+});
+
+test('project contract rejects slide_order and outline disagreement', async () => {
+ const plan = {
+ ...validPlan,
+ slide_order: ['slide-01', 'slide-03'],
+ };
+
+ await assert.rejects(
+ readDeckProjectContract(async (relPath) => (
+ relPath === 'project.json' ? JSON.stringify(plan) : completeSlide(relPath)
+ ), { maxAttempts: 1 }),
+ (error) => {
+ assert.ok(error instanceof DeckProjectContractError);
+ assert.equal(error.diagnostic.code, 'invalid_project_contract');
+ assert.match(error.diagnostic.continuationPrompt, /slide_order/);
+ assert.match(error.diagnostic.continuationPrompt, /outline/);
+ return true;
+ },
+ );
+});
+
+test('project contract follows slide_order when it intentionally differs from outline order', async () => {
+ const reversed = {
+ ...validPlan,
+ slide_order: ['slide-02', 'slide-01'],
+ };
+
+ const deck = await readDeckProjectContract(async (relPath) => (
+ relPath === 'project.json'
+ ? JSON.stringify(reversed)
+ : completeSlide(relPath.endsWith('slide-01.html') ? 'First file' : 'Second file')
+ ), { maxAttempts: 1 });
+
+ assert.deepEqual(deck.slides.map((slide) => slide.slideId), ['slide-02', 'slide-01']);
+ assert.deepEqual(deck.slides.map((slide) => slide.slideNumber), [1, 2]);
+ assert.deepEqual(deck.slides.map((slide) => slide.outlineEntry.id), ['finish', 'intro']);
+ assert.match(deck.slides[0].html, /Second file/);
+});
+
+test('project contract requires id, title, and bullets on every outline item', async (t) => {
+ const invalidItems = [
+ ['id', { title: '标题', bullets: [], slide_id: 'slide-01' }],
+ ['title', { id: 'intro', bullets: [], slide_id: 'slide-01' }],
+ ['bullets', { id: 'intro', title: '标题', slide_id: 'slide-01' }],
+ ];
+
+ for (const [field, outlineItem] of invalidItems) {
+ await t.test(`rejects missing ${field}`, async () => {
+ const plan = {
+ ...validPlan,
+ outline: [outlineItem],
+ slide_order: ['slide-01'],
+ };
+ await assert.rejects(
+ readDeckProjectContract(async (relPath) => (
+ relPath === 'project.json' ? JSON.stringify(plan) : completeSlide('第一页')
+ ), { maxAttempts: 1 }),
+ (error) => {
+ assert.equal(error.diagnostic.code, 'invalid_project_contract');
+ assert.equal(error.diagnostic.invalidOutlineField, field);
+ assert.match(error.diagnostic.continuationPrompt, new RegExp(field));
+ return true;
+ },
+ );
+ });
+ }
+});
+
+test('project contract requires string bullets and unique outline ids', async (t) => {
+ await t.test('rejects non-string bullet entries', async () => {
+ const plan = {
+ ...validPlan,
+ outline: [{ ...validPlan.outline[0], bullets: ['valid', 42] }],
+ slide_order: ['slide-01'],
+ };
+ await assert.rejects(
+ readDeckProjectContract(async (relPath) => (
+ relPath === 'project.json' ? JSON.stringify(plan) : completeSlide('第一页')
+ ), { maxAttempts: 1 }),
+ (error) => error.diagnostic.invalidOutlineField === 'bullets',
+ );
+ });
+
+ await t.test('rejects duplicate outline ids', async () => {
+ const plan = {
+ ...validPlan,
+ outline: [
+ validPlan.outline[0],
+ { ...validPlan.outline[1], id: validPlan.outline[0].id },
+ ],
+ };
+ await assert.rejects(
+ readDeckProjectContract(async (relPath) => (
+ relPath === 'project.json' ? JSON.stringify(plan) : completeSlide(relPath)
+ ), { maxAttempts: 1 }),
+ (error) => error.diagnostic.invalidOutlineField === 'id',
+ );
+ });
+});
+
+test('project contract reports every missing page for targeted continuation', async () => {
+ await assert.rejects(
+ readDeckProjectContract(async (relPath) => {
+ if (relPath === 'project.json') return JSON.stringify(validPlan);
+ if (relPath === 'slides/slide-01.html') return completeSlide('第一页');
+ throw new Error('not found');
+ }, { maxAttempts: 2, delayMs: 0 }),
+ (error) => {
+ assert.equal(error.diagnostic.code, 'missing_slide_files');
+ assert.deepEqual(error.diagnostic.missingPaths, ['slides/slide-02.html']);
+ assert.match(error.diagnostic.continuationPrompt, /只补写.*slides\/slide-02\.html/s);
+ assert.match(error.message, /missing_slide_files/);
+ return true;
+ },
+ );
+});
+
+test('slide completeness requires html and body opening and closing structure', async (t) => {
+ const malformedSlides = [
+ ['garbage with closing html only', 'garbage'],
+ ['html without body', 'content'],
+ ['body without closing body', '
content'],
+ ['truncated closing html tag', 'content \n '],
+ ];
+
+ for (const [name, malformedHtml] of malformedSlides) {
+ await t.test(name, async () => {
+ const oneSlidePlan = {
+ ...validPlan,
+ outline: [validPlan.outline[0]],
+ slide_order: ['slide-01'],
+ };
+ await assert.rejects(
+ readDeckProjectContract(async (relPath) => (
+ relPath === 'project.json' ? JSON.stringify(oneSlidePlan) : malformedHtml
+ ), { maxAttempts: 1 }),
+ (error) => {
+ assert.equal(error.diagnostic.code, 'missing_slide_files');
+ assert.deepEqual(error.diagnostic.missingPaths, ['slides/slide-01.html']);
+ return true;
+ },
+ );
+ });
+ }
+});
+
+test('bad project JSON returns a repairable same-session diagnostic', async () => {
+ await assert.rejects(
+ readProjectPlanWithRetry(async () => '{"status":"complete"', { maxAttempts: 2, delayMs: 0 }),
+ (error) => {
+ assert.equal(error.diagnostic.code, 'invalid_project_json');
+ assert.match(error.diagnostic.continuationPrompt, /修复 `project\.json` JSON/);
+ return true;
+ },
+ );
+});
+
+test('missing project.json has a distinct targeted diagnostic', async () => {
+ await assert.rejects(
+ readProjectPlanWithRetry(async () => {
+ const error = new Error('not found');
+ error.code = 'ENOENT';
+ throw error;
+ }, { maxAttempts: 2, delayMs: 0 }),
+ (error) => {
+ assert.equal(error.diagnostic.code, 'missing_project_json');
+ assert.match(error.diagnostic.continuationPrompt, /创建 `project\.json`/);
+ return true;
+ },
+ );
+});
+
+test('empty project.json retries and remains a targeted missing-project continuation', async () => {
+ let attempts = 0;
+ await assert.rejects(
+ readProjectPlanWithRetry(async () => {
+ attempts += 1;
+ return attempts === 1 ? '' : ' \n';
+ }, { maxAttempts: 2, delayMs: 0 }),
+ (error) => {
+ assert.equal(error.diagnostic.code, 'missing_project_json');
+ assert.match(error.diagnostic.summary, /missing or empty/);
+ assert.match(error.diagnostic.continuationPrompt, /创建 `project\.json`/);
+ return true;
+ },
+ );
+ assert.equal(attempts, 2);
+});
+
+test('new-deck skeleton is valid JSON but cannot satisfy completion contract', async () => {
+ const skeleton = createDeckProjectSkeleton({
+ title: '待规划 deck',
+ language: 'zh-CN',
+ style: { theme: 'light' },
+ });
+ const serialized = `${JSON.stringify(skeleton, null, 2)}\n`;
+
+ assert.deepEqual(JSON.parse(serialized), skeleton);
+ assert.equal(skeleton.status, 'planning');
+ assert.deepEqual(skeleton.outline, []);
+ assert.deepEqual(skeleton.slide_order, []);
+ await assert.rejects(
+ readDeckProjectContract(async (relPath) => {
+ if (relPath === 'project.json') return serialized;
+ throw new Error(`unexpected read: ${relPath}`);
+ }, { maxAttempts: 1 }),
+ (error) => error.diagnostic.code === 'project_incomplete',
+ );
+});
+
+test('existing-deck seed serializes element-only slides and passes the strict contract', async () => {
+ assert.equal(typeof deckProjectContract.createDeckProjectSeed, 'function');
+ const seed = deckProjectContract.createDeckProjectSeed({
+ hasExistingDeck: true,
+ title: '旧 deck',
+ language: 'zh-CN',
+ style: { theme: 'light' },
+ slides: [
+ { title: '旧第一页', html: completeSlide('旧第一页') },
+ {
+ title: '旧第二页',
+ html: '',
+ elements: [{ type: 'text', text: '来自 element model' }],
+ },
+ ],
+ serializeElementSlide: buildPureElementSlideHtml,
+ });
+
+ assert.equal(seed.plan.status, 'complete');
+ assert.deepEqual(seed.plan.slide_order, ['slide-01', 'slide-02']);
+ assert.deepEqual(seed.plan.outline[1], {
+ id: 'slide-02',
+ title: '旧第二页',
+ bullets: [],
+ slide_id: 'slide-02',
+ });
+ assert.equal(seed.slideFiles.length, 2);
+ assert.match(seed.slideFiles[1].html, /]/i);
+ assert.match(seed.slideFiles[1].html, /]/i);
+
+ const files = new Map([
+ ['project.json', JSON.stringify(seed.plan)],
+ ...seed.slideFiles.map((file) => [file.relPath, file.html]),
+ ]);
+ const deck = await readDeckProjectContract(async (relPath) => {
+ if (!files.has(relPath)) throw new Error('not found');
+ return files.get(relPath);
+ }, { maxAttempts: 1 });
+ assert.equal(deck.slides.length, 2);
+});
+
+test('legacy element-model seed round-trips every serialized element without losing content', async () => {
+ const legacySlide = {
+ title: '旧元素模型',
+ theme: {
+ background: '#fefefe',
+ ink: '#101010',
+ muted: '#606060',
+ primary: '#0055aa',
+ accent: '#ee5500',
+ panel: '#ffffff',
+ },
+ elements: [
+ { id: 'text-old', type: 'text', x: 2, y: 4, w: 30, h: 10, text: '旧正文', style: {} },
+ { id: 'list-old', type: 'list', x: 2, y: 16, w: 30, h: 20, items: ['旧列表一', '旧列表二'], style: {} },
+ { id: 'metric-old', type: 'metric', x: 36, y: 4, w: 20, h: 16, text: '98%', label: '旧指标', style: {} },
+ {
+ id: 'chart-old', type: 'chart', x: 36, y: 24, w: 40, h: 30, text: '旧图表',
+ data: [{ label: '旧类别', value: 7 }], style: {},
+ },
+ {
+ id: 'media-old', type: 'media', x: 2, y: 50, w: 24, h: 30,
+ text: '旧媒体', src: 'data:image/png;base64,AA==', style: {},
+ },
+ { id: 'shape-old', type: 'shape', x: 78, y: 58, w: 18, h: 20, text: '旧形状', style: {} },
+ ],
+ };
+ const seed = deckProjectContract.createDeckProjectSeed({
+ hasExistingDeck: true,
+ title: '旧 deck',
+ slides: [legacySlide],
+ serializeElementSlide: buildPureElementSlideHtml,
+ });
+ const files = new Map([
+ ['project.json', JSON.stringify(seed.plan)],
+ ...seed.slideFiles.map((file) => [file.relPath, file.html]),
+ ]);
+
+ const deck = await readDeckProjectContract(async (relPath) => files.get(relPath), { maxAttempts: 1 });
+ const html = deck.slides[0].html;
+
+ for (const id of legacySlide.elements.map((element) => element.id)) {
+ assert.match(html, new RegExp(`data-element-id="${id}"`), id);
+ }
+ for (const text of ['旧正文', '旧列表一', '旧列表二', '98%', '旧指标', '旧图表', '旧类别', '7', '旧媒体', '旧形状']) {
+ assert.match(html, new RegExp(text), text);
+ }
+ assert.match(html, /src="data:image\/png;base64,AA=="/);
+ assert.equal(seed.plan.status, 'complete');
+ assert.equal(deck.slides[0].slideId, 'slide-01');
+});
+
+test('existing-deck seed stays planning with an exact diagnostic when a slide cannot serialize', () => {
+ const seed = deckProjectContract.createDeckProjectSeed({
+ hasExistingDeck: true,
+ title: '旧 deck',
+ language: 'zh-CN',
+ slides: [
+ { title: '旧第一页', html: completeSlide('旧第一页') },
+ { title: '损坏页面' },
+ ],
+ serializeElementSlide: () => {
+ throw new Error('unsupported slide model');
+ },
+ });
+
+ assert.equal(seed.plan.status, 'planning');
+ assert.equal(seed.diagnostic.code, 'missing_slide_files');
+ assert.deepEqual(seed.diagnostic.missingPaths, ['slides/slide-02.html']);
+ assert.match(seed.diagnostic.continuationPrompt, /slides\/slide-02\.html/);
+});
+
+test('persistDeckProjectSeed creates slides directory before ordered writes', async () => {
+ const calls = [];
+ let directoryReady = false;
+ const fs = {
+ async mkdir(path, options) {
+ calls.push(['mkdir', path, options]);
+ directoryReady = true;
+ },
+ async writeFile(path, content) {
+ assert.equal(directoryReady, true);
+ calls.push(['write', path, content]);
+ },
+ };
+ const seed = {
+ plan: { status: 'complete' },
+ slideFiles: [
+ { relPath: 'slides/slide-01.html', html: completeSlide('one') },
+ { relPath: 'slides/slide-02.html', html: completeSlide('two') },
+ ],
+ };
+
+ await persistDeckProjectSeed(fs, '/deck', seed);
+
+ assert.deepEqual(calls.map(([operation, path]) => [operation, path]), [
+ ['mkdir', '/deck/slides'],
+ ['write', '/deck/project.json'],
+ ['write', '/deck/slides/slide-01.html'],
+ ['write', '/deck/slides/slide-02.html'],
+ ]);
+ assert.deepEqual(calls[0][2], { recursive: true });
+});
+
+test('persistDeckProjectSeed reports mkdir and slide write failures for same-session continuation', async (t) => {
+ await t.test('mkdir failure', async () => {
+ await assert.rejects(
+ persistDeckProjectSeed({
+ async mkdir() { throw new Error('/private/deck denied'); },
+ async writeFile() { assert.fail('write must not run'); },
+ }, '/deck', { plan: {}, slideFiles: [] }),
+ (error) => {
+ assert.ok(error instanceof DeckProjectContractError);
+ assert.equal(error.diagnostic.code, 'seed_fs_mkdir_failed');
+ assert.equal(error.diagnostic.phase, 'mkdir');
+ assert.deepEqual(error.diagnostic.missingPaths, ['slides']);
+ assert.match(error.diagnostic.continuationPrompt, /slides/);
+ return true;
+ },
+ );
+ });
+ await t.test('slide write failure', async () => {
+ await assert.rejects(
+ persistDeckProjectSeed({
+ async mkdir() {},
+ async writeFile(path) {
+ if (path.endsWith('slide-02.html')) throw new Error(' /Users/alice/deck');
+ },
+ }, '/deck', {
+ plan: {},
+ slideFiles: [
+ { relPath: 'slides/slide-01.html', html: completeSlide('one') },
+ { relPath: 'slides/slide-02.html', html: completeSlide('two') },
+ ],
+ }),
+ (error) => {
+ assert.equal(error.diagnostic.code, 'seed_fs_write_failed');
+ assert.equal(error.diagnostic.phase, 'slide-write');
+ assert.deepEqual(error.diagnostic.missingPaths, ['slides/slide-02.html']);
+ return true;
+ },
+ );
+ });
+});
+
+test('request always carries seed diagnostics while continuation depends on an existing session', () => {
+ assert.equal(typeof deckProjectContract.buildDeckRunRequestInput, 'function');
+ const diagnostic = {
+ code: 'missing_slide_files',
+ continuationPrompt: '只补写 slides/slide-02.html。',
+ };
+ const request = deckProjectContract.buildDeckRunRequestInput(
+ { operation: 'generate', instruction: '继续' },
+ { sessionId: 'session-1', projectContractDiagnostic: diagnostic },
+ );
+
+ assert.equal(request.continueAfterInterruption, true);
+ assert.equal(request.projectContractDiagnostic, diagnostic);
+ assert.equal(request.operation, 'generate');
+ assert.deepEqual(
+ deckProjectContract.buildDeckRunRequestInput(
+ { operation: 'generate' },
+ { sessionId: '', projectContractDiagnostic: diagnostic },
+ ),
+ { operation: 'generate', projectContractDiagnostic: diagnostic },
+ );
+});
+
+test('first-turn prompt receives seed filesystem continuation diagnostics', () => {
+ const diagnostic = {
+ code: 'seed_fs_mkdir_failed',
+ continuationPrompt: '创建 slides 目录后继续。',
+ missingPaths: ['slides'],
+ };
+ const request = deckProjectContract.buildDeckRunRequestInput(
+ { operation: 'generate', instruction: '生成演示稿' },
+ { sessionId: '', projectContractDiagnostic: diagnostic },
+ );
+ const prompt = buildAgentPrompt(request);
+
+ assert.equal(request.continueAfterInterruption, undefined);
+ assert.equal(request.projectContractDiagnostic, diagnostic);
+ assert.match(prompt, /seed_fs_mkdir_failed/);
+ assert.match(prompt, /创建 slides 目录后继续/);
+});
+
+test('skill defines the workspace root unambiguously and bounded plan-first completion', async () => {
+ const skillUrl = new URL('../../../../../../../../assembly/core/builtin_skills/ppt-design/SKILL.md', import.meta.url);
+ const skill = await readFile(skillUrl, 'utf8');
+
+ assert.doesNotMatch(skill, /\{\{ppt_project_dir\}\}/);
+ assert.match(skill, /当前工作区根目录就是当前 deck 根目录/);
+ assert.match(skill, /先.*project\.json.*再.*slides\/slide-NN\.html/s);
+ assert.match(skill, /有界完成检查/);
+ assert.match(skill, /仅检查一次/);
+});
+
+test('repository exposes and runs the focused PPT Live contract test in CI', async () => {
+ const repoRoot = new URL('../../../../../../../../../../', import.meta.url);
+ const packageJson = JSON.parse(await readFile(new URL('package.json', repoRoot), 'utf8'));
+ const ci = await readFile(new URL('.github/workflows/ci.yml', repoRoot), 'utf8');
+
+ assert.equal(
+ packageJson.scripts['test:ppt-live'],
+ 'node --test src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/test/*.test.mjs',
+ );
+ assert.match(ci, /name: Validate PPT Live generated-file contract[\s\S]*run: pnpm run test:ppt-live/);
+ assert.ok(
+ ci.indexOf('- name: Install dependencies') < ci.indexOf('- name: Validate PPT Live generated-file contract'),
+ 'PPT Live tests must run after pnpm install on a clean runner',
+ );
+});
+
+test('UI seeds deck projects through the production persistence helper', async () => {
+ const ui = await readFile(new URL('../ui.js', import.meta.url), 'utf8');
+ assert.match(ui, /import\s*\{[\s\S]*persistDeckProjectSeed[\s\S]*\}\s*from\s*['"]\.\/src\/deck-project-contract\.js['"]/);
+ assert.match(ui, /await persistDeckProjectSeed\(fs,\s*project\.dir,\s*seed\)/);
+});
diff --git a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/test/pptx-ooxml-artifact.test.mjs b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/test/pptx-ooxml-artifact.test.mjs
new file mode 100644
index 0000000000..b0f63f24da
--- /dev/null
+++ b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/test/pptx-ooxml-artifact.test.mjs
@@ -0,0 +1,623 @@
+import assert from 'node:assert/strict';
+import { createRequire } from 'node:module';
+import test from 'node:test';
+
+import {
+ buildSlideFromExtracted,
+ createPptxDeck,
+} from '../src/pptx-html-build.js';
+
+const requireFromPptxGen = createRequire(import.meta.resolve('pptxgenjs'));
+const JSZip = requireFromPptxGen('jszip');
+const requireFromWebUi = createRequire(
+ new URL('../../../../../../../../../web-ui/package.json', import.meta.url),
+);
+const { JSDOM, VirtualConsole } = requireFromWebUi('jsdom');
+
+const PNG_1X1 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=';
+const BODY_DIMENSIONS = { width: 1280, height: 720, errors: [] };
+
+function textElement({
+ text,
+ sourceId,
+ zIndex,
+ paintOrder,
+ x = 1,
+ y = 1,
+ w = 4,
+ h = 0.6,
+}) {
+ return {
+ type: 'p',
+ text,
+ sourceId,
+ kind: 'native',
+ zIndex,
+ paintOrder,
+ position: { x, y, w, h },
+ style: {
+ fontSize: 20,
+ fontFace: 'Arial',
+ color: '111111',
+ align: 'left',
+ lineSpacing: 24,
+ margin: 0,
+ },
+ };
+}
+
+async function writeAndOpen(pptx) {
+ const output = await pptx.write({ outputType: 'nodebuffer' });
+ assert.ok(Buffer.isBuffer(output), 'PptxGenJS 4.0.1 must return a Node Buffer');
+ assert.ok(output.length > 0, 'PPTX buffer must not be empty');
+ return JSZip.loadAsync(output);
+}
+
+async function zipText(zip, path) {
+ const entry = zip.file(path);
+ assert.ok(entry, `${path} must exist in the PPTX`);
+ return entry.async('string');
+}
+
+function topLevelSlideObjects(slideXml) {
+ return [...slideXml.matchAll(/[\s\S]*?<\/p:\1>/g)]
+ .map((match) => ({ type: match[1], xml: match[0] }));
+}
+
+function pictureExtents(objectXml) {
+ const match = objectXml.match(
+ /[\s\S]*?\s*/,
+ );
+ assert.ok(match, 'picture transform must contain offset and extent');
+ return {
+ x: Number(match[1]),
+ y: Number(match[2]),
+ cx: Number(match[3]),
+ cy: Number(match[4]),
+ };
+}
+
+function relatedMediaPaths(relsXml) {
+ return [...relsXml.matchAll(/Type="[^"]*\/image" Target="\.\.\/media\/([^"]+)"/g)]
+ .map((match) => `ppt/media/${match[1]}`);
+}
+
+async function withControllableExportDom(run) {
+ const dom = new JSDOM('', {
+ pretendToBeVisual: true,
+ virtualConsole: new VirtualConsole(),
+ });
+ const { window } = dom;
+ const { document } = window;
+ const savedGlobals = new Map();
+ const globals = {
+ window,
+ document,
+ DOMParser: window.DOMParser,
+ Node: window.Node,
+ NodeFilter: window.NodeFilter,
+ getComputedStyle: window.getComputedStyle.bind(window),
+ requestAnimationFrame: window.requestAnimationFrame.bind(window),
+ cancelAnimationFrame: window.cancelAnimationFrame.bind(window),
+ };
+ Object.entries(globals).forEach(([key, value]) => {
+ savedGlobals.set(key, Object.getOwnPropertyDescriptor(globalThis, key));
+ Object.defineProperty(globalThis, key, {
+ configurable: true,
+ writable: true,
+ value,
+ });
+ });
+
+ const rect = (left, top, width, height) => ({
+ x: left,
+ y: top,
+ left,
+ top,
+ width,
+ height,
+ right: left + width,
+ bottom: top + height,
+ toJSON() {
+ return { left, top, width, height };
+ },
+ });
+ const measuredRect = (element) => {
+ if (element.classList?.contains('ppt-export-root')
+ || element.classList?.contains('ppt-export-body')) {
+ return rect(0, 0, 1280, 720);
+ }
+ const style = element.style || {};
+ const order = Number(element.dataset?.layoutOrder || 0);
+ return rect(
+ parseFloat(style.left) || 40,
+ parseFloat(style.top) || (40 + order * 70),
+ parseFloat(style.width) || 480,
+ parseFloat(style.height) || 48,
+ );
+ };
+ const elementPrototype = window.HTMLElement.prototype;
+ const svgPrototype = window.SVGElement.prototype;
+ const originalHtmlRect = elementPrototype.getBoundingClientRect;
+ const originalSvgRect = svgPrototype.getBoundingClientRect;
+ elementPrototype.getBoundingClientRect = function getBoundingClientRect() {
+ return measuredRect(this);
+ };
+ svgPrototype.getBoundingClientRect = function getBoundingClientRect() {
+ return measuredRect(this);
+ };
+ const prototypeDescriptors = {
+ offsetWidth: Object.getOwnPropertyDescriptor(elementPrototype, 'offsetWidth'),
+ offsetHeight: Object.getOwnPropertyDescriptor(elementPrototype, 'offsetHeight'),
+ scrollWidth: Object.getOwnPropertyDescriptor(elementPrototype, 'scrollWidth'),
+ scrollHeight: Object.getOwnPropertyDescriptor(elementPrototype, 'scrollHeight'),
+ };
+ Object.defineProperties(elementPrototype, {
+ offsetWidth: { configurable: true, get() { return measuredRect(this).width; } },
+ offsetHeight: { configurable: true, get() { return measuredRect(this).height; } },
+ scrollWidth: { configurable: true, get() { return measuredRect(this).width; } },
+ scrollHeight: { configurable: true, get() { return measuredRect(this).height; } },
+ });
+ const originalCreateRange = document.createRange.bind(document);
+ document.createRange = () => ({
+ element: null,
+ selectNodeContents(element) {
+ this.element = element;
+ },
+ getBoundingClientRect() {
+ return this.element ? measuredRect(this.element) : rect(0, 0, 0, 0);
+ },
+ detach() {},
+ });
+
+ try {
+ return await run({ window, document });
+ } finally {
+ document.createRange = originalCreateRange;
+ elementPrototype.getBoundingClientRect = originalHtmlRect;
+ svgPrototype.getBoundingClientRect = originalSvgRect;
+ Object.entries(prototypeDescriptors).forEach(([key, descriptor]) => {
+ if (descriptor) Object.defineProperty(elementPrototype, key, descriptor);
+ else delete elementPrototype[key];
+ });
+ for (const [key, descriptor] of savedGlobals) {
+ if (descriptor) Object.defineProperty(globalThis, key, descriptor);
+ else delete globalThis[key];
+ }
+ window.close();
+ }
+}
+
+test('DOM preparation main path exports editable OOXML plus local visual fallback', async () => {
+ await withControllableExportDom(async () => {
+ const [deckExportModule, slideExportModule, elementModelModule] = await Promise.all([
+ import('../src/export-deck-browser.js'),
+ import('../src/export-slide-browser.js'),
+ import('../src/element-model-html.js'),
+ ]);
+ const { exportPptxPrepared } = deckExportModule;
+ const { prepareSlidesForPptxExport } = slideExportModule;
+ assert.equal(slideExportModule.buildElementSlideHtml, elementModelModule.buildElementSlideHtml);
+ const html = `
+
+ • First prepared bullet
+ • Second prepared bullet
+ Editable mixed text
+
+
Merged prepared first
Merged prepared second
+
+
+ | Prepared header | Prepared cell |
+
+
+
+
+
+
+
+ Foreground prepared text
+ `;
+ const deck = {
+ title: 'Prepared DOM integration',
+ slides: [{ id: 'slide-dom', title: 'Prepared DOM integration', html }],
+ };
+ const rasterPhases = [];
+
+ const prepared = await prepareSlidesForPptxExport(deck.slides, {
+ renderRaster: async (_rasterHtml, _slideIndex, metadata) => {
+ rasterPhases.push(metadata.phase);
+ return PNG_1X1.replace(/^data:image\/png;base64,/, '');
+ },
+ });
+
+ assert.equal(prepared.length, 1);
+ assert.ok(rasterPhases.length >= 3);
+ assert.ok(rasterPhases.every((phase) => phase === 'local-visual'));
+ assert.ok(prepared[0].slideData.elements.some((item) => item.type === 'list'));
+ assert.ok(prepared[0].slideData.elements.some((item) => item.type === 'shape'));
+ assert.ok(prepared[0].slideData.elements.some((item) => item.type === 'image'));
+ assert.ok(prepared[0].slideData.fallbackLayers.some((item) => item.sourceId === 'gradient-card'));
+ assert.equal(prepared[0].slideData.fullPageFallback, null);
+
+ const exported = await exportPptxPrepared(deck, prepared);
+ const pptxBuffer = Buffer.from(exported.base64, 'base64');
+ assert.ok(Buffer.isBuffer(pptxBuffer) && pptxBuffer.length > 0);
+ const zip = await JSZip.loadAsync(pptxBuffer);
+ const [slideXml, relsXml] = await Promise.all([
+ zipText(zip, 'ppt/slides/slide1.xml'),
+ zipText(zip, 'ppt/slides/_rels/slide1.xml.rels'),
+ ]);
+ const objects = topLevelSlideObjects(slideXml);
+
+ for (const text of [
+ 'First prepared bullet',
+ 'Second ',
+ 'prepared',
+ ' bullet',
+ 'Editable ',
+ 'mixed',
+ ' text',
+ 'Merged prepared first',
+ 'Merged prepared second',
+ 'Prepared header',
+ 'Prepared cell',
+ 'Prepared SVG text',
+ 'Prepared complex SVG label',
+ 'Foreground prepared text',
+ ]) {
+ assert.match(slideXml, new RegExp(`${text}`), text);
+ }
+ assert.match(slideXml, //);
+ assert.match(slideXml, //g) || []).length >= 4);
+ assert.ok((slideXml.match(//g) || []).length >= 4);
+ assert.ok(relatedMediaPaths(relsXml).length >= 4);
+ assert.ok(objects.some((item) => item.type === 'pic'));
+ assert.ok(objects.findIndex((item) => item.xml.includes('Foreground prepared text'))
+ > objects.map((item) => item.type).lastIndexOf('pic'));
+ assert.ok(!(objects.length === 1 && objects[0].type === 'pic'));
+ });
+});
+
+test('prepare main path records security repairs and escalates unsafe geometry to page visual fallback', async () => {
+ await withControllableExportDom(async () => {
+ const { prepareSlidesForPptxExport } = await import('../src/export-slide-browser.js');
+ const rendered = [];
+ const html = `
+
+
+
+ Overflow
+ `;
+ const prepared = await prepareSlidesForPptxExport([{ id: 'unsafe', html }], {
+ renderRaster: async (rasterHtml, _index, metadata) => {
+ rendered.push({ rasterHtml, metadata });
+ return PNG_1X1.replace(/^data:image\/png;base64,/, '');
+ },
+ });
+
+ assert.ok(prepared[0].diagnostics.some((item) => (
+ item.code === 'active_content_removed' && item.severity === 'repaired'
+ )));
+ assert.ok(prepared[0].diagnostics.some((item) => item.code === 'text_out_of_bounds'));
+ assert.equal(rendered[0].metadata.phase, 'page-visual');
+ assert.doesNotMatch(rendered[0].rasterHtml, /