yonge 5 дней назад
Родитель
Сommit
fc4219e9a6

+ 1 - 1
dist/assets/index-wWLZYJgM.js → dist/assets/index-B76AOBvW.js

@@ -1 +1 @@
-import{u as s,af as r}from"./index-CR2okBFo.js";import{a5 as c,cB as n,R as p,ae as t,af as _,al as u,ag as i,bH as f}from"./index-CbtUpGeg.js";const l={class:"pptScreen"},m=c({__name:"pptScreen",setup(d){const a=n(),e=p(!0),o=s();return a.setScreenMode("pptScreen"),o.initPPTData().then(()=>{e.value=!1}),(S,k)=>(t(),_("div",l,[e.value?i("",!0):(t(),u(r,{key:0}))]))}}),P=f(m,[["__scopeId","data-v-678905e9"]]);export{P as default};
+import{u as s,af as r}from"./index-CoIqepi0.js";import{a5 as c,cB as n,R as p,ae as t,af as _,al as u,ag as i,bH as f}from"./index-CEo0I-m1.js";const l={class:"pptScreen"},m=c({__name:"pptScreen",setup(d){const a=n(),e=p(!0),o=s();return a.setScreenMode("pptScreen"),o.initPPTData().then(()=>{e.value=!1}),(S,k)=>(t(),_("div",l,[e.value?i("",!0):(t(),u(r,{key:0}))]))}}),P=f(m,[["__scopeId","data-v-678905e9"]]);export{P as default};

+ 1 - 1
dist/assets/index-DXj4Hazp.js → dist/assets/index-BYsojs_a.js

@@ -1 +1 @@
-import{_ as t}from"./404-R0y5dkvZ.js";import{E as c}from"./index-BGwTPGiT.js";import{a5 as n,ae as i,af as r,ak as o,aq as _,am as d,P as l,aS as p,bM as m,bN as f,bH as u}from"./index-CbtUpGeg.js";const s=a=>(m("data-v-340bbdaa"),a=a(),f(),a),h={class:"login"},b={class:"error"},g=s(()=>o("img",{src:t,class:"img",alt:""},null,-1)),k=s(()=>o("div",{class:"tit"},"登录已过期或服务器错误!",-1)),v=n({__name:"login",setup(a){function e(){window.close()}return(x,B)=>(i(),r("div",h,[o("div",b,[g,k,_(l(c),{class:"backBtn",type:"primary",plain:"",onClick:e},{default:d(()=>[p("关闭页面")]),_:1})])]))}}),S=u(v,[["__scopeId","data-v-340bbdaa"]]);export{S as default};
+import{_ as t}from"./404-R0y5dkvZ.js";import{E as c}from"./index-ByoINUVj.js";import{a5 as n,ae as i,af as r,ak as o,aq as _,am as d,P as l,aS as p,bM as m,bN as f,bH as u}from"./index-CEo0I-m1.js";const s=a=>(m("data-v-340bbdaa"),a=a(),f(),a),h={class:"login"},b={class:"error"},g=s(()=>o("img",{src:t,class:"img",alt:""},null,-1)),k=s(()=>o("div",{class:"tit"},"登录已过期或服务器错误!",-1)),v=n({__name:"login",setup(a){function e(){window.close()}return(x,B)=>(i(),r("div",h,[o("div",b,[g,k,_(l(c),{class:"backBtn",type:"primary",plain:"",onClick:e},{default:d(()=>[p("关闭页面")]),_:1})])]))}}),S=u(v,[["__scopeId","data-v-340bbdaa"]]);export{S as default};

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
dist/assets/index-ByoINUVj.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
dist/assets/index-C7GKa4m2.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
dist/assets/index-CEo0I-m1.js


+ 1 - 1
dist/assets/index-CR2okBFo.js → dist/assets/index-CoIqepi0.js

@@ -1,4 +1,4 @@
-import{cF as dw,cG as hw,aG as vw,cH as pw,cI as gw,cJ as mw,cK as yw,ah as Yt,cL as Sw,cM as bw,cN as xw,cO as Cw,aM as ww,aF as Tw,cP as Iw,aB as c1,bg as _w,cQ as Aw,cR as Ew,cS as Pw,cT as Mw,cU as Rw,cV as Dw,cW as Lw,aE as kw,cX as Ow,s as Ae,bl as Bw,al as je,ag as ze,af as ve,ak as Z,cY as Nw,cZ as Fw,c_ as $w,c$ as zw,bk as Uw,cw as ul,aS as Cv,aq as Ye,d0 as Hw,d1 as Vw,a5 as $e,d2 as Gw,d3 as Ww,d4 as jw,d5 as Kw,d6 as Xw,d7 as Yw,d8 as Zw,d9 as Qw,da as Jw,db as qw,dc as eT,v as tT,dd as rT,de as nT,df as iT,aW as aT,dg as oT,dh as sT,di as lT,dj as uT,dk as cT,ay as sr,dl as fT,dm as dT,dn as hT,dp as vT,A as pT,dq as gT,dr as mT,bq as yT,ds as ST,dt as bT,du as xT,ap as CT,ac as f1,ai as dt,dv as wT,au as Ce,dw as TT,X as d1,y as IT,dx as _T,aP as AT,dy as ET,w as _r,dz as PT,dA as MT,H as RT,dB as DT,bA as hn,aD as LT,ae as ie,bN as ea,$ as wc,dC as kT,bM as ta,dD as OT,aC as BT,aO as NT,R as fe,dE as FT,br as rh,b3 as Lr,aj as Hs,b1 as lt,b2 as Ua,an as wv,dF as $T,dG as zT,dH as UT,dI as HT,dJ as VT,dK as GT,dL as WT,Q as jT,dM as KT,dN as XT,dO as YT,at as Mt,dP as ZT,aR as QT,aY as JT,ad as cl,aZ as qT,dQ as eI,dR as tI,dS as rI,P as le,a6 as nI,dT as iI,dU as aI,dV as oI,dW as sI,a7 as lI,dX as uI,dY as cI,dZ as fI,d_ as dI,d$ as hI,b4 as vI,aA as Tv,e0 as pI,e1 as gI,E as gr,x as mI,e2 as yI,e3 as SI,e4 as bI,am as _n,e5 as xI,az as hr,aU as nh,e6 as CI,as as jr,e7 as wI,e8 as Iv,cE as mr,by as ih,ct as ah,bI as TI,bH as Ve,bu as oh,bv as _v,bx as Wt,bB as Tc,bL as di,e9 as wu,bE as hi,ea as II,bJ as ra,bC as _I,bw as Bt,eb as AI,ec as ui,cu as EI,ed as PI,ee as MI,ef as RI,eg as DI,bK as Zp,eh as LI,bF as Qp,cB as Ic,cA as kI,ei as Hu,ej as Il}from"./index-CbtUpGeg.js";/**
+import{cF as dw,cG as hw,aG as vw,cH as pw,cI as gw,cJ as mw,cK as yw,ah as Yt,cL as Sw,cM as bw,cN as xw,cO as Cw,aM as ww,aF as Tw,cP as Iw,aB as c1,bg as _w,cQ as Aw,cR as Ew,cS as Pw,cT as Mw,cU as Rw,cV as Dw,cW as Lw,aE as kw,cX as Ow,s as Ae,bl as Bw,al as je,ag as ze,af as ve,ak as Z,cY as Nw,cZ as Fw,c_ as $w,c$ as zw,bk as Uw,cw as ul,aS as Cv,aq as Ye,d0 as Hw,d1 as Vw,a5 as $e,d2 as Gw,d3 as Ww,d4 as jw,d5 as Kw,d6 as Xw,d7 as Yw,d8 as Zw,d9 as Qw,da as Jw,db as qw,dc as eT,v as tT,dd as rT,de as nT,df as iT,aW as aT,dg as oT,dh as sT,di as lT,dj as uT,dk as cT,ay as sr,dl as fT,dm as dT,dn as hT,dp as vT,A as pT,dq as gT,dr as mT,bq as yT,ds as ST,dt as bT,du as xT,ap as CT,ac as f1,ai as dt,dv as wT,au as Ce,dw as TT,X as d1,y as IT,dx as _T,aP as AT,dy as ET,w as _r,dz as PT,dA as MT,H as RT,dB as DT,bA as hn,aD as LT,ae as ie,bN as ea,$ as wc,dC as kT,bM as ta,dD as OT,aC as BT,aO as NT,R as fe,dE as FT,br as rh,b3 as Lr,aj as Hs,b1 as lt,b2 as Ua,an as wv,dF as $T,dG as zT,dH as UT,dI as HT,dJ as VT,dK as GT,dL as WT,Q as jT,dM as KT,dN as XT,dO as YT,at as Mt,dP as ZT,aR as QT,aY as JT,ad as cl,aZ as qT,dQ as eI,dR as tI,dS as rI,P as le,a6 as nI,dT as iI,dU as aI,dV as oI,dW as sI,a7 as lI,dX as uI,dY as cI,dZ as fI,d_ as dI,d$ as hI,b4 as vI,aA as Tv,e0 as pI,e1 as gI,E as gr,x as mI,e2 as yI,e3 as SI,e4 as bI,am as _n,e5 as xI,az as hr,aU as nh,e6 as CI,as as jr,e7 as wI,e8 as Iv,cE as mr,by as ih,ct as ah,bI as TI,bH as Ve,bu as oh,bv as _v,bx as Wt,bB as Tc,bL as di,e9 as wu,bE as hi,ea as II,bJ as ra,bC as _I,bw as Bt,eb as AI,ec as ui,cu as EI,ed as PI,ee as MI,ef as RI,eg as DI,bK as Zp,eh as LI,bF as Qp,cB as Ic,cA as kI,ei as Hu,ej as Il}from"./index-CEo0I-m1.js";/**
 * vue v3.4.34
 * (c) 2018-present Yuxi (Evan) You and Vue contributors
 * @license MIT

+ 1 - 1
dist/assets/index-Doo0kRjl.js → dist/assets/index-CoKNU2ey.js

@@ -1 +1 @@
-import{u as c,af as l}from"./index-CR2okBFo.js";import{w as p,bA as d,a5 as g,cB as m,R as t,ae as r,af as u,al as f,ag as w,bH as k}from"./index-CbtUpGeg.js";import"./shapes-ZRVLueco.js";const S=()=>{function o(a){if(window.parent===window)return;const{api:e,playState:n}=a.data||{};e==="headerTogge"?window.parent.postMessage({api:"headerTogge",playState:n},"*"):e==="changeTogge"?window.parent.postMessage({api:"changeTogge"},"*"):["clickTempo","clickViewFigner"].includes(e)&&window.parent.postMessage({api:e},"*")}p(()=>{window.addEventListener("message",o)}),d(()=>{window.removeEventListener("message",o)})},_=g({__name:"mobileScreen",setup(o){S();const a=m(),e=t(!0),n=t(!1),i=c();return a.setScreenMode("mobileScreen"),i.initPPTData().then(()=>{e.value=!1}),(v,s)=>(r(),u("div",{class:"mobileScreen",onClick:s[0]||(s[0]=b=>n.value=!1)},[e.value?w("",!0):(r(),f(l,{key:0}))]))}}),B=k(_,[["__scopeId","data-v-90b2692a"]]);export{B as default};
+import{u as c,af as l}from"./index-CoIqepi0.js";import{w as p,bA as d,a5 as g,cB as m,R as t,ae as r,af as u,al as f,ag as w,bH as k}from"./index-CEo0I-m1.js";import"./shapes-DA7-XwCS.js";const S=()=>{function o(a){if(window.parent===window)return;const{api:e,playState:n}=a.data||{};e==="headerTogge"?window.parent.postMessage({api:"headerTogge",playState:n},"*"):e==="changeTogge"?window.parent.postMessage({api:"changeTogge"},"*"):["clickTempo","clickViewFigner"].includes(e)&&window.parent.postMessage({api:e},"*")}p(()=>{window.addEventListener("message",o)}),d(()=>{window.removeEventListener("message",o)})},_=g({__name:"mobileScreen",setup(o){S();const a=m(),e=t(!0),n=t(!1),i=c();return a.setScreenMode("mobileScreen"),i.initPPTData().then(()=>{e.value=!1}),(v,s)=>(r(),u("div",{class:"mobileScreen",onClick:s[0]||(s[0]=b=>n.value=!1)},[e.value?w("",!0):(r(),f(l,{key:0}))]))}}),B=k(_,[["__scopeId","data-v-90b2692a"]]);export{B as default};

+ 1 - 1
dist/assets/index-7z4RZoJG.js → dist/assets/index-DEXAsilW.js

@@ -1 +1 @@
-import{_ as a}from"./404-R0y5dkvZ.js";import{a5 as o,en as t,ae as r,af as c,bM as _,bN as d,ak as s,bH as p}from"./index-CbtUpGeg.js";const i=e=>(_("data-v-dda25179"),e=e(),d(),e),n={class:"errorPage"},m=i(()=>s("div",{class:"error"},[s("img",{src:a,class:"img",alt:""}),s("div",{class:"tit"},"页面找不到了~")],-1)),l=[m],f=o({__name:"errorPage",setup(e){return t(),(u,h)=>(r(),c("div",n,l))}}),I=p(f,[["__scopeId","data-v-dda25179"]]);export{I as default};
+import{_ as a}from"./404-R0y5dkvZ.js";import{a5 as o,en as t,ae as r,af as c,bM as _,bN as d,ak as s,bH as p}from"./index-CEo0I-m1.js";const i=e=>(_("data-v-dda25179"),e=e(),d(),e),n={class:"errorPage"},m=i(()=>s("div",{class:"error"},[s("img",{src:a,class:"img",alt:""}),s("div",{class:"tit"},"页面找不到了~")],-1)),l=[m],f=o({__name:"errorPage",setup(e){return t(),(u,h)=>(r(),c("div",n,l))}}),I=p(f,[["__scopeId","data-v-dda25179"]]);export{I as default};

+ 1 - 1
dist/assets/shapes-ZRVLueco.js → dist/assets/shapes-DA7-XwCS.js

@@ -1,4 +1,4 @@
-import{cE as k,bB as $1}from"./index-CbtUpGeg.js";import{ag as Z,ah as H1,ai as U,aj as n0,ak as c0,al as i0,am as j1,an as K1,ao as Y1,ap as R}from"./index-CR2okBFo.js";var z1={exports:{}};/*!
+import{cE as k,bB as $1}from"./index-CEo0I-m1.js";import{ag as Z,ah as H1,ai as U,aj as n0,ak as c0,al as i0,am as j1,an as K1,ao as Y1,ap as R}from"./index-CoIqepi0.js";var z1={exports:{}};/*!
  * clipboard.js v2.0.11
  * https://clipboardjs.com/
  *

+ 1 - 1
dist/index.html

@@ -10,7 +10,7 @@
     <meta name="description" content="ppt编辑器" />
     <meta name="keywords" content="ppt,powerpoint,office powerpoint,在线ppt,幻灯片,演示文稿,ppt在线制作,Vue3,TypeScript" />
     <title>PPT</title>
-    <script type="module" crossorigin src="./assets/index-CbtUpGeg.js"></script>
+    <script type="module" crossorigin src="./assets/index-CEo0I-m1.js"></script>
     <link rel="stylesheet" crossorigin href="./assets/index-AavUEaYy.css">
   </head>
   <body>

+ 91 - 42
src/utils/aiPptTemplateEngine.ts

@@ -7,9 +7,6 @@ import type {
   PPTVideoElement,
   PPTEnjoyElement,
   PPTCloudCoachElement,
-  PPTListeningPracticeElement,
-  PPTRhythmPracticeElement,
-  PPTMusicResourcesElement,
   PPTLineElement,
   PPTShapeElement,
   PPTTextElement,
@@ -48,6 +45,9 @@ interface ImageRect {
 
 const SLIDE_WIDTH = 1600
 const SLIDE_HEIGHT = 900
+const VIEWPORT_WIDTH = 1920
+const VIEWPORT_HEIGHT = 1080
+const VIEWPORT_SCALE = VIEWPORT_WIDTH / SLIDE_WIDTH
 const FONT_NAME = "Microsoft Yahei"
 const GENERIC_PAGE_TITLES = new Set(["封面", "课程导入", "故事导入", "导入", "首页", "cover"])
 
@@ -76,16 +76,38 @@ function buildSlide(page: AiCoursewarePage, outline: AiCoursewareOutline, palett
     ? [dedicatedResource]
     : videoBinding(page)
       ? videoResourceTemplate(page, outline, palette)
-      : [...templateFor(role)(page, outline, palette, index), ...inlineResourceElements(page, palette)]
+      : [...templateForPage(page, role)(page, outline, palette, index), ...inlineResourceElements(page, palette)]
   return {
     id: nanoid(10),
-    elements: keepInCanvas(elements),
+    elements: scaleElementsToViewport(keepInCanvas(elements)),
     background: backgroundConfig(role, palette, options),
     remark: slideRemark(page),
   }
 }
 
-function templateFor(role: PageRole) {
+function templateForPage(page: AiCoursewarePage, role: PageRole) {
+  const layout = cleanText(page.visualPlan?.layout || page.visual?.layout).toLowerCase()
+  switch (layout) {
+    case "cover_scene": return coverTemplate
+    case "full_image_question": return immersiveImageTemplate
+    case "split_image": return storyTemplate
+    case "goal_cards": return goalTemplate
+    case "listen_wave": return listenTemplate
+    case "lyric_focus": return lyricTemplate
+    case "score_focus": return notationTemplate
+    case "rhythm_grid": return rhythmTemplate
+    case "instrument_focus": return instrumentTemplate
+    case "compare_listen": return compareTemplate
+    case "knowledge_cards": return knowledgeTemplate
+    case "activity_steps": return activityTemplate
+    case "practice_check": return practiceTemplate
+    case "summary_checklist": return summaryTemplate
+    case "homework_list": return homeworkTemplate
+    default: return templateForRole(role)
+  }
+}
+
+function templateForRole(role: PageRole) {
   switch (role) {
     case "cover": return coverTemplate
     case "goal": return goalTemplate
@@ -108,6 +130,24 @@ function templateFor(role: PageRole) {
   }
 }
 
+function immersiveImageTemplate(page: AiCoursewarePage, outline: AiCoursewareOutline, palette: Palette, index: number): PPTElement[] {
+  if (!hasImage(page)) return storyTemplate(page, outline, palette, index)
+  const title = displayPageTitle(page, outline)
+  const question = page.interaction?.question || activityTask(page) || studentHeadline(page) || studentPoints(page, 1)[0]
+  const panelOnLeft = index % 2 === 0
+  const panelLeft = panelOnLeft ? 0 : 930
+  const textLeft = panelOnLeft ? 104 : 1010
+  const elements: PPTElement[] = [imageElement(page, { left: 0, top: 0, width: SLIDE_WIDTH, height: SLIDE_HEIGHT })]
+  elements.push(shapeElement({ left: panelLeft, top: 0, width: 670, height: 900, fill: palette.dark, opacity: 0.82, radius: 0 }))
+  elements.push(textElement({ left: textLeft, top: 110, width: 480, height: 48, content: page.teachingStage || "观察与发现", fontSize: 22, color: palette.warm, bold: true }))
+  elements.push(textElement({ left: textLeft, top: 188, width: 500, height: 160, content: title, fontSize: fitFont(title, 500, 160, 56, 40), color: "#FFFFFF", bold: true }))
+  elements.push(shapeElement({ left: textLeft, top: 382, width: 94, height: 10, fill: palette.accent, radius: 4 }))
+  if (question) {
+    elements.push(textElement({ left: textLeft, top: 454, width: 500, height: 200, content: question, fontSize: fitFont(question, 500, 200, 38, 28), color: "#FFFFFF", bold: true, lineHeight: 1.35 }))
+  }
+  return elements
+}
+
 function coverTemplate(page: AiCoursewarePage, outline: AiCoursewareOutline, palette: Palette): PPTElement[] {
   const title = chapterTitle(outline, page)
   const subtitle = [outline.gradeName, conciseTextbook(outline.textbookName), outline.unitName].filter(Boolean).join(" · ")
@@ -412,31 +452,9 @@ function addClassroomCue(elements: PPTElement[], page: AiCoursewarePage, palette
 
 function dedicatedResourceElement(page: AiCoursewarePage): PPTElement | undefined {
   const binding = (page.resourceBindings || []).find(item => item.verified !== false && item.dedicatedPage)
-  if (!binding) return undefined
+  if (!binding || binding.resourceType !== "MUSIC") return undefined
   const base = { id: nanoid(10), left: 0, top: 0, width: SLIDE_WIDTH, height: SLIDE_HEIGHT, rotate: 0 }
-  if (binding.resourceType === "MUSIC") {
-    return { ...base, type: "elf", subtype: "elf-sing-play", sid: binding.resourceId, title: binding.name || page.title } as PPTCloudCoachElement
-  }
-  if (binding.resourceType === "RHYTHM" && binding.dataJson) {
-    return { ...base, type: "elf", subtype: "elf-rhythm-practice", dataJson: binding.dataJson } as PPTRhythmPracticeElement
-  }
-  if (binding.resourceType === "LISTEN") {
-    const metadata = parseResourceJson(binding.dataJson)
-    const code = String(metadata.code || metadata.instrumentCode || binding.contentUrl || "")
-    if (code) {
-      return {
-        ...base, type: "elf", subtype: "elf-listening-practice", code,
-        name: binding.name || page.title, instrumentImg: binding.coverUrl || String(metadata.img || ""),
-      } as PPTListeningPracticeElement
-    }
-  }
-  const typeMap: Record<string, "INSTRUMENT" | "MUSICIAN" | "MUSIC" | "THEORY"> = {
-    INSTRUMENT: "INSTRUMENT", MUSICIAN: "MUSICIAN", MUSIC_WIKI: "MUSIC", THEORY: "THEORY",
-  }
-  const sType = typeMap[binding.resourceType]
-  if (sType) {
-    return { ...base, type: "elf", subtype: "elf-music-resources", sid: binding.resourceId, name: binding.name || page.title, sType } as PPTMusicResourcesElement
-  }
+  return { ...base, type: "elf", subtype: "elf-sing-play", sid: binding.resourceId, title: binding.name || page.title } as PPTCloudCoachElement
 }
 
 function inlineResourceElements(page: AiCoursewarePage, palette: Palette): PPTElement[] {
@@ -464,17 +482,6 @@ function videoResourceTemplate(page: AiCoursewarePage, outline: AiCoursewareOutl
   return elements
 }
 
-function parseResourceJson(value?: string): Record<string, unknown> {
-  if (!value) return {}
-  try {
-    const parsed = JSON.parse(value)
-    return parsed && typeof parsed === "object" ? parsed : {}
-  }
-  catch {
-    return {}
-  }
-}
-
 function normalizeOutline(outline: AiCoursewareOutline): AiCoursewareOutline {
   const chapter = cleanText(outline.chapterName || outline.title || "音乐课件")
   return {
@@ -675,9 +682,51 @@ function keepInCanvas(elements: PPTElement[]) {
   }) as PPTElement[]
 }
 
+function scaleElementsToViewport(elements: PPTElement[]): PPTElement[] {
+  return elements.map(element => {
+    if (element.type === "line") {
+      return {
+        ...element,
+        left: element.left * VIEWPORT_SCALE,
+        top: element.top * VIEWPORT_SCALE,
+        width: element.width * VIEWPORT_SCALE,
+        start: [element.start[0] * VIEWPORT_SCALE, element.start[1] * VIEWPORT_SCALE],
+        end: [element.end[0] * VIEWPORT_SCALE, element.end[1] * VIEWPORT_SCALE],
+      }
+    }
+
+    const scaled = {
+      ...element,
+      left: element.left * VIEWPORT_SCALE,
+      top: element.top * VIEWPORT_SCALE,
+      width: element.width * VIEWPORT_SCALE,
+      height: element.height * VIEWPORT_SCALE,
+    } as PPTElement
+    if (scaled.type === "text") {
+      scaled.content = scaleInlineFontSizes(scaled.content)
+    }
+    if ("radius" in scaled && typeof scaled.radius === "number") {
+      scaled.radius *= VIEWPORT_SCALE
+    }
+    if ("shadow" in scaled && scaled.shadow) {
+      scaled.shadow = {
+        ...scaled.shadow,
+        h: scaled.shadow.h * VIEWPORT_SCALE,
+        v: scaled.shadow.v * VIEWPORT_SCALE,
+        blur: scaled.shadow.blur * VIEWPORT_SCALE,
+      }
+    }
+    return scaled
+  })
+}
+
+function scaleInlineFontSizes(content: string) {
+  return content.replace(/font-size:\s*([\d.]+)px/g, (_match, size) => `font-size:${Number(size) * VIEWPORT_SCALE}px`)
+}
+
 function runPreflight(slides: Slide[]) {
   slides.forEach((slide, index) => {
-    const invalid = slide.elements.filter(element => element.left < 0 || element.top < 0 || element.left + element.width > SLIDE_WIDTH + 1 || (element.type !== "line" && element.top + element.height > SLIDE_HEIGHT + 1))
+    const invalid = slide.elements.filter(element => element.left < 0 || element.top < 0 || element.left + element.width > VIEWPORT_WIDTH + 1 || (element.type !== "line" && element.top + element.height > VIEWPORT_HEIGHT + 1))
     const emptyText = slide.elements.filter(element => element.type === "text" && !stripHtml(element.content))
     if (invalid.length || emptyText.length) console.warn("AI PPT preflight issue", { pageNo: index + 1, invalid: invalid.length, emptyText: emptyText.length })
   })

Некоторые файлы не были показаны из-за большого количества измененных файлов