diff --git a/packages/sdk/src/core/widgets/iframe.ts b/packages/sdk/src/core/widgets/iframe.ts index e246fd0..bb6eaa5 100644 --- a/packages/sdk/src/core/widgets/iframe.ts +++ b/packages/sdk/src/core/widgets/iframe.ts @@ -1,5 +1,5 @@ import type { IframeWidgetNode } from '../schema'; -import { pickUrlLike } from './urlLike'; +import { normalizeUrlLike, pickUrlLike } from './urlLike'; /** * goView iframe option shape varies across versions. @@ -209,16 +209,16 @@ function pickSrc(option: GoViewIframeOption): string { if (url) { if (looksLikeHtml(url)) { const extracted = extractSrcFromEmbedHtml(url); - return extracted || toDataHtmlUrl(url); + return extracted ? normalizeUrlLike(extracted) : toDataHtmlUrl(url); } - return url; + return normalizeUrlLike(url); } // 2) Some exports store raw HTML instead of a URL. const html = pickHtmlString(option); if (html) { const extracted = extractSrcFromEmbedHtml(html); - return extracted || toDataHtmlUrl(html); + return extracted ? normalizeUrlLike(extracted) : toDataHtmlUrl(html); } // 3) List-ish shapes. @@ -237,9 +237,9 @@ function pickSrc(option: GoViewIframeOption): string { if (!listUrl) return ''; if (looksLikeHtml(listUrl)) { const extracted = extractSrcFromEmbedHtml(listUrl); - return extracted || toDataHtmlUrl(listUrl); + return extracted ? normalizeUrlLike(extracted) : toDataHtmlUrl(listUrl); } - return listUrl; + return normalizeUrlLike(listUrl); } function pickFit(option: GoViewIframeOption): IframeWidgetNode['props']['fit'] | undefined { diff --git a/packages/sdk/src/core/widgets/urlLike.ts b/packages/sdk/src/core/widgets/urlLike.ts index e0d9d57..4eff40e 100644 --- a/packages/sdk/src/core/widgets/urlLike.ts +++ b/packages/sdk/src/core/widgets/urlLike.ts @@ -10,7 +10,31 @@ * - nested objects under `data` / `config` / `options` */ export function pickUrlLike(input: unknown, maxDepth = 3): string { - return pickUrlLikeInner(input, maxDepth); + const raw = pickUrlLikeInner(input, maxDepth); + return normalizeUrlLike(raw); +} + +/** + * Normalize URL-ish strings from low-code exports. + * + * Common cases: + * - protocol-relative URLs (//example.com) → https://example.com + * - "www.example.com" → https://www.example.com + */ +export function normalizeUrlLike(url: string): string { + const s = url.trim(); + if (!s) return ''; + + // Don't touch data URLs, streaming protocols, or explicit protocols. + if (/^(data:|rtsp:\/\/|rtmp:\/\/|https?:\/\/)/i.test(s)) return s; + + // protocol-relative URLs + if (s.startsWith('//')) return `https:${s}`; + + // bare hostnames + if (/^www\./i.test(s)) return `https://${s}`; + + return s; } function looksLikeJsonString(input: string): boolean { diff --git a/packages/sdk/src/core/widgets/video.ts b/packages/sdk/src/core/widgets/video.ts index 6825e61..105b3bb 100644 --- a/packages/sdk/src/core/widgets/video.ts +++ b/packages/sdk/src/core/widgets/video.ts @@ -1,5 +1,5 @@ import type { VideoWidgetNode } from '../schema'; -import { pickUrlLike } from './urlLike'; +import { normalizeUrlLike, pickUrlLike } from './urlLike'; /** * goView video option shape varies across versions. @@ -221,9 +221,9 @@ function pickFirstUrlFromList(input: unknown): string { function normalizeSrcMaybeFromHtml(raw: string): string { if (!raw) return ''; - if (!looksLikeHtml(raw)) return raw; + if (!looksLikeHtml(raw)) return normalizeUrlLike(raw); const extracted = extractSrcFromVideoHtml(raw); - return extracted || raw; + return extracted ? normalizeUrlLike(extracted) : raw; } function pickSrc(option: GoViewVideoOption): string {