import type { DetailUiProtocol, DetailUiScreenReviewPatch, } from "@/lib/detail-ui-protocol"; import type { CanvasNode, TextNodeData } from "@/types/canvas"; type CanvasTextNodeDraft = Omit; function createTextNodeDraft(params: { parentId: string; x: number; y: number; width: number; height: number; content: string; fontSize: number; fontWeight: string; color: string; lineHeight: number; textAlign: "left" | "center" | "right"; metadata?: Record; }): CanvasTextNodeDraft { return { type: "text", position: { x: params.x, y: params.y }, size: { width: params.width, height: params.height }, data: { content: params.content, fontSize: params.fontSize, fontWeight: params.fontWeight, color: params.color, lineHeight: params.lineHeight, textAlign: params.textAlign, transparent: true, metadata: params.metadata, } as TextNodeData & { metadata?: Record }, connections: [], parentId: params.parentId, }; } export function buildDetailTextLayerNodesFromProtocol(params: { parentNodeId: string; parentPosition: { x: number; y: number }; parentSize: { width: number; height: number }; screenSize: { width: number; height: number }; screenProtocol?: DetailUiProtocol["screens"][number]; }) { const protocolBlocks = params.screenProtocol?.blocks || []; if (protocolBlocks.length === 0) return [] as CanvasTextNodeDraft[]; const scaleX = params.parentSize.width / Math.max(1, params.screenSize.width); const scaleY = params.parentSize.height / Math.max(1, params.screenSize.height); return protocolBlocks.map((block) => createTextNodeDraft({ parentId: params.parentNodeId, x: params.parentPosition.x + Math.round(block.frame.x * scaleX), y: params.parentPosition.y + Math.round(block.frame.y * scaleY), width: Math.max(120, Math.round(block.frame.width * scaleX)), height: Math.max(48, Math.round(block.frame.height * scaleY)), content: block.content, fontSize: Math.max(12, Math.round(block.style.fontSize * Math.min(scaleX, scaleY))), fontWeight: block.style.fontWeight, color: block.style.color, lineHeight: block.style.lineHeight, textAlign: block.style.textAlign, metadata: { detailUiBlockId: block.id, detailUiBlockType: block.type, detailUiScreenIndex: params.screenProtocol?.index, }, }), ); } export function buildDetailTextLayerNodesFromReviewPatch(params: { parentNodeId: string; parentPosition: { x: number; y: number }; parentSize: { width: number; height: number }; screenSize: { width: number; height: number }; uiPatch?: DetailUiScreenReviewPatch; }) { if (!params.uiPatch) return [] as CanvasTextNodeDraft[]; return buildDetailTextLayerNodesFromProtocol({ parentNodeId: params.parentNodeId, parentPosition: params.parentPosition, parentSize: params.parentSize, screenSize: params.screenSize, screenProtocol: params.uiPatch.recommendedScreen, }); } export function buildLegacyDetailTextLayerNodes(params: { parentNodeId: string; parentPosition: { x: number; y: number }; parentWidth: number; screenIndex: number; title?: string; subtitle?: string; body?: string; titleFontSize: number; titleFontWeight: string; subtitleFontSize: number; subtitleFontWeight: string; bodyFontSize: number; bodyFontWeight: string; }) { const safeLeft = Math.round(params.parentWidth * 0.07); const textWidth = Math.max(240, Math.round(params.parentWidth - safeLeft * 2)); const drafts: CanvasTextNodeDraft[] = []; const titleText = String(params.title || "").trim(); const subtitleText = String(params.subtitle || "").trim(); const bodyText = String(params.body || "").trim(); if (titleText) { drafts.push( createTextNodeDraft({ parentId: params.parentNodeId, x: params.parentPosition.x + safeLeft, y: params.parentPosition.y + 72, width: textWidth, height: 140, content: titleText, fontSize: params.screenIndex === 0 ? params.titleFontSize : Math.round(params.titleFontSize * 0.86), fontWeight: params.titleFontWeight, color: "#111827", lineHeight: 1.15, textAlign: "left", }), ); } if (subtitleText) { drafts.push( createTextNodeDraft({ parentId: params.parentNodeId, x: params.parentPosition.x + safeLeft, y: params.parentPosition.y + 214, width: textWidth, height: 120, content: subtitleText, fontSize: params.subtitleFontSize, fontWeight: params.subtitleFontWeight, color: "#374151", lineHeight: 1.22, textAlign: "left", }), ); } if (bodyText) { drafts.push( createTextNodeDraft({ parentId: params.parentNodeId, x: params.parentPosition.x + safeLeft, y: params.parentPosition.y + 304, width: textWidth, height: 260, content: bodyText, fontSize: params.bodyFontSize, fontWeight: params.bodyFontWeight, color: "#4b5563", lineHeight: 1.38, textAlign: "left", }), ); } return drafts; }