variables and templates
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import React, { memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { autocompletion } from '@codemirror/autocomplete'
|
||||
import CodeMirror from '@uiw/react-codemirror'
|
||||
import FlowContext from '../../lib/flowContext'
|
||||
import { useResizeHeight } from '../../hooks/useResizeHeight'
|
||||
import { nunjucksCompletionSource } from '../../lib/nunjucksAutocomplete'
|
||||
import { plantumlLanguage } from '../../lib/plantumlLanguage'
|
||||
import { useTheme } from '../../lib/themeContext'
|
||||
import {
|
||||
@@ -116,7 +118,22 @@ export const ConfigNode = memo(function ConfigNode({ id, data, width, height }:
|
||||
[insertAt]
|
||||
)
|
||||
|
||||
const extensions = useMemo(() => [plantumlLanguage.extension], [])
|
||||
const variableIds = useMemo(() => connectedVariableNodes.map((n: any) => n.id), [connectedVariableNodes])
|
||||
const functionIds = useMemo(() => connectedFunctionNodes.map((n: any) => n.id), [connectedFunctionNodes])
|
||||
const configTitles = useMemo(
|
||||
() => connectedConfigNodes.map((n: any) => n.data?.title ?? n.id),
|
||||
[connectedConfigNodes],
|
||||
)
|
||||
const extensions = useMemo(
|
||||
() => [
|
||||
plantumlLanguage.extension,
|
||||
autocompletion({
|
||||
override: [nunjucksCompletionSource(variableIds, configTitles, functionIds)],
|
||||
activateOnTyping: true,
|
||||
}),
|
||||
],
|
||||
[variableIds, functionIds, configTitles],
|
||||
)
|
||||
const [editorHeight, editorContainerRef] = useResizeHeight(180)
|
||||
|
||||
const dimensions =
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { memo, useContext, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import nunjucks from 'nunjucks'
|
||||
import FlowContext from '../../lib/flowContext'
|
||||
import { useTheme } from '../../lib/themeContext'
|
||||
import { Empty, EmptyHeader, EmptyTitle, EmptyDescription, EmptyContent, EmptyMedia } from '../ui/empty'
|
||||
@@ -188,19 +189,46 @@ export const RenderingNode = memo(function RenderingNode({ id, width, height }:
|
||||
|
||||
if (srcId && srcNode?.type === 'config') configIdsUsed.add(srcId)
|
||||
|
||||
const resolvedPlantuml = resolveIncludes(plantumlText)
|
||||
const resolvedIncludes = resolveIncludes(plantumlText)
|
||||
|
||||
const varMap: Record<string, string> = {}
|
||||
// Use null prototype so keys like "var", "constructor", "toString" don't collide with Object.prototype
|
||||
const nunjucksContext = Object.create(null) as Record<string, unknown>
|
||||
|
||||
for (const e of edges) {
|
||||
if (configIdsUsed.has(e.target)) {
|
||||
const src = nodes.find((n: any) => n.id === e.source)
|
||||
if (src?.type === 'variable') {
|
||||
const v = src.data?.value
|
||||
varMap[src.id] = v === undefined || v === null ? '' : String(v)
|
||||
if (!configIdsUsed.has(e.target)) continue
|
||||
const src = nodes.find((n: any) => n.id === e.source)
|
||||
if (src?.type === 'variable') {
|
||||
const v = src.data?.value
|
||||
const str = v === undefined || v === null ? '' : String(v)
|
||||
varMap[src.id] = str
|
||||
// Keep booleans/numbers for {% if %} etc.; Nunjucks treats "false" as truthy
|
||||
nunjucksContext[src.id] =
|
||||
v === undefined || v === null ? '' : typeof v === 'boolean' || typeof v === 'number' ? v : str
|
||||
} else if (src?.type === 'function') {
|
||||
const body = src.data?.body ?? 'return args[0];'
|
||||
nunjucksContext[src.id] = (...args: unknown[]) => {
|
||||
try {
|
||||
const fn = new Function('args', body)
|
||||
const result = fn(args)
|
||||
if (result === undefined || result === null) return ''
|
||||
if (typeof result === 'string' || typeof result === 'number' || typeof result === 'boolean') return result
|
||||
return String(result)
|
||||
} catch {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let afterNunjucks: string
|
||||
try {
|
||||
const env = new nunjucks.Environment([], { autoescape: false })
|
||||
afterNunjucks = env.renderString(resolvedIncludes, nunjucksContext)
|
||||
} catch (nunjucksErr: any) {
|
||||
throw new Error(`Nunjucks: ${nunjucksErr?.message ?? String(nunjucksErr)}`)
|
||||
}
|
||||
|
||||
const resolveFunctionCalls = (text: string): string => {
|
||||
const fnCallRegex = /\$\{([\w-]+)\s*\(([^)]*)\)\}/g
|
||||
return text.replace(fnCallRegex, (match, funcId: string, argsStr: string) => {
|
||||
@@ -224,7 +252,7 @@ export const RenderingNode = memo(function RenderingNode({ id, width, height }:
|
||||
const resolveVariables = (text: string): string =>
|
||||
text.replace(/\$\{([^}]+)\}/g, (_, varId: string) => varMap[varId.trim()] ?? '')
|
||||
|
||||
const afterFunctions = resolveFunctionCalls(resolvedPlantuml)
|
||||
const afterFunctions = resolveFunctionCalls(afterNunjucks)
|
||||
let resolvedWithVars = resolveVariables(afterFunctions)
|
||||
if (theme === 'dark') {
|
||||
resolvedWithVars = resolvedWithVars.replace(
|
||||
|
||||
Reference in New Issue
Block a user