sdk: improve legacy URL extraction; editor: ctrl right-click select

This commit is contained in:
ErSan 2026-01-27 20:11:28 +08:00
parent b6f69b8d6a
commit de4243ca10
5 changed files with 66 additions and 42 deletions

View File

@ -165,7 +165,12 @@ export function Canvas(props: CanvasProps) {
if (targetId && !props.selectionIds.includes(targetId)) {
// goView-ish: right click selects the item.
props.onSelectSingle(targetId);
// Ctrl/Cmd keeps multi-select parity (add to selection instead of replacing).
if ((e as React.MouseEvent).ctrlKey || (e as React.MouseEvent).metaKey) {
props.onToggleSelect(targetId);
} else {
props.onSelectSingle(targetId);
}
}
setCtx({ clientX: e.clientX, clientY: e.clientY, worldX: p.x, worldY: p.y, targetId });

View File

@ -1,4 +1,5 @@
import type { IframeWidgetNode } from '../schema';
import { pickUrlLike } from './urlLike';
/**
* goView iframe option shape varies across versions.
@ -16,23 +17,8 @@ export interface GoViewIframeOption {
*/
export type LegacyIframeOption = GoViewIframeOption;
function asString(v: unknown): string {
if (typeof v === 'string') return v;
if (!v) return '';
// Some goView widgets store data as { value: '...' } or { url: '...' }.
if (typeof v === 'object') {
const obj = v as Record<string, unknown>;
if (typeof obj.value === 'string') return obj.value;
if (typeof obj.url === 'string') return obj.url;
if (typeof obj.src === 'string') return obj.src;
}
return '';
}
function pickSrc(option: GoViewIframeOption): string {
return asString(option.dataset) || asString(option.src) || asString(option.url);
return pickUrlLike(option.dataset) || pickUrlLike(option.src) || pickUrlLike(option.url);
}
export function convertGoViewIframeOptionToNodeProps(option: GoViewIframeOption): IframeWidgetNode['props'] {

View File

@ -1,4 +1,5 @@
import type { ImageWidgetNode } from '../schema';
import { pickUrlLike } from './urlLike';
/**
* goView Image option shape varies across versions. We keep this intentionally
@ -24,25 +25,16 @@ export interface GoViewImageOption {
borderRadius?: number;
}
function pickSrc(option: GoViewImageOption): string {
return pickUrlLike(option.dataset) || pickUrlLike(option.src) || pickUrlLike(option.url);
}
function asString(v: unknown): string {
if (typeof v === 'string') return v;
if (!v) return '';
// Some goView widgets store data as { value: '...' } or { url: '...' }.
if (typeof v === 'object') {
const obj = v as Record<string, unknown>;
if (typeof obj.value === 'string') return obj.value;
if (typeof obj.url === 'string') return obj.url;
if (typeof obj.src === 'string') return obj.src;
}
return '';
}
function pickSrc(option: GoViewImageOption): string {
return asString(option.dataset) || asString(option.src) || asString(option.url);
}
function pickFit(option: GoViewImageOption): ImageWidgetNode['props']['fit'] | undefined {
const raw = asString(option.fit) || asString(option.objectFit);
if (!raw) return undefined;

View File

@ -0,0 +1,48 @@
/**
* Small helper for permissive imports (goView / low-code exports).
*
* Many widgets store their URL-ish source in slightly different shapes:
* - string
* - { value: string }
* - { url: string }
* - { src: string }
* - { dataset: string | { value/url/src } }
* - nested objects under `data` / `config` / `options`
*/
export function pickUrlLike(input: unknown, maxDepth = 3): string {
return pickUrlLikeInner(input, maxDepth);
}
function pickUrlLikeInner(input: unknown, depth: number): string {
if (typeof input === 'string') return input;
if (!input) return '';
if (Array.isArray(input)) {
for (const item of input) {
const v = pickUrlLikeInner(item, depth - 1);
if (v) return v;
}
return '';
}
if (typeof input !== 'object') return '';
const obj = input as Record<string, unknown>;
// Common direct keys.
for (const key of ['value', 'url', 'src', 'href', 'link', 'path', 'iframeUrl', 'videoUrl', 'mp4']) {
const v = obj[key];
if (typeof v === 'string' && v) return v;
}
if (depth <= 0) return '';
// Common nesting keys.
for (const key of ['dataset', 'data', 'config', 'option', 'options', 'props']) {
const v = obj[key];
const nested = pickUrlLikeInner(v, depth - 1);
if (nested) return nested;
}
return '';
}

View File

@ -1,4 +1,5 @@
import type { VideoWidgetNode } from '../schema';
import { pickUrlLike } from './urlLike';
/**
* goView video option shape varies across versions.
@ -23,24 +24,16 @@ export interface GoViewVideoOption {
*/
export type LegacyVideoOption = GoViewVideoOption;
function pickSrc(option: GoViewVideoOption): string {
return pickUrlLike(option.dataset) || pickUrlLike(option.src) || pickUrlLike(option.url);
}
function asString(v: unknown): string {
if (typeof v === 'string') return v;
if (!v) return '';
if (typeof v === 'object') {
const obj = v as Record<string, unknown>;
if (typeof obj.value === 'string') return obj.value;
if (typeof obj.url === 'string') return obj.url;
if (typeof obj.src === 'string') return obj.src;
}
return '';
}
function pickSrc(option: GoViewVideoOption): string {
return asString(option.dataset) || asString(option.src) || asString(option.url);
}
function pickFit(option: GoViewVideoOption): VideoWidgetNode['props']['fit'] | undefined {
const raw = asString(option.fit) || asString(option.objectFit);
if (!raw) return undefined;