From 01e43a0021a5853bd2ac525297f7e0605288eeba Mon Sep 17 00:00:00 2001 From: clawdbot Date: Tue, 27 Jan 2026 21:52:27 +0800 Subject: [PATCH] sdk: improve goView import URL inference --- packages/sdk/src/core/goview/convert.ts | 24 ++++++++++++++++++++++-- packages/sdk/src/core/widgets/urlLike.ts | 16 +++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/packages/sdk/src/core/goview/convert.ts b/packages/sdk/src/core/goview/convert.ts index dc4bf67..a9e9919 100644 --- a/packages/sdk/src/core/goview/convert.ts +++ b/packages/sdk/src/core/goview/convert.ts @@ -118,6 +118,19 @@ function isVideo(c: GoViewComponentLike): boolean { return k.includes('mp4') || k.includes('media') || k.includes('player') || k.includes('stream'); } +function looksLikeImageOption(option: unknown): boolean { + const url = pickUrlLike(option); + if (!url) return false; + + // Common direct image URLs. + if (/\.(png|jpe?g|gif|webp|bmp|svg)(\?|#|$)/i.test(url)) return true; + + // data urls + if (/^data:image\//i.test(url)) return true; + + return false; +} + function looksLikeIframeOption(option: unknown): boolean { if (!option || typeof option !== 'object') return false; const o = option as Record; @@ -130,7 +143,11 @@ function looksLikeIframeOption(option: unknown): boolean { // If it isn't an obvious media URL, it's often an iframe/embed. // (We deliberately keep this conservative; image/video are handled earlier.) - return /^https?:\/\//i.test(url) && !/\.(mp4|m3u8|flv)(\?|#|$)/i.test(url); + return ( + (/^https?:\/\//i.test(url) || /^data:text\/html/i.test(url)) && + !/\.(png|jpe?g|gif|webp|bmp|svg)(\?|#|$)/i.test(url) && + !/\.(mp4|m3u8|flv)(\?|#|$)/i.test(url) + ); } function looksLikeVideoOption(option: unknown): boolean { @@ -147,6 +164,9 @@ function looksLikeVideoOption(option: unknown): boolean { if (/\.(mp4|m3u8|flv)(\?|#|$)/i.test(url)) return true; if (/\bm3u8\b/i.test(url)) return true; + // Streaming-ish protocols (seen in CCTV/camera widgets). + if (/^(rtsp|rtmp):\/\//i.test(url)) return true; + return false; } @@ -300,7 +320,7 @@ export function convertGoViewProjectToScreen(input: GoViewProjectLike | GoViewSt continue; } - if (isImage(c)) { + if (isImage(c) || looksLikeImageOption(option)) { const props = convertGoViewImageOptionToNodeProps(option as GoViewImageOption); nodes.push({ id: c.id ?? `import_image_${Math.random().toString(16).slice(2)}`, diff --git a/packages/sdk/src/core/widgets/urlLike.ts b/packages/sdk/src/core/widgets/urlLike.ts index 8439f65..84e9e6b 100644 --- a/packages/sdk/src/core/widgets/urlLike.ts +++ b/packages/sdk/src/core/widgets/urlLike.ts @@ -65,7 +65,21 @@ function pickUrlLikeInner(input: unknown, depth: number): string { if (depth <= 0) return ''; // Common nesting keys. - for (const key of ['dataset', 'data', 'config', 'option', 'options', 'props', 'source', 'media']) { + for (const key of [ + 'dataset', + 'data', + 'config', + 'option', + 'options', + 'props', + 'source', + 'media', + // widget-ish wrappers seen in exports + 'iframe', + 'video', + 'image', + 'img', + ]) { const v = obj[key]; const nested = pickUrlLikeInner(v, depth - 1); if (nested) return nested;