Browse Source

fix(47-02): polish inline parameter panel visuals

Center max-width container, clip rounded bottom corners, improve
chevron visibility with subtle background, remove duplicate model
selector from image/video nodes (already in FloatingNodeHeader),
and unify control sizing across Gemini and LLM selects.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
handoff-20260429-1057
shrimbly 4 months ago
parent
commit
8e44762838
  1. 28
      src/components/nodes/GenerateImageNode.tsx
  2. 18
      src/components/nodes/GenerateVideoNode.tsx
  3. 8
      src/components/nodes/InlineParameterPanel.tsx
  4. 4
      src/components/nodes/LLMGenerateNode.tsx

28
src/components/nodes/GenerateImageNode.tsx

@ -758,24 +758,6 @@ export function GenerateImageNode({ id, data, selected }: NodeProps<NanoBananaNo
onToggle={handleToggleParams} onToggle={handleToggleParams}
nodeId={id} nodeId={id}
> >
{/* Model selector: Browse button + current model display */}
<div className="flex items-center justify-between gap-2">
<div className="flex-1 min-w-0">
<div className="text-[11px] text-neutral-200 truncate">
{displayTitle}
</div>
<div className="text-[9px] text-neutral-500">
{enabledProviders.find(p => p.id === currentProvider)?.name || currentProvider}
</div>
</div>
<button
onClick={() => setIsBrowseDialogOpen(true)}
className="nodrag nopan shrink-0 px-2 py-1 text-[10px] bg-neutral-700 hover:bg-neutral-600 border border-neutral-600 rounded text-neutral-300 transition-colors"
>
Browse
</button>
</div>
{/* Gemini-specific controls - unified styling to match ModelParameters */} {/* Gemini-specific controls - unified styling to match ModelParameters */}
{isGeminiProvider && currentModelId && ( {isGeminiProvider && currentModelId && (
<div className="space-y-1.5"> <div className="space-y-1.5">
@ -785,7 +767,7 @@ export function GenerateImageNode({ id, data, selected }: NodeProps<NanoBananaNo
<select <select
value={currentModelId} value={currentModelId}
onChange={handleModelChange} onChange={handleModelChange}
className="nodrag nopan w-full text-[10px] py-0.5 px-1.5 bg-neutral-800/50 border border-neutral-700 rounded focus:outline-none focus:ring-1 focus:ring-neutral-600 text-neutral-300" className="nodrag nopan w-full text-[9px] py-0.5 px-1 bg-neutral-900/50 border border-neutral-700 rounded focus:outline-none focus:ring-1 focus:ring-neutral-600 text-neutral-300"
> >
{GEMINI_IMAGE_MODELS.map((m) => ( {GEMINI_IMAGE_MODELS.map((m) => (
<option key={m.value} value={m.value}> <option key={m.value} value={m.value}>
@ -801,7 +783,7 @@ export function GenerateImageNode({ id, data, selected }: NodeProps<NanoBananaNo
<select <select
value={nodeData.aspectRatio || "1:1"} value={nodeData.aspectRatio || "1:1"}
onChange={handleAspectRatioChange} onChange={handleAspectRatioChange}
className="nodrag nopan w-full text-[10px] py-0.5 px-1.5 bg-neutral-800/50 border border-neutral-700 rounded focus:outline-none focus:ring-1 focus:ring-neutral-600 text-neutral-300" className="nodrag nopan w-full text-[9px] py-0.5 px-1 bg-neutral-900/50 border border-neutral-700 rounded focus:outline-none focus:ring-1 focus:ring-neutral-600 text-neutral-300"
> >
{aspectRatios.map((ratio) => ( {aspectRatios.map((ratio) => (
<option key={ratio} value={ratio}> <option key={ratio} value={ratio}>
@ -818,7 +800,7 @@ export function GenerateImageNode({ id, data, selected }: NodeProps<NanoBananaNo
<select <select
value={nodeData.resolution || "2K"} value={nodeData.resolution || "2K"}
onChange={handleResolutionChange} onChange={handleResolutionChange}
className="nodrag nopan w-full text-[10px] py-0.5 px-1.5 bg-neutral-800/50 border border-neutral-700 rounded focus:outline-none focus:ring-1 focus:ring-neutral-600 text-neutral-300" className="nodrag nopan w-full text-[9px] py-0.5 px-1 bg-neutral-900/50 border border-neutral-700 rounded focus:outline-none focus:ring-1 focus:ring-neutral-600 text-neutral-300"
> >
{resolutions.map((res) => ( {resolutions.map((res) => (
<option key={res} value={res}> <option key={res} value={res}>
@ -831,7 +813,7 @@ export function GenerateImageNode({ id, data, selected }: NodeProps<NanoBananaNo
{/* Google Search toggle */} {/* Google Search toggle */}
{(currentModelId === "nano-banana-pro" || currentModelId === "nano-banana-2") && ( {(currentModelId === "nano-banana-pro" || currentModelId === "nano-banana-2") && (
<label className="flex items-center gap-1.5 text-[10px] text-neutral-300 cursor-pointer"> <label className="flex items-center gap-1.5 text-[9px] text-neutral-300 cursor-pointer">
<input <input
type="checkbox" type="checkbox"
checked={nodeData.useGoogleSearch || false} checked={nodeData.useGoogleSearch || false}
@ -844,7 +826,7 @@ export function GenerateImageNode({ id, data, selected }: NodeProps<NanoBananaNo
{/* Image Search toggle (NB2 only) */} {/* Image Search toggle (NB2 only) */}
{currentModelId === "nano-banana-2" && ( {currentModelId === "nano-banana-2" && (
<label className="flex items-center gap-1.5 text-[10px] text-neutral-300 cursor-pointer"> <label className="flex items-center gap-1.5 text-[9px] text-neutral-300 cursor-pointer">
<input <input
type="checkbox" type="checkbox"
checked={nodeData.useImageSearch || false} checked={nodeData.useImageSearch || false}

18
src/components/nodes/GenerateVideoNode.tsx

@ -821,24 +821,6 @@ export function GenerateVideoNode({ id, data, selected }: NodeProps<GenerateVide
onToggle={handleToggleParams} onToggle={handleToggleParams}
nodeId={id} nodeId={id}
> >
{/* Model selector: Browse button + current model display */}
<div className="flex items-center justify-between gap-2">
<div className="flex-1 min-w-0">
<div className="text-[11px] text-neutral-200 truncate">
{displayTitle}
</div>
<div className="text-[9px] text-neutral-500">
{enabledProviders.find(p => p.id === currentProvider)?.name || currentProvider}
</div>
</div>
<button
onClick={() => setIsBrowseDialogOpen(true)}
className="nodrag nopan shrink-0 px-2 py-1 text-[10px] bg-neutral-700 hover:bg-neutral-600 border border-neutral-600 rounded text-neutral-300 transition-colors"
>
Browse
</button>
</div>
{/* External provider parameters - reuse ModelParameters component */} {/* External provider parameters - reuse ModelParameters component */}
{nodeData.selectedModel?.modelId && !isVeoModel(nodeData.selectedModel.modelId) && ( {nodeData.selectedModel?.modelId && !isVeoModel(nodeData.selectedModel.modelId) && (
<ModelParameters <ModelParameters

8
src/components/nodes/InlineParameterPanel.tsx

@ -25,13 +25,13 @@ export function InlineParameterPanel({
<button <button
type="button" type="button"
onClick={onToggle} onClick={onToggle}
className="nodrag nopan w-full flex items-center justify-center py-1 text-neutral-500 hover:text-neutral-300 transition-colors" className="nodrag nopan w-full flex items-center justify-center py-1.5 bg-neutral-800/80 text-neutral-400 hover:text-neutral-200 transition-colors"
aria-label={expanded ? "Collapse parameters" : "Expand parameters"} aria-label={expanded ? "Collapse parameters" : "Expand parameters"}
aria-expanded={expanded} aria-expanded={expanded}
aria-controls={`params-${nodeId}`} aria-controls={`params-${nodeId}`}
> >
<svg <svg
className="w-4 h-4 transition-transform duration-200" className="w-3.5 h-3.5 transition-transform duration-200"
style={{ transform: expanded ? "rotate(180deg)" : "rotate(0deg)" }} style={{ transform: expanded ? "rotate(180deg)" : "rotate(0deg)" }}
viewBox="0 0 24 24" viewBox="0 0 24 24"
fill="none" fill="none"
@ -53,8 +53,8 @@ export function InlineParameterPanel({
opacity: expanded ? 1 : 0, opacity: expanded ? 1 : 0,
}} }}
> >
<div className="nodrag nopan nowheel bg-neutral-900 px-3 pb-3"> <div className="nodrag nopan nowheel bg-neutral-900 px-3 pt-2 pb-3 rounded-b-lg">
<div className="space-y-2 max-w-[280px]">{children}</div> <div className="space-y-2 max-w-[280px] mx-auto">{children}</div>
</div> </div>
</div> </div>
</div> </div>

4
src/components/nodes/LLMGenerateNode.tsx

@ -252,7 +252,7 @@ export function LLMGenerateNode({ id, data, selected }: NodeProps<LLMGenerateNod
<select <select
value={provider} value={provider}
onChange={handleProviderChange} onChange={handleProviderChange}
className="nodrag nopan w-full text-[10px] py-0.5 px-1.5 bg-neutral-800/50 border border-neutral-700 rounded focus:outline-none focus:ring-1 focus:ring-neutral-600 text-neutral-300" className="nodrag nopan w-full text-[9px] py-0.5 px-1 bg-neutral-900/50 border border-neutral-700 rounded focus:outline-none focus:ring-1 focus:ring-neutral-600 text-neutral-300"
> >
{LLM_PROVIDERS.map(p => ( {LLM_PROVIDERS.map(p => (
<option key={p.value} value={p.value}>{p.label}</option> <option key={p.value} value={p.value}>{p.label}</option>
@ -266,7 +266,7 @@ export function LLMGenerateNode({ id, data, selected }: NodeProps<LLMGenerateNod
<select <select
value={nodeData.model || availableModels[0].value} value={nodeData.model || availableModels[0].value}
onChange={handleModelChange} onChange={handleModelChange}
className="nodrag nopan w-full text-[10px] py-0.5 px-1.5 bg-neutral-800/50 border border-neutral-700 rounded focus:outline-none focus:ring-1 focus:ring-neutral-600 text-neutral-300" className="nodrag nopan w-full text-[9px] py-0.5 px-1 bg-neutral-900/50 border border-neutral-700 rounded focus:outline-none focus:ring-1 focus:ring-neutral-600 text-neutral-300"
> >
{availableModels.map(m => ( {availableModels.map(m => (
<option key={m.value} value={m.value}>{m.label}</option> <option key={m.value} value={m.value}>{m.label}</option>

Loading…
Cancel
Save