From 7b742959f13f4943cb9c5f1a33b9497060e4ce1c Mon Sep 17 00:00:00 2001 From: jinwyang <755508716@qq.com> Date: Fri, 20 Mar 2026 13:59:27 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(=E5=85=83=E7=B4=A0=E6=B8=B2=E6=9F=93):?= =?UTF-8?q?=20=E7=BB=9F=E4=B8=80=E5=A4=84=E7=90=86viewBox=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E5=B9=B6=E4=BF=AE=E5=A4=8D=E7=BC=A9=E6=94=BE=E8=AE=A1?= =?UTF-8?q?=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加getViewBoxDims工具函数统一处理不同格式的viewBox 修复当viewBox为字符串或无效值时元素缩放计算问题 确保在生成、导出和渲染时viewBox格式一致 --- .../element/LatexElement/BaseLatexElement.tsx | 18 +++++++++++++++--- .../components/element/LatexElement/index.tsx | 18 +++++++++++++++--- .../element/ShapeElement/BaseShapeElement.tsx | 18 +++++++++++++++--- .../components/element/ShapeElement/index.tsx | 18 +++++++++++++++--- lib/export/use-export-pptx.ts | 8 ++++++-- lib/generation/scene-generator.ts | 4 ++-- 6 files changed, 68 insertions(+), 16 deletions(-) diff --git a/components/slide-renderer/components/element/LatexElement/BaseLatexElement.tsx b/components/slide-renderer/components/element/LatexElement/BaseLatexElement.tsx index a1a1c15be..2601e5ac5 100644 --- a/components/slide-renderer/components/element/LatexElement/BaseLatexElement.tsx +++ b/components/slide-renderer/components/element/LatexElement/BaseLatexElement.tsx @@ -3,6 +3,15 @@ import { useRef, useState, useLayoutEffect } from 'react'; import type { PPTLatexElement } from '@/lib/types/slides'; +function getViewBoxDims(viewBox: [number, number] | string | undefined): [number, number] { + if (Array.isArray(viewBox)) return viewBox; + if (typeof viewBox === 'string') { + const parts = viewBox.trim().split(/\s+/).map(Number); + return parts.length >= 4 ? [parts[2], parts[3]] : parts.length >= 2 ? [parts[0], parts[1]] : [0, 0]; + } + return [0, 0]; +} + export interface BaseLatexElementProps { elementInfo: PPTLatexElement; } @@ -47,9 +56,12 @@ export function BaseLatexElement({ elementInfo }: BaseLatexElementProps) { className="transform-origin-[0_0] overflow-visible" > { + const [vbW, vbH] = getViewBoxDims(elementInfo.viewBox); + const sx = vbW > 0 ? elementInfo.width / vbW : 1; + const sy = vbH > 0 ? elementInfo.height / vbH : 1; + return `scale(${sx}, ${sy}) translate(0,0) matrix(1,0,0,1,0,0)`; + })()} > diff --git a/components/slide-renderer/components/element/LatexElement/index.tsx b/components/slide-renderer/components/element/LatexElement/index.tsx index 9709cda65..fe47c8f9b 100644 --- a/components/slide-renderer/components/element/LatexElement/index.tsx +++ b/components/slide-renderer/components/element/LatexElement/index.tsx @@ -3,6 +3,15 @@ import { useRef, useState, useLayoutEffect } from 'react'; import type { PPTLatexElement } from '@/lib/types/slides'; +function getViewBoxDims(viewBox: [number, number] | string | undefined): [number, number] { + if (Array.isArray(viewBox)) return viewBox; + if (typeof viewBox === 'string') { + const parts = viewBox.trim().split(/\s+/).map(Number); + return parts.length >= 4 ? [parts[2], parts[3]] : parts.length >= 2 ? [parts[0], parts[1]] : [0, 0]; + } + return [0, 0]; +} + export { BaseLatexElement } from './BaseLatexElement'; export interface LatexElementProps { @@ -61,9 +70,12 @@ export function LatexElement({ elementInfo, selectElement }: LatexElementProps) className="transform-origin-[0_0]" > { + const [vbW, vbH] = getViewBoxDims(elementInfo.viewBox); + const sx = vbW > 0 ? elementInfo.width / vbW : 1; + const sy = vbH > 0 ? elementInfo.height / vbH : 1; + return `scale(${sx}, ${sy}) translate(0,0) matrix(1,0,0,1,0,0)`; + })()} > diff --git a/components/slide-renderer/components/element/ShapeElement/BaseShapeElement.tsx b/components/slide-renderer/components/element/ShapeElement/BaseShapeElement.tsx index 66cde7598..4a11e5148 100644 --- a/components/slide-renderer/components/element/ShapeElement/BaseShapeElement.tsx +++ b/components/slide-renderer/components/element/ShapeElement/BaseShapeElement.tsx @@ -8,6 +8,15 @@ import { useElementFill } from '../hooks/useElementFill'; import { GradientDefs } from './GradientDefs'; import { PatternDefs } from './PatternDefs'; +function getViewBoxDims(viewBox: [number, number] | string | undefined): [number, number] { + if (Array.isArray(viewBox)) return viewBox; + if (typeof viewBox === 'string') { + const parts = viewBox.trim().split(/\s+/).map(Number); + return parts.length >= 4 ? [parts[2], parts[3]] : parts.length >= 2 ? [parts[0], parts[1]] : [0, 0]; + } + return [0, 0]; +} + export interface BaseShapeElementProps { elementInfo: PPTShapeElement; } @@ -72,9 +81,12 @@ export function BaseShapeElement({ elementInfo }: BaseShapeElementProps) { )} { + const [vbW, vbH] = getViewBoxDims(elementInfo.viewBox); + const sx = vbW > 0 ? elementInfo.width / vbW : 1; + const sy = vbH > 0 ? elementInfo.height / vbH : 1; + return `scale(${sx}, ${sy}) translate(0,0) matrix(1,0,0,1,0,0)`; + })()} > = 4 ? [parts[2], parts[3]] : parts.length >= 2 ? [parts[0], parts[1]] : [0, 0]; + } + return [0, 0]; +} + export { BaseShapeElement } from './BaseShapeElement'; export interface ShapeElementProps { @@ -144,9 +153,12 @@ export function ShapeElement({ elementInfo, selectElement }: ShapeElementProps) )} { + const [vbW, vbH] = getViewBoxDims(elementInfo.viewBox); + const sx = vbW > 0 ? elementInfo.width / vbW : 1; + const sy = vbH > 0 ? elementInfo.height / vbH : 1; + return `scale(${sx}, ${sy}) translate(0,0) matrix(1,0,0,1,0,0)`; + })()} > { + const parts = String(el.viewBox).trim().split(/\s+/).map(Number); + return parts.length >= 4 ? [parts[2], parts[3]] : parts.length >= 2 ? [parts[0], parts[1]] : [0, 0]; + })(); const scale = { - x: el.width / el.viewBox[0], - y: el.height / el.viewBox[1], + x: vb[0] > 0 ? el.width / vb[0] : 1, + y: vb[1] > 0 ? el.height / vb[1] : 1, }; const points = formatPoints(toPoints(el.path), ratioPx2Inch, scale); diff --git a/lib/generation/scene-generator.ts b/lib/generation/scene-generator.ts index 1dc22937d..cd65ad702 100644 --- a/lib/generation/scene-generator.ts +++ b/lib/generation/scene-generator.ts @@ -394,8 +394,8 @@ function fixElementDefaults( if (el.type === 'shape') { const shapeEl = el as Record; - if (!shapeEl.viewBox) { - shapeEl.viewBox = `0 0 ${el.width ?? 100} ${el.height ?? 100}`; + if (!shapeEl.viewBox || !Array.isArray(shapeEl.viewBox)) { + shapeEl.viewBox = [el.width ?? 100, el.height ?? 100]; } if (!shapeEl.path) { // Default to rectangle From b2537abfd895d2c1f6c0ca1da69dd0b88bb932ad Mon Sep 17 00:00:00 2001 From: jinwyang <755508716@qq.com> Date: Fri, 20 Mar 2026 14:10:15 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix(asr-settings):=20=E6=A3=80=E6=9F=A5medi?= =?UTF-8?q?aDevices=20API=E6=98=AF=E5=90=A6=E5=8F=AF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在开始录音前添加对mediaDevices API的可用性检查,避免在不支持的环境下报错 --- components/settings/asr-settings.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/settings/asr-settings.tsx b/components/settings/asr-settings.tsx index 5666db0e4..8c01ab484 100644 --- a/components/settings/asr-settings.tsx +++ b/components/settings/asr-settings.tsx @@ -89,6 +89,9 @@ export function ASRSettings({ selectedProviderId }: ASRSettingsProps) { setIsRecording(true); } else { try { + if (!navigator.mediaDevices?.getUserMedia) { + throw new Error('mediaDevices API not available (requires HTTPS or localhost)'); + } const stream = await navigator.mediaDevices.getUserMedia({ audio: true, });