fix: nunjuclk rendering
This commit is contained in:
@@ -480,12 +480,14 @@ function RenderingNodeComponent({ id, data, width, height, selected }: Props) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setResolvedContent(afterNunjucks)
|
// Collapse runs of newlines so {% for %} / {{ }} on their own lines don't leave blank lines
|
||||||
|
const resolved = afterNunjucks.replace(/(\r?\n)(\s*\r?\n)+/g, '$1').trim()
|
||||||
|
setResolvedContent(resolved)
|
||||||
const typeRenderer = getConfigType(configTypeId)
|
const typeRenderer = getConfigType(configTypeId)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const renderOptions = configTypeId === 'wireframe' ? { width: viewportWidth, height: viewportHeight } : undefined
|
const renderOptions = configTypeId === 'wireframe' ? { width: viewportWidth, height: viewportHeight } : undefined
|
||||||
const htmlOrSvg = await typeRenderer.render(afterNunjucks, renderOptions)
|
const htmlOrSvg = await typeRenderer.render(resolved, renderOptions)
|
||||||
if (cancelled || thisRunId !== runIdRef.current) return
|
if (cancelled || thisRunId !== runIdRef.current) return
|
||||||
setRenderedContent(htmlOrSvg)
|
setRenderedContent(htmlOrSvg)
|
||||||
setError(null)
|
setError(null)
|
||||||
@@ -539,6 +541,18 @@ function RenderingNodeComponent({ id, data, width, height, selected }: Props) {
|
|||||||
// Kroki and other SVG sources may prepend <?xml ... ?> so we detect by presence of <svg> tag
|
// Kroki and other SVG sources may prepend <?xml ... ?> so we detect by presence of <svg> tag
|
||||||
const isSvgOutput = Boolean(renderedContent?.trim() && /<svg[\s>]/i.test(renderedContent.trim()))
|
const isSvgOutput = Boolean(renderedContent?.trim() && /<svg[\s>]/i.test(renderedContent.trim()))
|
||||||
|
|
||||||
|
/** Strip remaining Nunjucks tags from resolved content for display in raw view (so tags don't show as literal lines). */
|
||||||
|
const rawDisplayContent = useMemo(() => {
|
||||||
|
if (resolvedContent == null) return ''
|
||||||
|
return resolvedContent
|
||||||
|
.replace(/\{%[\s\S]*?%\}/g, '')
|
||||||
|
.replace(/\{\{[\s\S]*?\}\}/g, '')
|
||||||
|
.replace(/\{#[\s\S]*?#\}/g, '')
|
||||||
|
.replace(/(\r?\n)\s*(\r?\n)/g, '$1$2')
|
||||||
|
.replace(/^\s*\n|\n\s*$/g, (m) => (m === '\n' ? '\n' : ''))
|
||||||
|
.trim()
|
||||||
|
}, [resolvedContent])
|
||||||
|
|
||||||
/** Process SVG HTML so it keeps aspect ratio and fills the viewport (used only for display, not download). */
|
/** Process SVG HTML so it keeps aspect ratio and fills the viewport (used only for display, not download). */
|
||||||
const displayContent = useMemo(() => {
|
const displayContent = useMemo(() => {
|
||||||
if (!renderedContent || !isSvgOutput) return renderedContent
|
if (!renderedContent || !isSvgOutput) return renderedContent
|
||||||
@@ -650,16 +664,34 @@ function RenderingNodeComponent({ id, data, width, height, selected }: Props) {
|
|||||||
<ToggleGroup
|
<ToggleGroup
|
||||||
type="single"
|
type="single"
|
||||||
value={viewMode}
|
value={viewMode}
|
||||||
onValueChange={(v) => v && (v === 'preview' || v === 'raw') && setViewMode(v)}
|
onValueChange={(v) => {
|
||||||
|
if (v === 'preview' || v === 'raw') setViewMode(v)
|
||||||
|
}}
|
||||||
aria-label="View mode"
|
aria-label="View mode"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="gap-0 rounded-md p-0.5 [&>button]:rounded-none [&>button:first-child]:rounded-l-md [&>button:last-child]:rounded-r-md [&>button:not(:first-child)]:border-l-0"
|
className="gap-0 rounded-md p-0.5 [&>button]:rounded-none [&>button:first-child]:rounded-l-md [&>button:last-child]:rounded-r-md [&>button:not(:first-child)]:border-l-0"
|
||||||
>
|
>
|
||||||
<ToggleGroupItem value="preview" aria-label="Preview" className="gap-1.5 px-2.5 h-7">
|
<ToggleGroupItem
|
||||||
|
value="preview"
|
||||||
|
aria-label="Preview"
|
||||||
|
className="gap-1.5 px-2.5 h-7"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
setViewMode('preview')
|
||||||
|
}}
|
||||||
|
>
|
||||||
Preview
|
Preview
|
||||||
</ToggleGroupItem>
|
</ToggleGroupItem>
|
||||||
<ToggleGroupItem value="raw" aria-label="Raw config" className="gap-1.5 px-2.5 h-7">
|
<ToggleGroupItem
|
||||||
|
value="raw"
|
||||||
|
aria-label="Raw config"
|
||||||
|
className="gap-1.5 px-2.5 h-7"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
setViewMode('raw')
|
||||||
|
}}
|
||||||
|
>
|
||||||
Raw
|
Raw
|
||||||
</ToggleGroupItem>
|
</ToggleGroupItem>
|
||||||
</ToggleGroup>
|
</ToggleGroup>
|
||||||
@@ -800,18 +832,18 @@ function RenderingNodeComponent({ id, data, width, height, selected }: Props) {
|
|||||||
size="icon"
|
size="icon"
|
||||||
className="absolute top-2 right-2 z-10 h-7 w-7 shrink-0 rounded-md shadow-sm"
|
className="absolute top-2 right-2 z-10 h-7 w-7 shrink-0 rounded-md shadow-sm"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const text = resolvedContent ?? ''
|
const text = rawDisplayContent
|
||||||
if (text) {
|
if (text) {
|
||||||
navigator.clipboard?.writeText(text).then(() => toast.success('Copied to clipboard')).catch(() => {})
|
navigator.clipboard?.writeText(text).then(() => toast.success('Copied to clipboard')).catch(() => {})
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
disabled={!resolvedContent}
|
disabled={!rawDisplayContent}
|
||||||
title="Copy raw output"
|
title="Copy raw output"
|
||||||
>
|
>
|
||||||
<Copy className="size-3.5" />
|
<Copy className="size-3.5" />
|
||||||
</Button>
|
</Button>
|
||||||
<CodeMirror
|
<CodeMirror
|
||||||
value={resolvedContent ?? ''}
|
value={rawDisplayContent}
|
||||||
height={`${rawEditorHeight}px`}
|
height={`${rawEditorHeight}px`}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
extensions={rawExtensions}
|
extensions={rawExtensions}
|
||||||
@@ -917,7 +949,7 @@ function RenderingNodeComponent({ id, data, width, height, selected }: Props) {
|
|||||||
<BaseNodeFooter>
|
<BaseNodeFooter>
|
||||||
<NodeFooterEdgeIndicators nodeId={id} nodeType="render">
|
<NodeFooterEdgeIndicators nodeId={id} nodeType="render">
|
||||||
{viewMode === 'raw'
|
{viewMode === 'raw'
|
||||||
? (resolvedContent != null ? `Raw · ${resolvedContent.length} chars` : '—')
|
? (rawDisplayContent ? `Raw · ${rawDisplayContent.length} chars` : '—')
|
||||||
: renderedContent
|
: renderedContent
|
||||||
? `${configType.label} · ${renderedContent.length} chars`
|
? `${configType.label} · ${renderedContent.length} chars`
|
||||||
: error
|
: error
|
||||||
|
|||||||
Reference in New Issue
Block a user