feat: full screen node

This commit is contained in:
2026-03-11 20:22:32 +01:00
parent 0b8c18a43f
commit 86d264e83a
11 changed files with 151 additions and 12 deletions

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useMemo, useState } from 'react'
import React, { useCallback, useContext, useMemo, useState } from 'react'
import {
AbstractNodeProps,
createAbstractNodeComponent,
@@ -15,6 +15,8 @@ import { NodeMenubar } from '@/components/base/NodeMenubar'
import { NodeFooterEdgeIndicators } from '@/components/base/NodeFooterEdgeIndicators'
import { NodeHeaderTitle } from '@/components/base/NodeHeaderTitle'
import { InputHandle, OutputHandle } from '@/components/base/NodeHandles'
import FlowContext from '@/lib/flowContext'
import { getNodeType } from '@/lib/nodeRegistry'
import { Button } from '@/components/ui/button'
import { usePlatform } from '@/app/kosmos/KosmosContext'
import { Bot, Play, Loader2 } from 'lucide-react'
@@ -51,6 +53,9 @@ function serializeNodeForContext(nodes: { id: string; type?: string; data?: unkn
}
function AgentNodeComponent({ id, data, width, height, selected }: Props) {
const flowContext = useContext(FlowContext)
const setFullscreenNodeId = flowContext?.setFullscreenNodeId
const supportsFullscreen = getNodeType('agent')?.supportsFullscreen
const { nodes, sourceIds, updateData } = useAbstractNode<AgentNodeData>(id, data ?? {})
const { aiConnection } = usePlatform()
const [running, setRunning] = useState(false)
@@ -137,6 +142,7 @@ function AgentNodeComponent({ id, data, width, height, selected }: Props) {
<BaseNodeHeaderRow
icon={<Bot className="size-4" />}
title={<NodeHeaderTitle nodeId={id} displayTitle={id} />}
onHeaderDoubleClick={supportsFullscreen && setFullscreenNodeId ? () => setFullscreenNodeId(id) : undefined}
/>
<BaseNodeContent>
<div className="shrink-0 w-full">

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useMemo, useRef } from 'react'
import React, { useCallback, useContext, useMemo, useRef } from 'react'
import { autocompletion } from '@codemirror/autocomplete'
import CodeMirror from '@uiw/react-codemirror'
import { javascript } from '@codemirror/lang-javascript'
@@ -42,12 +42,17 @@ import { InputHandle, OutputHandle } from '../base/NodeHandles'
import { NodeFooterEdgeIndicators } from '../base/NodeFooterEdgeIndicators'
import { NodeHeaderTitle } from '../base/NodeHeaderTitle'
import { NodeMenubar } from '../base/NodeMenubar'
import FlowContext from '../../lib/flowContext'
import { getNodeType } from '../../lib/nodeRegistry'
export type ConfigNodeData = { configType?: ConfigTypeId; content?: string; title?: string; /** @deprecated use content */ plantuml?: string }
type Props = AbstractNodeProps<ConfigNodeData>
function ConfigNodeComponent({ id, data, width, height, selected }: Props) {
const flowContext = useContext(FlowContext)
const setFullscreenNodeId = flowContext?.setFullscreenNodeId
const supportsFullscreen = getNodeType('config')?.supportsFullscreen
const configTypeId = getConfigTypeId(data ?? {})
const configType = getConfigType(configTypeId)
const content = getConfigContent(data ?? {})
@@ -262,6 +267,7 @@ function ConfigNodeComponent({ id, data, width, height, selected }: Props) {
</SelectContent>
</Select>
}
onHeaderDoubleClick={supportsFullscreen && setFullscreenNodeId ? () => setFullscreenNodeId(id) : undefined}
/>
<BaseNodeContent>

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useMemo, useRef } from 'react'
import React, { useCallback, useContext, useMemo, useRef } from 'react'
import CodeMirror from '@uiw/react-codemirror'
import { javascript } from '@codemirror/lang-javascript'
import {
@@ -19,6 +19,8 @@ import { InputHandle, OutputHandle } from '../base/NodeHandles'
import { NodeFooterEdgeIndicators } from '../base/NodeFooterEdgeIndicators'
import { NodeHeaderTitle } from '../base/NodeHeaderTitle'
import { NodeMenubar } from '../base/NodeMenubar'
import FlowContext from '../../lib/flowContext'
import { getNodeType } from '../../lib/nodeRegistry'
import { MenubarItem, MenubarShortcut } from '../ui/menubar'
import { Kbd } from '../ui/kbd'
import { Code2, Variable } from 'lucide-react'
@@ -28,6 +30,9 @@ export type FunctionNodeData = { body?: string }
type Props = AbstractNodeProps<FunctionNodeData>
function FunctionNodeComponent({ id, data, width, height, selected }: Props) {
const flowContext = useContext(FlowContext)
const setFullscreenNodeId = flowContext?.setFullscreenNodeId
const supportsFullscreen = getNodeType('function')?.supportsFullscreen
const bodyValue = data?.body ?? ''
const { theme } = useTheme()
const { nodes, sourceIds, updateData } = useAbstractNode<FunctionNodeData>(id, data ?? {})
@@ -92,7 +97,11 @@ function FunctionNodeComponent({ id, data, width, height, selected }: Props) {
return (
<BaseNode className="min-w-72 min-h-[260px]" dimensions={dimensions} resizable nodeId={id} selected={selected} handles={<><InputHandle id="in" nodeId={id} /><OutputHandle id="out" /></>}>
<BaseNodeHeaderRow icon={<Code2 className="size-4" />} title={<NodeHeaderTitle nodeId={id} displayTitle={id} />} />
<BaseNodeHeaderRow
icon={<Code2 className="size-4" />}
title={<NodeHeaderTitle nodeId={id} displayTitle={id} />}
onHeaderDoubleClick={supportsFullscreen && setFullscreenNodeId ? () => setFullscreenNodeId(id) : undefined}
/>
<BaseNodeContent>
<div className="shrink-0 w-full">

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'
import nunjucks from 'nunjucks'
import CodeMirror from '@uiw/react-codemirror'
import { javascript } from '@codemirror/lang-javascript'
@@ -19,7 +19,8 @@ import {
BaseNodeHeaderRow,
} from '../base/BaseNode'
import { getDefaultDataForType, getNextNodeId } from '../../lib/flowUtils'
import { getDefaultStyle } from '../../lib/nodeRegistry'
import FlowContext from '../../lib/flowContext'
import { getDefaultStyle, getNodeType } from '../../lib/nodeRegistry'
import { NodeFooterEdgeIndicators } from '../base/NodeFooterEdgeIndicators'
import { NodeHeaderTitle } from '../base/NodeHeaderTitle'
import { NodeMenubar } from '../base/NodeMenubar'
@@ -47,6 +48,9 @@ type Props = AbstractNodeProps<RenderingNodeData>
type ViewMode = 'preview' | 'raw'
function RenderingNodeComponent({ id, data, width, height, selected }: Props) {
const flowContext = useContext(FlowContext)
const setFullscreenNodeId = flowContext?.setFullscreenNodeId
const supportsFullscreen = getNodeType('render')?.supportsFullscreen
const [renderedContent, setRenderedContent] = useState<string | null>(null)
const [resolvedContent, setResolvedContent] = useState<string | null>(null)
const [error, setError] = useState<null | { kind: string; message: string }>(null)
@@ -675,6 +679,7 @@ function RenderingNodeComponent({ id, data, width, height, selected }: Props) {
<BaseNodeHeaderRow
icon={<Sparkles className="size-4" />}
title={<NodeHeaderTitle nodeId={id} displayTitle={id} />}
onHeaderDoubleClick={supportsFullscreen && setFullscreenNodeId ? () => setFullscreenNodeId(id) : undefined}
right={
incomingIds.length > 0 ? (
<ToggleGroup