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,
});