diff --git a/packages/editor/package.json b/packages/editor/package.json index 334ed9b..2c441f0 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -6,6 +6,7 @@ "scripts": { "dev": "vite", "build": "tsc -b && vite build", + "typecheck": "tsc -b --noEmit", "lint": "eslint .", "preview": "vite preview" }, diff --git a/packages/sdk/src/core/goview/convert.ts b/packages/sdk/src/core/goview/convert.ts index 6408f00..5ffb1ee 100644 --- a/packages/sdk/src/core/goview/convert.ts +++ b/packages/sdk/src/core/goview/convert.ts @@ -338,25 +338,53 @@ export function convertGoViewProjectToScreen(input: GoViewProjectLike | GoViewSt for (const raw of componentList) { const c = unwrapComponent(raw); const option = optionOf(c); + + // We try to infer the widget kind early so we can pick better default sizes + // when exports omit sizing information. + const inferredType: 'text' | 'image' | 'iframe' | 'video' | undefined = isTextCommon(c) + ? 'text' + : isImage(c) || looksLikeImageOption(option) + ? 'image' + : // Important: run video checks before iframe checks; iframe URL detection is broader. + isVideo(c) || looksLikeVideoOption(option) + ? 'video' + : isIframe(c) || looksLikeIframeOption(option) + ? 'iframe' + : undefined; + + const defaults = + inferredType === 'text' + ? { w: 320, h: 60 } + : inferredType === 'image' + ? { w: 320, h: 180 } + : inferredType === 'iframe' + ? { w: 480, h: 270 } + : inferredType === 'video' + ? { w: 480, h: 270 } + : { w: 320, h: 60 }; + const optSize = pickSizeLike(option); - const rect = c.attr + const attr = c.attr as unknown as Record | undefined; + const rect = attr ? { - x: toNumber((c.attr as unknown as Record).x, 0), - y: toNumber((c.attr as unknown as Record).y, 0), + x: toNumber(attr.x, 0), + y: toNumber(attr.y, 0), // Prefer explicit attr sizing, but fall back to option sizing when missing. - w: toNumber((c.attr as unknown as Record).w, optSize.w ?? 320), - h: toNumber((c.attr as unknown as Record).h, optSize.h ?? 60), + w: toNumber(attr.w, optSize.w ?? defaults.w), + h: toNumber(attr.h, optSize.h ?? defaults.h), } - : { x: 0, y: 0, w: optSize.w ?? 320, h: optSize.h ?? 60 }; + : { x: 0, y: 0, w: optSize.w ?? defaults.w, h: optSize.h ?? defaults.h }; - if (isTextCommon(c)) { + const zIndex = attr?.zIndex === undefined ? undefined : toNumber(attr.zIndex, 0); + + if (inferredType === 'text') { const props = convertGoViewTextOptionToNodeProps(option as GoViewTextOption); nodes.push({ id: c.id ?? `import_text_${Math.random().toString(16).slice(2)}`, type: 'text', rect, - zIndex: c.attr?.zIndex === undefined ? undefined : toNumber((c.attr as unknown as Record).zIndex, 0), + zIndex, locked: c.status?.lock ?? false, hidden: c.status?.hide ?? false, props, @@ -364,13 +392,13 @@ export function convertGoViewProjectToScreen(input: GoViewProjectLike | GoViewSt continue; } - if (isImage(c) || looksLikeImageOption(option)) { + if (inferredType === 'image') { const props = convertGoViewImageOptionToNodeProps(option as GoViewImageOption); nodes.push({ id: c.id ?? `import_image_${Math.random().toString(16).slice(2)}`, type: 'image', rect, - zIndex: c.attr?.zIndex === undefined ? undefined : toNumber((c.attr as unknown as Record).zIndex, 0), + zIndex, locked: c.status?.lock ?? false, hidden: c.status?.hide ?? false, props, @@ -378,14 +406,13 @@ export function convertGoViewProjectToScreen(input: GoViewProjectLike | GoViewSt continue; } - // Prefer explicit component keys over heuristics. - if (isIframe(c)) { + if (inferredType === 'iframe') { const props = convertGoViewIframeOptionToNodeProps(option as GoViewIframeOption); nodes.push({ id: c.id ?? `import_iframe_${Math.random().toString(16).slice(2)}`, type: 'iframe', rect, - zIndex: c.attr?.zIndex === undefined ? undefined : toNumber((c.attr as unknown as Record).zIndex, 0), + zIndex, locked: c.status?.lock ?? false, hidden: c.status?.hide ?? false, props, @@ -393,43 +420,13 @@ export function convertGoViewProjectToScreen(input: GoViewProjectLike | GoViewSt continue; } - if (isVideo(c)) { + if (inferredType === 'video') { const props = convertGoViewVideoOptionToNodeProps(option as GoViewVideoOption); nodes.push({ id: c.id ?? `import_video_${Math.random().toString(16).slice(2)}`, type: 'video', rect, - zIndex: c.attr?.zIndex === undefined ? undefined : toNumber((c.attr as unknown as Record).zIndex, 0), - locked: c.status?.lock ?? false, - hidden: c.status?.hide ?? false, - props, - }); - continue; - } - - // Heuristic fallback: distinguish common URL patterns. - // Important: run video checks before iframe checks; iframe URL detection is broader. - if (looksLikeVideoOption(option)) { - const props = convertGoViewVideoOptionToNodeProps(option as GoViewVideoOption); - nodes.push({ - id: c.id ?? `import_video_${Math.random().toString(16).slice(2)}`, - type: 'video', - rect, - zIndex: c.attr?.zIndex === undefined ? undefined : toNumber((c.attr as unknown as Record).zIndex, 0), - locked: c.status?.lock ?? false, - hidden: c.status?.hide ?? false, - props, - }); - continue; - } - - if (looksLikeIframeOption(option)) { - const props = convertGoViewIframeOptionToNodeProps(option as GoViewIframeOption); - nodes.push({ - id: c.id ?? `import_iframe_${Math.random().toString(16).slice(2)}`, - type: 'iframe', - rect, - zIndex: c.attr?.zIndex === undefined ? undefined : toNumber((c.attr as unknown as Record).zIndex, 0), + zIndex, locked: c.status?.lock ?? false, hidden: c.status?.hide ?? false, props,