import { ASTRALVIEW_SCHEMA_VERSION, createEmptyScreen, type ImageWidgetNode, type IframeWidgetNode, type Screen, type TextWidgetNode, type VideoWidgetNode, } from '../schema'; import { convertGoViewImageOptionToNodeProps, type GoViewImageOption } from '../widgets/image'; import { convertGoViewIframeOptionToNodeProps, type GoViewIframeOption } from '../widgets/iframe'; import { convertGoViewVideoOptionToNodeProps, type GoViewVideoOption } from '../widgets/video'; import { convertGoViewTextOptionToNodeProps, type GoViewTextOption } from '../widgets/text'; import { pickUrlLike } from '../widgets/urlLike'; export interface GoViewComponentLike { id?: string; // some exports wrap the actual component under a nested field component?: GoViewComponentLike; // component identity key?: string; // e.g. "TextCommon" (sometimes) componentKey?: string; chartConfig?: { key?: string; chartKey?: string; type?: string; option?: unknown; data?: unknown; }; // geometry attr?: { x: number; y: number; w: number; h: number; zIndex?: number }; // state status?: { lock?: boolean; hide?: boolean }; // widget-specific config option?: unknown; } export interface GoViewEditCanvasConfigLike { projectName?: string; width?: number; height?: number; background?: string; } export interface GoViewStorageLike { editCanvasConfig?: GoViewEditCanvasConfigLike; componentList?: GoViewComponentLike[]; } export interface GoViewProjectLike { width?: number; height?: number; canvas?: { width?: number; height?: number }; componentList?: GoViewComponentLike[]; // persisted store shape (some variants) editCanvasConfig?: GoViewEditCanvasConfigLike; } function unwrapComponent(c: GoViewComponentLike): GoViewComponentLike { // Prefer nested component shapes but keep outer fields as fallback. // Some exports wrap components multiple times like: // { id, attr, component: { component: { chartConfig, option } } } // We unwrap iteratively to avoid recursion pitfalls. let out: GoViewComponentLike = c; let depth = 0; while (out.component && depth < 6) { const inner = out.component; out = { // Prefer outer for geometry/id, but prefer inner for identity/option when present. ...out, ...inner, // keep unwrapping if there are more layers component: inner.component, }; depth++; } return out; } function keyOf(cIn: GoViewComponentLike): string { const c = unwrapComponent(cIn); return ( c.chartConfig?.key ?? c.chartConfig?.chartKey ?? c.chartConfig?.type ?? c.componentKey ?? c.key ?? '' ).toLowerCase(); } function isTextCommon(c: GoViewComponentLike): boolean { const k = keyOf(c); if (k === 'textcommon') return true; return k.includes('text'); } function isImage(c: GoViewComponentLike): boolean { const k = keyOf(c); // goView variants: "Image", "image", sometimes with suffixes. return k === 'image' || k.includes('image') || k.includes('picture'); } function isIframe(c: GoViewComponentLike): boolean { const k = keyOf(c); // goView variants: "Iframe", "IframeCommon", etc. if (k === 'iframe' || k.includes('iframe')) return true; // Other names seen in low-code editors for embedded web content. return ( k.includes('embed') || k.includes('webview') || k.includes('html') || k.includes('browser') || k.includes('webpage') || // keep the plain 'web' check last; it's broad and may overlap other widgets. k === 'web' || k.endsWith('_web') || k.startsWith('web_') || // common embed platforms (usually rendered in an