Merge branch 'feat/custom_schema_and_layout' of github.com:presenton/presenton into feat/custom_schema_and_layout
This commit is contained in:
commit
5faf61d70f
16 changed files with 722 additions and 27 deletions
|
|
@ -60,7 +60,7 @@ async def stream_outlines(presentation_id: str):
|
|||
sql_session.refresh(presentation)
|
||||
|
||||
yield SSECompleteResponse(
|
||||
key="presentation", value=presentation.model_dump_json()
|
||||
key="presentation", value=presentation.model_dump(mode="json")
|
||||
).to_string()
|
||||
|
||||
return StreamingResponse(inner(), media_type="text/event-stream")
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ from fastapi import APIRouter, Body, HTTPException
|
|||
from fastapi.responses import StreamingResponse
|
||||
from sqlalchemy import delete
|
||||
from sqlmodel import select
|
||||
from models.presentation_outline_model import PresentationOutlineModel, SlideOutlineModel
|
||||
from models.presentation_outline_model import (
|
||||
PresentationOutlineModel,
|
||||
SlideOutlineModel,
|
||||
)
|
||||
from models.pptx_models import PptxPresentationModel
|
||||
from models.presentation_layout import PresentationLayoutModel
|
||||
from models.presentation_structure_model import PresentationStructureModel
|
||||
|
|
@ -304,8 +307,11 @@ async def create_pptx(pptx_model: Annotated[PptxPresentationModel, Body()]):
|
|||
|
||||
return pptx_path
|
||||
|
||||
|
||||
@PRESENTATION_ROUTER.post("/generate")
|
||||
async def generate_presentation_api(data: Annotated[GeneratePresentationRequest, Body()]):
|
||||
async def generate_presentation_api(
|
||||
data: Annotated[GeneratePresentationRequest, Body()],
|
||||
):
|
||||
presentation_id = str(uuid.uuid4())
|
||||
print("**" * 40)
|
||||
print(f"Generating presentation with ID: {presentation_id}")
|
||||
|
|
@ -341,7 +347,7 @@ async def generate_presentation_api(data: Annotated[GeneratePresentationRequest,
|
|||
|
||||
presentation_content_json = json.loads(presentation_content_text)
|
||||
presentation_content = PresentationOutlineModel(**presentation_content_json)
|
||||
outlines = presentation_content.slides[:data.n_slides]
|
||||
outlines = presentation_content.slides[: data.n_slides]
|
||||
total_outlines = len(outlines)
|
||||
|
||||
print("-" * 40)
|
||||
|
|
@ -441,7 +447,10 @@ async def generate_presentation_api(data: Annotated[GeneratePresentationRequest,
|
|||
if response.status != 200:
|
||||
error_text = await response.text()
|
||||
print(f"Failed to get PPTX model: {error_text}")
|
||||
raise HTTPException(status_code=500, detail="Failed to convert presentation to PPTX model")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail="Failed to convert presentation to PPTX model",
|
||||
)
|
||||
pptx_model_data = await response.json()
|
||||
print(f"Received PPTX model data: {json.dumps(pptx_model_data, indent=2)}")
|
||||
|
||||
|
|
@ -453,9 +462,7 @@ async def generate_presentation_api(data: Annotated[GeneratePresentationRequest,
|
|||
await pptx_creator.create_ppt()
|
||||
|
||||
export_directory = get_exports_directory()
|
||||
pptx_path = os.path.join(
|
||||
export_directory, f"{presentation_content.title}.pptx"
|
||||
)
|
||||
pptx_path = os.path.join(export_directory, f"{presentation_content.title}.pptx")
|
||||
pptx_creator.save(pptx_path)
|
||||
|
||||
presentation_and_path = PresentationAndPath(
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@ class PptxPictureBoxModel(PptxShapeModel):
|
|||
position: PptxPositionModel
|
||||
margin: Optional[PptxSpacingModel] = None
|
||||
clip: bool = True
|
||||
opacity: Optional[float] = None
|
||||
overlay: Optional[str] = None
|
||||
border_radius: Optional[List[int]] = None
|
||||
shape: Optional[PptxBoxShapeEnum] = None
|
||||
|
|
|
|||
|
|
@ -12,3 +12,4 @@ class SlideModel(SQLModel, table=True):
|
|||
index: int
|
||||
content: dict = Field(sa_column=Column(JSON))
|
||||
html_content: Optional[str]
|
||||
properties: Optional[dict] = Field(sa_column=Column(JSON))
|
||||
|
|
|
|||
|
|
@ -20,6 +20,15 @@ class SSEStatusResponse(BaseModel):
|
|||
).to_string()
|
||||
|
||||
|
||||
class SSEErrorResponse(BaseModel):
|
||||
detail: str
|
||||
|
||||
def to_string(self):
|
||||
return SSEResponse(
|
||||
event="response", data=json.dumps({"type": "error", "detail": self.detail})
|
||||
).to_string()
|
||||
|
||||
|
||||
class SSECompleteResponse(BaseModel):
|
||||
key: str
|
||||
value: object
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ from utils.image_utils import (
|
|||
create_circle_image,
|
||||
fit_image,
|
||||
round_image_corners,
|
||||
set_image_opacity,
|
||||
)
|
||||
|
||||
BLANK_SLIDE_LAYOUT = 6
|
||||
|
|
@ -174,6 +175,7 @@ class PptxPresentationCreator:
|
|||
picture_model.clip
|
||||
or picture_model.border_radius
|
||||
or picture_model.overlay
|
||||
or picture_model.opacity
|
||||
or picture_model.object_fit
|
||||
or picture_model.shape
|
||||
):
|
||||
|
|
@ -206,6 +208,8 @@ class PptxPresentationCreator:
|
|||
image = create_circle_image(image)
|
||||
if picture_model.overlay:
|
||||
image = change_image_color(image, picture_model.overlay)
|
||||
if picture_model.opacity:
|
||||
image = set_image_opacity(image, picture_model.opacity)
|
||||
image_path = os.path.join(self._temp_dir, f"{str(uuid.uuid4())}.png")
|
||||
image.save(image_path)
|
||||
|
||||
|
|
|
|||
1
servers/fastapi/utils/error_handling.py
Normal file
1
servers/fastapi/utils/error_handling.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
# async def format_exc(e: Exception):
|
||||
|
|
@ -182,6 +182,28 @@ def create_circle_image(
|
|||
return result
|
||||
|
||||
|
||||
def set_image_opacity(image: Image.Image, opacity: float) -> Image.Image:
|
||||
# Clamp opacity to valid range
|
||||
opacity = max(0.0, min(1.0, opacity))
|
||||
|
||||
# Convert to RGBA if not already
|
||||
if image.mode != "RGBA":
|
||||
image = image.convert("RGBA")
|
||||
|
||||
# Get the original alpha channel
|
||||
original_alpha = image.getchannel("A")
|
||||
|
||||
# Create new alpha channel with adjusted opacity
|
||||
new_alpha = original_alpha.point(lambda x: int(x * opacity))
|
||||
|
||||
# Create new image with modified alpha channel
|
||||
result = Image.new("RGBA", image.size)
|
||||
result.paste(image.convert("RGB"), (0, 0))
|
||||
result.putalpha(new_alpha)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def fit_image(
|
||||
image: Image.Image, width: int, height: int, object_fit: PptxObjectFitModel
|
||||
) -> Image.Image:
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export async function POST(req: NextRequest) {
|
|||
const page = await browser.newPage();
|
||||
await page.setViewport({ width: 1280, height: 720 });
|
||||
|
||||
await page.goto(`http://localhost/pdf-maker?id=${id}`, { waitUntil: 'networkidle0', timeout: 80000 });
|
||||
await page.goto(`http://localhost/pdf-maker?id=${id}`, { waitUntil: 'networkidle0', timeout: 180000 });
|
||||
|
||||
await page.waitForFunction('() => document.readyState === "complete"')
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { PptxPresentationModel } from "@/types/pptx_models";
|
|||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import sharp from "sharp";
|
||||
|
||||
interface GetAllChildElementsAttributesArgs {
|
||||
element: ElementHandle<Element>;
|
||||
|
|
@ -16,6 +17,7 @@ interface GetAllChildElementsAttributesArgs {
|
|||
inheritedBackground?: ElementAttributes['background'];
|
||||
inheritedBorderRadius?: number[];
|
||||
inheritedZIndex?: number;
|
||||
inheritedOpacity?: number;
|
||||
screenshotsDir: string;
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +77,7 @@ async function getBrowserAndPage(id: string): Promise<[Browser, Page]> {
|
|||
await page.setViewport({ width: 1280, height: 720, deviceScaleFactor: 1 });
|
||||
await page.goto(`http://localhost/pdf-maker?id=${id}`, {
|
||||
waitUntil: "networkidle0",
|
||||
timeout: 60000,
|
||||
timeout: 180000,
|
||||
});
|
||||
return [browser, page];
|
||||
}
|
||||
|
|
@ -115,6 +117,13 @@ async function postProcessSlidesAttributes(slidesAttributes: SlideAttributesResu
|
|||
async function screenshotElement(element: ElementAttributes, screenshotsDir: string) {
|
||||
const screenshotPath = path.join(screenshotsDir, `${uuidv4()}.png`) as `${string}.png`;
|
||||
|
||||
// For SVG elements, use convertSvgToPng
|
||||
if (element.tagName === 'svg') {
|
||||
const pngBuffer = await convertSvgToPng(element);
|
||||
fs.writeFileSync(screenshotPath, pngBuffer);
|
||||
return screenshotPath;
|
||||
}
|
||||
|
||||
// Hide all elements except the target element and its ancestors
|
||||
await element.element?.evaluate((el) => {
|
||||
const originalOpacities = new Map();
|
||||
|
|
@ -137,12 +146,12 @@ async function screenshotElement(element: ElementAttributes, screenshotsDir: str
|
|||
|
||||
hideAllExcept(el);
|
||||
|
||||
(el as any).__restoreOpacities = () => {
|
||||
(el as any).__restoreStyles = () => {
|
||||
originalOpacities.forEach((opacity, elem) => {
|
||||
(elem as HTMLElement).style.opacity = opacity;
|
||||
});
|
||||
};
|
||||
});
|
||||
}, element.opacity, element.font?.color);
|
||||
|
||||
const screenshot = await element.element?.screenshot({ path: screenshotPath });
|
||||
if (!screenshot) {
|
||||
|
|
@ -150,14 +159,32 @@ async function screenshotElement(element: ElementAttributes, screenshotsDir: str
|
|||
}
|
||||
|
||||
await element.element?.evaluate((el) => {
|
||||
if ((el as any).__restoreOpacities) {
|
||||
(el as any).__restoreOpacities();
|
||||
if ((el as any).__restoreStyles) {
|
||||
(el as any).__restoreStyles();
|
||||
}
|
||||
});
|
||||
|
||||
return screenshotPath;
|
||||
}
|
||||
|
||||
const convertSvgToPng = async (element_attibutes: ElementAttributes) => {
|
||||
const svgHtml = await element_attibutes.element?.evaluate((el) => {
|
||||
|
||||
// Apply font color
|
||||
const fontColor = window.getComputedStyle(el).color;
|
||||
(el as HTMLElement).style.color = fontColor;
|
||||
|
||||
return el.outerHTML;
|
||||
}) || '';
|
||||
|
||||
const svgBuffer = Buffer.from(svgHtml);
|
||||
const pngBuffer = await sharp(svgBuffer)
|
||||
.resize(Math.round(element_attibutes.position?.width ?? 10), Math.round(element_attibutes.position?.height ?? 10))
|
||||
.toFormat('png')
|
||||
.toBuffer();
|
||||
return pngBuffer;
|
||||
}
|
||||
|
||||
|
||||
async function getSlidesAttributes(slides: ElementHandle<Element>[], screenshotsDir: string): Promise<SlideAttributesResult[]> {
|
||||
const slideAttributes = await Promise.all(
|
||||
|
|
@ -182,12 +209,13 @@ async function getSlidesWrapper(page: Page): Promise<ElementHandle<Element>> {
|
|||
return slides_wrapper;
|
||||
}
|
||||
|
||||
async function getAllChildElementsAttributes({ element, rootRect = null, depth = 0, inheritedFont, inheritedBackground, inheritedBorderRadius, inheritedZIndex, screenshotsDir }: GetAllChildElementsAttributesArgs): Promise<SlideAttributesResult> {
|
||||
async function getAllChildElementsAttributes({ element, rootRect = null, depth = 0, inheritedFont, inheritedBackground, inheritedBorderRadius, inheritedZIndex, inheritedOpacity, screenshotsDir }: GetAllChildElementsAttributesArgs): Promise<SlideAttributesResult> {
|
||||
if (!rootRect) {
|
||||
const rootAttributes = await getElementAttributes(element);
|
||||
inheritedFont = rootAttributes.font;
|
||||
inheritedBackground = rootAttributes.background;
|
||||
inheritedZIndex = rootAttributes.zIndex;
|
||||
inheritedOpacity = rootAttributes.opacity;
|
||||
rootRect = {
|
||||
left: rootAttributes.position?.left ?? 0,
|
||||
top: rootAttributes.position?.top ?? 0,
|
||||
|
|
@ -220,6 +248,9 @@ async function getAllChildElementsAttributes({ element, rootRect = null, depth =
|
|||
if (inheritedZIndex !== undefined && attributes.zIndex === 0) {
|
||||
attributes.zIndex = inheritedZIndex;
|
||||
}
|
||||
if (inheritedOpacity !== undefined && (attributes.opacity === undefined || attributes.opacity === 1)) {
|
||||
attributes.opacity = inheritedOpacity;
|
||||
}
|
||||
|
||||
if (attributes.position && attributes.position.left !== undefined && attributes.position.top !== undefined) {
|
||||
attributes.position = {
|
||||
|
|
@ -251,6 +282,7 @@ async function getAllChildElementsAttributes({ element, rootRect = null, depth =
|
|||
inheritedBackground: attributes.background || inheritedBackground,
|
||||
inheritedBorderRadius: attributes.borderRadius || inheritedBorderRadius,
|
||||
inheritedZIndex: attributes.zIndex || inheritedZIndex,
|
||||
inheritedOpacity: attributes.opacity || inheritedOpacity,
|
||||
screenshotsDir,
|
||||
});
|
||||
allResults.push(...childResults.elements.map(attr => ({ attributes: attr, depth: depth + 1 })));
|
||||
|
|
@ -284,14 +316,16 @@ async function getAllChildElementsAttributes({ element, rootRect = null, depth =
|
|||
const isCanvas = attributes.tagName === 'canvas';
|
||||
const isTable = attributes.tagName === 'table';
|
||||
|
||||
const isRootPosition = attributes.position &&
|
||||
const occupiesRoot = attributes.position &&
|
||||
attributes.position.left === 0 &&
|
||||
attributes.position.top === 0 &&
|
||||
attributes.position.width === rootRect!.width &&
|
||||
attributes.position.height === rootRect!.height;
|
||||
|
||||
const hasOtherProperties = hasBackground || hasBorder || hasShadow || hasText || hasImage || isSvg || isCanvas || isTable;
|
||||
return hasOtherProperties && !isRootPosition;
|
||||
const hasVisualProperties = hasBackground || hasBorder || hasShadow || hasText;
|
||||
const hasSpecialContent = hasImage || isSvg || isCanvas || isTable;
|
||||
|
||||
return (hasVisualProperties && !occupiesRoot) || hasSpecialContent;
|
||||
}) : allResults;
|
||||
|
||||
if (depth === 0) {
|
||||
|
|
@ -430,6 +464,23 @@ async function getElementAttributes(element: ElementHandle<Element>): Promise<El
|
|||
return background;
|
||||
}
|
||||
|
||||
function parseBackgroundImage(computedStyles: CSSStyleDeclaration) {
|
||||
const backgroundImage = computedStyles.backgroundImage;
|
||||
|
||||
|
||||
if (!backgroundImage || backgroundImage === 'none') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Extract URL from background-image style
|
||||
const urlMatch = backgroundImage.match(/url\(['"]?([^'"]+)['"]?\)/);
|
||||
if (urlMatch && urlMatch[1]) {
|
||||
return urlMatch[1];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function parseBorder(computedStyles: CSSStyleDeclaration) {
|
||||
const borderColorResult = colorToHex(computedStyles.borderColor);
|
||||
const borderWidth = parseFloat(computedStyles.borderWidth);
|
||||
|
|
@ -783,7 +834,75 @@ async function getElementAttributes(element: ElementHandle<Element>): Promise<El
|
|||
return undefined;
|
||||
}
|
||||
|
||||
function parseFilters(computedStyles: CSSStyleDeclaration) {
|
||||
const filter = computedStyles.filter;
|
||||
if (!filter || filter === 'none') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const filters: {
|
||||
invert?: number;
|
||||
brightness?: number;
|
||||
contrast?: number;
|
||||
saturate?: number;
|
||||
hueRotate?: number;
|
||||
blur?: number;
|
||||
grayscale?: number;
|
||||
sepia?: number;
|
||||
opacity?: number;
|
||||
} = {};
|
||||
|
||||
// Parse filter functions
|
||||
const filterFunctions = filter.match(/[a-zA-Z]+\([^)]*\)/g);
|
||||
if (filterFunctions) {
|
||||
filterFunctions.forEach(func => {
|
||||
const match = func.match(/([a-zA-Z]+)\(([^)]*)\)/);
|
||||
if (match) {
|
||||
const filterType = match[1];
|
||||
const value = parseFloat(match[2]);
|
||||
|
||||
if (!isNaN(value)) {
|
||||
switch (filterType) {
|
||||
case 'invert':
|
||||
filters.invert = value;
|
||||
break;
|
||||
case 'brightness':
|
||||
filters.brightness = value;
|
||||
break;
|
||||
case 'contrast':
|
||||
filters.contrast = value;
|
||||
break;
|
||||
case 'saturate':
|
||||
filters.saturate = value;
|
||||
break;
|
||||
case 'hue-rotate':
|
||||
filters.hueRotate = value;
|
||||
break;
|
||||
case 'blur':
|
||||
filters.blur = value;
|
||||
break;
|
||||
case 'grayscale':
|
||||
filters.grayscale = value;
|
||||
break;
|
||||
case 'sepia':
|
||||
filters.sepia = value;
|
||||
break;
|
||||
case 'opacity':
|
||||
filters.opacity = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Return undefined if no filters were parsed
|
||||
return Object.keys(filters).length > 0 ? filters : undefined;
|
||||
}
|
||||
|
||||
function parseElementAttributes(el: Element) {
|
||||
let tagName = el.tagName.toLowerCase();
|
||||
|
||||
const computedStyles = window.getComputedStyle(el);
|
||||
|
||||
const position = parsePosition(el);
|
||||
|
|
@ -809,7 +928,9 @@ async function getElementAttributes(element: ElementHandle<Element>): Promise<El
|
|||
|
||||
const textAlign = computedStyles.textAlign as 'left' | 'center' | 'right' | 'justify';
|
||||
const objectFit = computedStyles.objectFit as 'contain' | 'cover' | 'fill' | undefined;
|
||||
const imageSrc = (el as HTMLImageElement).src;
|
||||
|
||||
const parsedBackgroundImage = parseBackgroundImage(computedStyles);
|
||||
const imageSrc = (el as HTMLImageElement).src || parsedBackgroundImage;
|
||||
|
||||
const borderRadiusValue = parseBorderRadius(computedStyles, el);
|
||||
|
||||
|
|
@ -817,11 +938,17 @@ async function getElementAttributes(element: ElementHandle<Element>): Promise<El
|
|||
|
||||
const textWrap = computedStyles.whiteSpace !== 'nowrap';
|
||||
|
||||
const filters = parseFilters(computedStyles);
|
||||
|
||||
const opacity = parseFloat(computedStyles.opacity);
|
||||
const elementOpacity = isNaN(opacity) ? undefined : opacity;
|
||||
|
||||
return {
|
||||
tagName: el.tagName.toLowerCase(),
|
||||
tagName: tagName,
|
||||
id: el.id,
|
||||
className: (el.className && typeof el.className === 'string') ? el.className : (el.className ? el.className.toString() : undefined),
|
||||
innerText: innerText,
|
||||
opacity: elementOpacity,
|
||||
background: background,
|
||||
border: border,
|
||||
shadow: shadow,
|
||||
|
|
@ -842,6 +969,7 @@ async function getElementAttributes(element: ElementHandle<Element>): Promise<El
|
|||
textWrap: textWrap,
|
||||
should_screenshot: false,
|
||||
element: undefined,
|
||||
filters: filters,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
499
servers/nextjs/package-lock.json
generated
499
servers/nextjs/package-lock.json
generated
|
|
@ -35,6 +35,7 @@
|
|||
"@tiptap/extension-underline": "^2.0.0",
|
||||
"@tiptap/react": "^2.11.5",
|
||||
"@tiptap/starter-kit": "^2.11.5",
|
||||
"@types/sharp": "^0.32.0",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.0.0",
|
||||
|
|
@ -48,6 +49,7 @@
|
|||
"react-dom": "^18",
|
||||
"react-redux": "^9.1.2",
|
||||
"recharts": "^2.15.4",
|
||||
"sharp": "^0.34.3",
|
||||
"sonner": "^2.0.6",
|
||||
"tailwind-merge": "^2.5.3",
|
||||
"tailwind-scrollbar-hide": "^2.0.0",
|
||||
|
|
@ -229,6 +231,15 @@
|
|||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "1.4.5",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz",
|
||||
"integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.2.tgz",
|
||||
|
|
@ -267,6 +278,402 @@
|
|||
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-arm64": {
|
||||
"version": "0.34.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz",
|
||||
"integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-arm64": "1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-x64": {
|
||||
"version": "0.34.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz",
|
||||
"integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-x64": "1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-darwin-arm64": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz",
|
||||
"integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-darwin-x64": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz",
|
||||
"integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-arm": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz",
|
||||
"integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-arm64": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz",
|
||||
"integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-ppc64": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz",
|
||||
"integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-s390x": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz",
|
||||
"integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-x64": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz",
|
||||
"integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz",
|
||||
"integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz",
|
||||
"integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-arm": {
|
||||
"version": "0.34.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz",
|
||||
"integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm": "1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-arm64": {
|
||||
"version": "0.34.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz",
|
||||
"integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm64": "1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-ppc64": {
|
||||
"version": "0.34.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz",
|
||||
"integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-ppc64": "1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-s390x": {
|
||||
"version": "0.34.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz",
|
||||
"integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-s390x": "1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-x64": {
|
||||
"version": "0.34.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz",
|
||||
"integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-x64": "1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linuxmusl-arm64": {
|
||||
"version": "0.34.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz",
|
||||
"integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linuxmusl-x64": {
|
||||
"version": "0.34.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz",
|
||||
"integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-wasm32": {
|
||||
"version": "0.34.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz",
|
||||
"integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==",
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/runtime": "^1.4.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-arm64": {
|
||||
"version": "0.34.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz",
|
||||
"integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-ia32": {
|
||||
"version": "0.34.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz",
|
||||
"integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-x64": {
|
||||
"version": "0.34.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz",
|
||||
"integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||
|
|
@ -2369,6 +2776,15 @@
|
|||
"@types/react": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/sharp": {
|
||||
"version": "0.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/sharp/-/sharp-0.32.0.tgz",
|
||||
"integrity": "sha512-OOi3kL+FZDnPhVzsfD37J88FNeZh6gQsGcLc95NbeURRGvmSjeXiDcyWzF2o3yh/gQAUn2uhh/e+CPCa5nwAxw==",
|
||||
"deprecated": "This is a stub types definition. sharp provides its own type definitions, so you do not need this installed.",
|
||||
"dependencies": {
|
||||
"sharp": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/sinonjs__fake-timers": {
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz",
|
||||
|
|
@ -3176,6 +3592,18 @@
|
|||
"react-dom": "^18 || ^19 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/color": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
|
||||
"integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1",
|
||||
"color-string": "^1.9.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
|
|
@ -3194,6 +3622,15 @@
|
|||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/color-string": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
|
||||
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
|
||||
"dependencies": {
|
||||
"color-name": "^1.0.0",
|
||||
"simple-swizzle": "^0.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/colorette": {
|
||||
"version": "2.0.20",
|
||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
|
||||
|
|
@ -3573,6 +4010,14 @@
|
|||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
|
||||
"integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-node-es": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
|
||||
|
|
@ -6342,6 +6787,47 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/sharp": {
|
||||
"version": "0.34.3",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz",
|
||||
"integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"color": "^4.2.3",
|
||||
"detect-libc": "^2.0.4",
|
||||
"semver": "^7.7.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-darwin-arm64": "0.34.3",
|
||||
"@img/sharp-darwin-x64": "0.34.3",
|
||||
"@img/sharp-libvips-darwin-arm64": "1.2.0",
|
||||
"@img/sharp-libvips-darwin-x64": "1.2.0",
|
||||
"@img/sharp-libvips-linux-arm": "1.2.0",
|
||||
"@img/sharp-libvips-linux-arm64": "1.2.0",
|
||||
"@img/sharp-libvips-linux-ppc64": "1.2.0",
|
||||
"@img/sharp-libvips-linux-s390x": "1.2.0",
|
||||
"@img/sharp-libvips-linux-x64": "1.2.0",
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.2.0",
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.2.0",
|
||||
"@img/sharp-linux-arm": "0.34.3",
|
||||
"@img/sharp-linux-arm64": "0.34.3",
|
||||
"@img/sharp-linux-ppc64": "0.34.3",
|
||||
"@img/sharp-linux-s390x": "0.34.3",
|
||||
"@img/sharp-linux-x64": "0.34.3",
|
||||
"@img/sharp-linuxmusl-arm64": "0.34.3",
|
||||
"@img/sharp-linuxmusl-x64": "0.34.3",
|
||||
"@img/sharp-wasm32": "0.34.3",
|
||||
"@img/sharp-win32-arm64": "0.34.3",
|
||||
"@img/sharp-win32-ia32": "0.34.3",
|
||||
"@img/sharp-win32-x64": "0.34.3"
|
||||
}
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
|
|
@ -6446,6 +6932,19 @@
|
|||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/simple-swizzle": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
|
||||
"dependencies": {
|
||||
"is-arrayish": "^0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-swizzle/node_modules/is-arrayish": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
||||
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
|
||||
},
|
||||
"node_modules/slice-ansi": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
"@tiptap/extension-underline": "^2.0.0",
|
||||
"@tiptap/react": "^2.11.5",
|
||||
"@tiptap/starter-kit": "^2.11.5",
|
||||
"@types/sharp": "^0.32.0",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.0.0",
|
||||
|
|
@ -50,6 +51,7 @@
|
|||
"react-dom": "^18",
|
||||
"react-redux": "^9.1.2",
|
||||
"recharts": "^2.15.4",
|
||||
"sharp": "^0.34.3",
|
||||
"sonner": "^2.0.6",
|
||||
"tailwind-merge": "^2.5.3",
|
||||
"tailwind-scrollbar-hide": "^2.0.0",
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ const QuoteSlideLayout: React.FC<QuoteSlideLayoutProps> = ({ data: slideData })
|
|||
}}
|
||||
>
|
||||
{/* Background Image */}
|
||||
<div
|
||||
<div
|
||||
className="absolute inset-0 w-full h-full bg-cover bg-center bg-no-repeat"
|
||||
style={{
|
||||
backgroundImage: `url('${slideData?.backgroundImage?.__image_url__ || ''}')`,
|
||||
|
|
@ -66,7 +66,7 @@ const QuoteSlideLayout: React.FC<QuoteSlideLayoutProps> = ({ data: slideData })
|
|||
{/* Main Content */}
|
||||
<div className="relative z-10 px-8 sm:px-12 lg:px-20 py-12 flex-1 flex flex-col justify-center h-full">
|
||||
<div className="text-center space-y-8 max-w-4xl mx-auto">
|
||||
|
||||
|
||||
{/* Heading */}
|
||||
<div className="space-y-4">
|
||||
<h1 className="text-3xl sm:text-4xl lg:text-5xl font-bold text-white leading-tight">
|
||||
|
|
@ -80,12 +80,12 @@ const QuoteSlideLayout: React.FC<QuoteSlideLayoutProps> = ({ data: slideData })
|
|||
<div className="space-y-6">
|
||||
{/* Quote Icon */}
|
||||
<div className="flex justify-center">
|
||||
<svg
|
||||
className="w-12 h-12 text-purple-300 opacity-80"
|
||||
fill="currentColor"
|
||||
<svg
|
||||
className="w-12 h-12 text-purple-300 opacity-80"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path d="M14.017 21v-7.391c0-5.704 3.731-9.57 8.983-10.609l.995 2.151c-2.432.917-3.995 3.638-3.995 5.849h4v10h-9.983zm-14.017 0v-7.391c0-5.704 3.748-9.57 9-10.609l.996 2.151c-2.433.917-3.996 3.638-3.996 5.849h3.983v10h-9.983z"/>
|
||||
<path d="M14.017 21v-7.391c0-5.704 3.731-9.57 8.983-10.609l.995 2.151c-2.432.917-3.995 3.638-3.995 5.849h4v10h-9.983zm-14.017 0v-7.391c0-5.704 3.748-9.57 9-10.609l.996 2.151c-2.433.917-3.996 3.638-3.996 5.849h3.983v10h-9.983z" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ export interface ElementAttributes {
|
|||
id?: string;
|
||||
className?: string;
|
||||
innerText?: string;
|
||||
opacity?: number;
|
||||
background?: {
|
||||
color?: string;
|
||||
opacity?: number;
|
||||
|
|
@ -61,6 +62,17 @@ export interface ElementAttributes {
|
|||
textWrap?: boolean;
|
||||
should_screenshot?: boolean;
|
||||
element?: ElementHandle<Element>;
|
||||
filters?: {
|
||||
invert?: number;
|
||||
brightness?: number;
|
||||
contrast?: number;
|
||||
saturate?: number;
|
||||
hueRotate?: number;
|
||||
blur?: number;
|
||||
grayscale?: number;
|
||||
sepia?: number;
|
||||
opacity?: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface SlideAttributesResult {
|
||||
|
|
|
|||
|
|
@ -305,6 +305,7 @@ export interface PptxPictureBoxModel extends PptxShapeModel {
|
|||
margin?: PptxSpacingModel;
|
||||
clip: boolean;
|
||||
overlay?: string;
|
||||
opacity?: number;
|
||||
border_radius?: number[];
|
||||
shape?: PptxBoxShapeEnum;
|
||||
object_fit?: PptxObjectFitModel;
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ export function convertElementAttributesToPptxSlides(
|
|||
function convertElementToPptxShape(
|
||||
element: ElementAttributes
|
||||
): PptxTextBoxModel | PptxAutoShapeBoxModel | PptxConnectorModel | PptxPictureBoxModel | null {
|
||||
|
||||
if (!element.position) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -218,11 +219,18 @@ function convertToPictureBox(element: ElementAttributes): PptxPictureBoxModel {
|
|||
path: element.imageSrc || ''
|
||||
};
|
||||
|
||||
// Set overlay to white if invert is 1 and brightness is 0
|
||||
let overlay = element.overlay;
|
||||
if (element.filters?.invert === 1 && element.filters?.brightness === 0) {
|
||||
overlay = 'FFFFFF';
|
||||
}
|
||||
|
||||
return {
|
||||
position,
|
||||
margin: undefined,
|
||||
clip: element.clip ?? true,
|
||||
overlay: element.overlay,
|
||||
overlay,
|
||||
opacity: element.opacity,
|
||||
border_radius: element.borderRadius ? element.borderRadius.map(r => Math.round(r)) : undefined,
|
||||
shape: element.shape ? (element.shape as PptxBoxShapeEnum) : PptxBoxShapeEnum.RECTANGLE,
|
||||
object_fit: objectFit,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue