|
|
|
|
@ -158,6 +158,9 @@ function looksLikeIframeOption(option: unknown): boolean {
|
|
|
|
|
if (typeof option === 'object') {
|
|
|
|
|
const o = option as Record<string, unknown>;
|
|
|
|
|
if ('iframeUrl' in o || 'iframeSrc' in o || 'embedUrl' in o) return true;
|
|
|
|
|
|
|
|
|
|
// Some exports store raw HTML instead of a URL.
|
|
|
|
|
if ('html' in o || 'htmlContent' in o || 'content' in o || 'template' in o) return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const url = pickUrlLike(option);
|
|
|
|
|
@ -181,7 +184,19 @@ function looksLikeVideoOption(option: unknown): boolean {
|
|
|
|
|
// Prefer explicit video-ish keys when option is an object.
|
|
|
|
|
if (typeof option === 'object') {
|
|
|
|
|
const o = option as Record<string, unknown>;
|
|
|
|
|
if ('videoUrl' in o || 'videoSrc' in o || 'mp4' in o || 'm3u8' in o || 'flv' in o || 'hls' in o || 'rtsp' in o) {
|
|
|
|
|
if (
|
|
|
|
|
'videoUrl' in o ||
|
|
|
|
|
'videoSrc' in o ||
|
|
|
|
|
'mp4' in o ||
|
|
|
|
|
'm3u8' in o ||
|
|
|
|
|
'flv' in o ||
|
|
|
|
|
'hls' in o ||
|
|
|
|
|
'rtsp' in o ||
|
|
|
|
|
// list-ish shapes
|
|
|
|
|
'sources' in o ||
|
|
|
|
|
'sourceList' in o ||
|
|
|
|
|
'urlList' in o
|
|
|
|
|
) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -323,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<string, unknown> | undefined;
|
|
|
|
|
const rect = attr
|
|
|
|
|
? {
|
|
|
|
|
x: toNumber((c.attr as unknown as Record<string, unknown>).x, 0),
|
|
|
|
|
y: toNumber((c.attr as unknown as Record<string, unknown>).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<string, unknown>).w, optSize.w ?? 320),
|
|
|
|
|
h: toNumber((c.attr as unknown as Record<string, unknown>).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<string, unknown>).zIndex, 0),
|
|
|
|
|
zIndex,
|
|
|
|
|
locked: c.status?.lock ?? false,
|
|
|
|
|
hidden: c.status?.hide ?? false,
|
|
|
|
|
props,
|
|
|
|
|
@ -349,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<string, unknown>).zIndex, 0),
|
|
|
|
|
zIndex,
|
|
|
|
|
locked: c.status?.lock ?? false,
|
|
|
|
|
hidden: c.status?.hide ?? false,
|
|
|
|
|
props,
|
|
|
|
|
@ -363,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<string, unknown>).zIndex, 0),
|
|
|
|
|
zIndex,
|
|
|
|
|
locked: c.status?.lock ?? false,
|
|
|
|
|
hidden: c.status?.hide ?? false,
|
|
|
|
|
props,
|
|
|
|
|
@ -378,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<string, unknown>).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<string, unknown>).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<string, unknown>).zIndex, 0),
|
|
|
|
|
zIndex,
|
|
|
|
|
locked: c.status?.lock ?? false,
|
|
|
|
|
hidden: c.status?.hide ?? false,
|
|
|
|
|
props,
|
|
|
|
|
|