From a1b649697695cbff0dee0e677526a57b63fb2d0b Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 21 Mar 2026 23:35:48 +0800 Subject: [PATCH] feat: upgrade detail section component renderer --- .../canvas/DetailSurfaceInspectorPanel.tsx | 75 ++- components/canvas/nodes/TextNode.tsx | 52 +- lib/detail-ui-renderer.ts | 92 +++- lib/detail-ui-surface-adapter.ts | 477 ++++++++++++++++-- lib/ui-surface-protocol.ts | 5 +- types/canvas/index.ts | 10 + 6 files changed, 628 insertions(+), 83 deletions(-) diff --git a/components/canvas/DetailSurfaceInspectorPanel.tsx b/components/canvas/DetailSurfaceInspectorPanel.tsx index f37b221b9..563139514 100644 --- a/components/canvas/DetailSurfaceInspectorPanel.tsx +++ b/components/canvas/DetailSurfaceInspectorPanel.tsx @@ -32,15 +32,42 @@ function getActionClassName(action?: DetailSurfaceInspectorReviewState["action"] return "bg-emerald-50 text-emerald-700 ring-1 ring-inset ring-emerald-500/20 dark:bg-emerald-500/10 dark:text-emerald-300"; } +function collectRenderableScreenComponents(surface: UiSurfaceSnapshot, parentId: string) { + const result = [] as typeof surface.components; + const queue = [...getUiSurfaceChildren(surface, parentId)]; + + while (queue.length > 0) { + const component = queue.shift(); + if (!component) continue; + + if ( + component.type === "detail_text_block" || + component.type === "detail_card" || + component.type === "detail_badge" + ) { + result.push(component); + continue; + } + + queue.push(...getUiSurfaceChildren(surface, component.id)); + } + + return result; +} + export function DetailSurfaceInspectorPanel(params: { surface: UiSurfaceSnapshot | null; latestScreenValidation?: DetailSurfaceInspectorReviewState | null; }) { if (!params.surface) return null; + const surface = params.surface; - const screenComponents = getUiSurfaceScreenComponents(params.surface); - const totalComponents = countUiSurfaceComponents(params.surface); - const totalTextBlocks = countUiSurfaceComponents(params.surface, "detail_text_block"); + const screenComponents = getUiSurfaceScreenComponents(surface); + const totalRenderableBlocks = + countUiSurfaceComponents(surface, "detail_text_block") + + countUiSurfaceComponents(surface, "detail_card") + + countUiSurfaceComponents(surface, "detail_badge"); + const totalSections = countUiSurfaceComponents(surface, "detail_section"); const totalScreens = screenComponents.length; return ( @@ -51,12 +78,12 @@ export function DetailSurfaceInspectorPanel(params: {

Detail Surface Inspector

- {params.surface.surfaceId} + {surface.surfaceId}

- {params.surface.generationMode === "agent_layout" ? "AGENT Surface" : "Classic Surface"} + {surface.generationMode === "agent_layout" ? "AGENT Surface" : "Classic Surface"} @@ -71,16 +98,16 @@ export function DetailSurfaceInspectorPanel(params: {
- Components + Sections
-
{totalComponents}
+
{totalSections}
- Text Blocks + UI Blocks
-
{totalTextBlocks}
+
{totalRenderableBlocks}
@@ -88,38 +115,44 @@ export function DetailSurfaceInspectorPanel(params: {
Surface Validation
- {params.surface.validation?.passed === false - ? params.surface.validation.issues[0] || "存在待优化项" + {surface.validation?.passed === false + ? surface.validation.issues[0] || "存在待优化项" : "当前 surface 已通过基础校验"}
- {params.surface.validation?.passed === false ? "待优化" : "通过"} + {surface.validation?.passed === false ? "待优化" : "通过"}
{screenComponents.slice(0, 6).map((screenComponent) => { const screenIndex = Number(screenComponent.props?.screenIndex || 0); - const textStack = getUiSurfaceChildren(params.surface, screenComponent.id).find( + const textStack = getUiSurfaceChildren(surface, screenComponent.id).find( (component) => component.type === "detail_text_stack", ); const textBlocks = textStack - ? getUiSurfaceChildren(params.surface, textStack.id).filter( - (component) => component.type === "detail_text_block", - ) + ? collectRenderableScreenComponents(surface, textStack.id) : []; const titleBlock = - textBlocks.find((component) => component.props?.blockType === "title") || null; + textBlocks.find( + (component) => + component.metadata?.componentRole === "title" || + component.props?.blockType === "title", + ) || null; const subtitleBlock = - textBlocks.find((component) => component.props?.blockType === "subtitle") || null; + textBlocks.find( + (component) => + component.metadata?.componentRole === "subtitle" || + component.props?.blockType === "subtitle", + ) || null; const isLatestScreen = params.latestScreenValidation?.screenIndex === screenIndex; return ( @@ -139,11 +172,11 @@ export function DetailSurfaceInspectorPanel(params: { : ""}
- {resolveUiSurfaceTextBinding(params.surface, titleBlock, "content") || "未定义标题"} + {resolveUiSurfaceTextBinding(surface, titleBlock, "content") || "未定义标题"}
{subtitleBlock ? (
- {resolveUiSurfaceTextBinding(params.surface, subtitleBlock, "content")} + {resolveUiSurfaceTextBinding(surface, subtitleBlock, "content")}
) : null} diff --git a/components/canvas/nodes/TextNode.tsx b/components/canvas/nodes/TextNode.tsx index bea54dbfc..73eeeb7c2 100644 --- a/components/canvas/nodes/TextNode.tsx +++ b/components/canvas/nodes/TextNode.tsx @@ -46,6 +46,41 @@ export function TextNode({ node, isSelected }: TextNodeProps) { setIsEditing(true); }; + const containerStyle = data.transparent + ? undefined + : { + backgroundColor: data.backgroundColor || undefined, + borderColor: data.borderColor || undefined, + borderWidth: + typeof data.borderWidth === "number" ? `${data.borderWidth}px` : undefined, + borderStyle: + typeof data.borderWidth === "number" && data.borderWidth > 0 ? "solid" : undefined, + borderRadius: + typeof data.borderRadius === "number" ? `${data.borderRadius}px` : undefined, + padding: typeof data.padding === "number" ? `${data.padding}px` : undefined, + boxShadow: data.boxShadow || undefined, + backdropFilter: + typeof data.backdropBlur === "number" && data.backdropBlur > 0 + ? `blur(${data.backdropBlur}px)` + : undefined, + WebkitBackdropFilter: + typeof data.backdropBlur === "number" && data.backdropBlur > 0 + ? `blur(${data.backdropBlur}px)` + : undefined, + }; + + const textStyle = { + fontSize: data.fontSize || 14, + fontFamily: data.fontFamily || undefined, + color: data.color || "inherit", + fontWeight: data.fontWeight || "normal", + lineHeight: data.lineHeight || 1.35, + letterSpacing: + typeof data.letterSpacing === "number" ? `${data.letterSpacing}px` : undefined, + textAlign: data.textAlign || "left", + textTransform: data.textTransform || "none", + } as const; + return (
{isEditing ? (