feat: runnersok
This commit is contained in:
@@ -22,17 +22,9 @@ import {
|
||||
MenubarSubContent,
|
||||
MenubarSubTrigger,
|
||||
} from '@/components/ui/menubar'
|
||||
import { Sparkles, Play, ChevronDown, Loader2, RotateCw } from 'lucide-react'
|
||||
import { Sparkles, Loader2, RotateCw } from 'lucide-react'
|
||||
import { InputHandle, OutputHandle } from '@/components/graph/NodeHandles'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { ButtonGroup } from '@/components/ui/button-group'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { useTheme } from '@/lib/themeContext'
|
||||
import {
|
||||
useRenderingNodeState,
|
||||
@@ -165,95 +157,8 @@ function RenderingNodeComponent({ id, data, width, height, selected }: Props) {
|
||||
: undefined
|
||||
}
|
||||
right={
|
||||
state.incomingIds.length > 0 ? (
|
||||
<div className="flex items-center gap-2 shrink-0">
|
||||
<ButtonGroup className="nodrag nopan">
|
||||
{state.effectiveUpdateMode === 'manual' ? (
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
disabled={state.loading || !state.hasPendingInputs}
|
||||
className="h-7 gap-1.5 rounded-r-none border-r-0 px-2.5 text-xs"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
state.incrementRunTrigger()
|
||||
}}
|
||||
title={
|
||||
state.hasPendingInputs
|
||||
? 'Inputs changed — click to render'
|
||||
: !state.hasPendingInputs
|
||||
? 'No new data to render'
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{state.loading ? (
|
||||
<Loader2 className="size-3.5 animate-spin shrink-0" aria-hidden />
|
||||
) : (
|
||||
<Play className="size-3.5 shrink-0" />
|
||||
)}
|
||||
Run
|
||||
</Button>
|
||||
) : state.loading ? (
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
disabled
|
||||
className="h-7 gap-1.5 rounded-r-none border-r-0 px-2.5 text-xs"
|
||||
aria-label="Updating"
|
||||
>
|
||||
<Loader2 className="size-3.5 animate-spin shrink-0" aria-hidden />
|
||||
</Button>
|
||||
) : null}
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className={`h-7 min-w-[4.5rem] gap-1 pl-2 pr-1.5 text-xs font-normal ${state.effectiveUpdateMode === 'manual' || state.loading ? 'rounded-l-none' : 'rounded-l-md'}`}
|
||||
aria-label="Update mode"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<span className="text-muted-foreground">
|
||||
{state.effectiveUpdateMode === 'manual' ? 'Manual' : 'Auto'}
|
||||
</span>
|
||||
<ChevronDown className="size-3.5 shrink-0 opacity-70" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-64" onClick={(e) => e.stopPropagation()}>
|
||||
<DropdownMenuLabel className="text-xs font-normal text-muted-foreground">
|
||||
When to re-render
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuCheckboxItem
|
||||
checked={state.effectiveUpdateMode === 'auto'}
|
||||
onCheckedChange={(checked) =>
|
||||
checked && state.setUpdateMode('auto')
|
||||
}
|
||||
className="flex flex-col items-start gap-0.5 py-2"
|
||||
>
|
||||
<span className="font-medium">Auto</span>
|
||||
<span className="text-muted-foreground text-xs font-normal">
|
||||
Re-renders when upstream content changes
|
||||
</span>
|
||||
</DropdownMenuCheckboxItem>
|
||||
<DropdownMenuCheckboxItem
|
||||
checked={state.effectiveUpdateMode === 'manual'}
|
||||
onCheckedChange={(checked) =>
|
||||
checked && state.setUpdateMode('manual')
|
||||
}
|
||||
className="flex flex-col items-start gap-0.5 py-2"
|
||||
>
|
||||
<span className="font-medium">Manual</span>
|
||||
<span className="text-muted-foreground text-xs font-normal">
|
||||
Re-renders only when you click Run
|
||||
</span>
|
||||
</DropdownMenuCheckboxItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
state.incomingIds.length > 0 && state.loading ? (
|
||||
<Loader2 className="size-3.5 animate-spin shrink-0 text-muted-foreground" aria-label="Rendering" />
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -76,6 +76,7 @@ import {
|
||||
import { buildSourceSignatures, type NodeLike, type EdgeLike } from '@/lib/graph/renderingSignatures'
|
||||
import { getNodeDisplayStatus, type NodeDisplayStatus } from '@/lib/graph/state'
|
||||
import type { ConfigTypeId, SourceRenderingLogicContext } from '@/lib/graph/rendering'
|
||||
import { useRunStore } from '@/lib/graph/runStore'
|
||||
|
||||
export type OutputMode = 'image' | 'string'
|
||||
|
||||
@@ -207,8 +208,11 @@ export function useRenderingNodeState(
|
||||
() => (srcNode?.type ? getSourceRenderingLogic(srcNode.type as string) : null),
|
||||
[srcNode?.type]
|
||||
)
|
||||
const effectiveUpdateMode = (data?.updateMode ?? sourceLogic?.defaultUpdateMode ?? 'auto') as 'auto' | 'manual'
|
||||
const runTrigger = data?.runTrigger ?? 0
|
||||
// All rendering is now triggered by the global Run button — no auto/manual distinction.
|
||||
const effectiveUpdateMode: 'auto' | 'manual' = 'manual'
|
||||
// Use global run trigger from runStore instead of per-node trigger
|
||||
const globalRunTrigger = useRunStore((s) => s.globalRunTrigger)
|
||||
const runTrigger = globalRunTrigger
|
||||
const outputMode: OutputMode = (data?.outputMode ?? 'image') as OutputMode
|
||||
const setOutputMode = useCallback(
|
||||
(mode: OutputMode) => updateData({ outputMode: mode }),
|
||||
@@ -330,7 +334,7 @@ export function useRenderingNodeState(
|
||||
if (!hasCachedOutput) {
|
||||
setRenderedContent(null)
|
||||
setResolvedContent(null)
|
||||
setError({ kind: 'no-content', message: 'Click Run to render.' })
|
||||
setError({ kind: 'no-content', message: 'Press Run (⌘↵) to render.' })
|
||||
setLoading(false)
|
||||
}
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user