diff --git a/packages/editor/src/editor/Canvas.tsx b/packages/editor/src/editor/Canvas.tsx index 8c868fa..1fd29a5 100644 --- a/packages/editor/src/editor/Canvas.tsx +++ b/packages/editor/src/editor/Canvas.tsx @@ -152,8 +152,8 @@ export function Canvas(props: CanvasProps) { } } } else { - // Editor parity: right-click on empty space clears selection (unless additive). - if (!additive) props.onSelectSingle(undefined); + // Selection parity: right-click on empty space should NOT implicitly clear selection. + // (Left-click on background already clears unless additive via box-select.) } props.onOpenContextMenu({ clientX: e.clientX, clientY: e.clientY, worldX: p.x, worldY: p.y, targetId }); diff --git a/packages/sdk/src/core/goview/convert.ts b/packages/sdk/src/core/goview/convert.ts index 2880625..b245def 100644 --- a/packages/sdk/src/core/goview/convert.ts +++ b/packages/sdk/src/core/goview/convert.ts @@ -178,9 +178,40 @@ function looksLikeImageOption(option: unknown): boolean { return false; } +function looksLikeTextOption(option: unknown): boolean { + if (!option || typeof option !== 'object') return false; + const o = option as Record; + + // goView TextCommon exports usually include a string dataset plus some typography-ish keys. + if (typeof o.dataset !== 'string') return false; + + for (const key of [ + 'fontSize', + 'fontColor', + 'fontWeight', + 'letterSpacing', + 'paddingX', + 'paddingY', + 'textAlign', + 'backgroundColor', + 'borderWidth', + 'borderColor', + 'borderRadius', + 'writingMode', + ]) { + if (key in o) return true; + } + + return false; +} + function looksLikeIframeOption(option: unknown): boolean { if (!option) return false; + // Avoid false positives: some TextCommon widgets carry a link URL (or other URL-ish fields) + // but are still clearly text. + if (looksLikeTextOption(option)) return false; + if (typeof option === 'string') { const trimmed = option.trim(); if (trimmed.startsWith('<') && trimmed.includes('>')) return true; @@ -219,6 +250,9 @@ function looksLikeIframeOption(option: unknown): boolean { function looksLikeVideoOption(option: unknown): boolean { if (!option) return false; + // Avoid false positives: some TextCommon widgets carry link-like fields. + if (looksLikeTextOption(option)) return false; + // Prefer explicit video-ish keys when option is an object. if (typeof option === 'object') { const o = option as Record;