refactoring

This commit is contained in:
2026-03-12 17:07:42 +01:00
parent 084863909a
commit e29c5d643c
11 changed files with 136 additions and 87 deletions

View File

@@ -1,5 +1,7 @@
/**
* Central node and edge types for the app. Use AppNode / AppEdge in graph state and context.
* React Flow node/edge types for the app (graph state and context). AppNode / AppEdge
* describe the shape of nodes and edges; the node type *id* (e.g. 'config', 'render')
* lives in nodeRegistry and is stored as node.type.
*/
import type { Node, Edge } from '@xyflow/react'

View File

@@ -13,15 +13,20 @@
*
* 3. **Display** — The Rendering node picks a view (image viewport vs markdown content)
* from `outputType` on the renderer and shows the result. Output menu items (e.g. Export)
* come from the source/renderer via `outputMenuDescriptor`.
* come from the source node descriptor's getOutputMenuContent (see NodeTypeDescriptor).
*
* ## Implementing a new source
* - Implement `IRenderingSource` (resolve + optional output menu).
* - Register with `registerSourceRenderingLogic(nodeType, logic)`.
* - Implement source rendering logic (resolve step) and set it on the node descriptor
* via .sourceRenderingLogic(). Optionally set .outputMenuContent() for the Output menu.
* - The descriptor is registered via registerNodeType(); source logic is registered automatically.
*
* ## Implementing a new output type
* - Add a config type (or equivalent) fulfilling `IOutputTypeRenderer`.
* - Register in CONFIG_TYPES and use `getConfigType(id)` in the resolve step.
*
* ## Shared helpers
* - templateRefs.ts: isReachable, resolveExtendsRef, getTemplateRefs (Nunjucks extends/include/import).
* Used by config resolve and the rendering node hook so template/reachability logic stays in one place.
*/
import type {
@@ -60,8 +65,7 @@ export interface IResolveResult {
/**
* Contract for a node type that can feed the Rendering node (config, agent, etc.).
* Register via registerSourceRenderingLogic(nodeType, logic).
* Output menu content is registered per output type in the frontend (outputMenuRegistry).
* Set on the node descriptor via .sourceRenderingLogic(); output menu via .outputMenuContent().
*/
export interface IRenderingSource {
/** When to re-run: 'auto' on upstream changes, 'manual' only on Run. */

View File

@@ -30,7 +30,7 @@ export type ResolvedContentResult = {
reasoning?: string
}
/** Source logic implementation. Implements {@link IRenderingSource}. Output menu content is provided per output type via the frontend registry (see outputMenuRegistry). */
/** Source logic implementation. Implements {@link IRenderingSource}. Output menu is provided by the node descriptor's getOutputMenuContent. */
export type SourceRenderingLogic = {
defaultUpdateMode: 'auto' | 'manual'
getResolvedContent: (context: SourceRenderingLogicContext) => Promise<ResolvedContentResult>

View File

@@ -0,0 +1,54 @@
/**
* Shared helpers for Nunjucks template references (extends/include/import) and
* graph reachability. Used by config resolve logic and the rendering node hook
* so template parsing and "connected configs" stay in one place.
*/
export type EdgeLike = { source: string; target: string }
export type NodeLike = { id: string; data?: unknown }
/** BFS: is target reachable from start following directed edges? */
export function isReachable(
edges: EdgeLike[],
startId: string,
targetId: string
): boolean {
const q: string[] = [startId]
const seen = new Set<string>([startId])
while (q.length) {
const cur = q.shift()!
if (cur === targetId) return true
for (const e of edges) {
if (e.source === cur && !seen.has(e.target)) {
seen.add(e.target)
q.push(e.target)
}
}
}
return false
}
/** Resolve a template name to a node id (by id or by data.title). */
export function resolveExtendsRef(nodes: NodeLike[], name: string): string {
const refName = name.replace(/\.(puml|html)$/, '').trim()
return (
nodes.find(
(n) =>
n.id === refName ||
(n.data as Record<string, unknown>)?.title === refName
)?.id ?? refName
)
}
/** Extract template refs from content: extends "x", include "y", import "z" as ... */
export function getTemplateRefs(content: string): string[] {
const refs: string[] = []
const extendMatch = content.match(/\{\%\s*extends\s+["']([^"']+)["']\s*\%\}/)
if (extendMatch) refs.push(extendMatch[1].trim())
const includeRegex = /\{\%\s*include\s+["']([^"']+)["']\s*\%\}/g
let m
while ((m = includeRegex.exec(content)) !== null) refs.push(m[1].trim())
const importRegex = /\{\%\s*import\s+["']([^"']+)["']\s+as\s+\w+\s*\%\}/g
while ((m = importRegex.exec(content)) !== null) refs.push(m[1].trim())
return refs
}