Browse Source

feat: refine fallback button UI and model search dialog title

- Update fallback button icon and color scheme (emerald -> blue)
- Move fallback button into headerAction alongside browse button
- Add custom title prop to ModelSearchDialog for fallback context

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
handoff-20260429-1057
shrimbly 3 months ago
parent
commit
b2555ffc39
  1. 20
      src/components/WorkflowCanvas.tsx
  2. 5
      src/components/modals/ModelSearchDialog.tsx

20
src/components/WorkflowCanvas.tsx

@ -2307,22 +2307,19 @@ export function WorkflowCanvas() {
if (cap) setFallbackDialogState({ nodeId: node.id, capability: cap }); if (cap) setFallbackDialogState({ nodeId: node.id, capability: cap });
} }
}} }}
className={`nodrag nopan p-0.5 rounded transition-colors border ${ className={`nodrag nopan p-0.5 rounded transition-colors border flex items-center ${
hasFallback hasFallback
? "text-emerald-400 border-emerald-600/60 hover:text-emerald-300" ? "text-blue-400 border-blue-600/60 hover:text-blue-200"
: "text-neutral-500 border-neutral-600 hover:text-neutral-200" : "text-neutral-500 border-neutral-600 hover:text-neutral-200"
}`} }`}
title={hasFallback ? `Fallback: ${fallbackName}` : "Set fallback model (runs if primary fails)"} title={hasFallback ? `Fallback: ${fallbackName}` : "Set fallback model (runs if primary fails)"}
> >
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}> <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M12 2.25l8.25 3v6.75c0 4.5-3.375 8.25-8.25 9.75-4.875-1.5-8.25-5.25-8.25-9.75V5.25L12 2.25z" /> <path strokeLinecap="round" strokeLinejoin="round" d="M4 12a8 8 0 0 1 16 0M12 4v8M8 12Q9 7 12 4M16 12Q15 7 12 4M4 12l8 8M20 12l-8 8M11 20h2" />
{hasFallback && (
<path strokeLinecap="round" strokeLinejoin="round" d="M9 12l2 2 4-4" />
)}
</svg> </svg>
</button> </button>
{hasFallback && ( {hasFallback && (
<span className="absolute -top-0.5 -right-0.5 w-1.5 h-1.5 rounded-full bg-emerald-400 ring-1 ring-neutral-900 pointer-events-none" /> <span className="absolute -top-0.5 -right-0.5 w-1.5 h-1.5 rounded-full bg-blue-400 ring-1 ring-neutral-900 pointer-events-none" />
)} )}
</div> </div>
) : undefined; ) : undefined;
@ -2342,9 +2339,13 @@ export function WorkflowCanvas() {
customTitle={node.data?.customTitle} customTitle={node.data?.customTitle}
comment={node.data?.comment} comment={node.data?.comment}
provider={(node.data as any)?.selectedModel?.provider} provider={(node.data as any)?.selectedModel?.provider}
headerAction={browseAction} headerAction={(browseAction || fallbackButton) ? (
<>
{browseAction}
{fallbackButton}
</>
) : undefined}
headerButtons={optionalToggle} headerButtons={optionalToggle}
alwaysVisibleButtons={fallbackButton}
onCustomTitleChange={handleCustomTitleChange} onCustomTitleChange={handleCustomTitleChange}
onCommentChange={handleCommentChange} onCommentChange={handleCommentChange}
onRunNode={handleRunNode} onRunNode={handleRunNode}
@ -2483,6 +2484,7 @@ export function WorkflowCanvas() {
<ModelSearchDialog <ModelSearchDialog
isOpen isOpen
onClose={() => setFallbackDialogState(null)} onClose={() => setFallbackDialogState(null)}
title="Select fallback model"
initialCapabilityFilter={fallbackDialogState.capability} initialCapabilityFilter={fallbackDialogState.capability}
showClearOption showClearOption
onClearSelection={() => { onClearSelection={() => {

5
src/components/modals/ModelSearchDialog.tsx

@ -113,6 +113,8 @@ interface ModelSearchDialogProps {
showClearOption?: boolean; showClearOption?: boolean;
/** Callback when the "Remove fallback" row is clicked */ /** Callback when the "Remove fallback" row is clicked */
onClearSelection?: () => void; onClearSelection?: () => void;
/** Custom dialog title (defaults to "Browse Models") */
title?: string;
} }
export function ModelSearchDialog({ export function ModelSearchDialog({
@ -123,6 +125,7 @@ export function ModelSearchDialog({
initialCapabilityFilter, initialCapabilityFilter,
showClearOption, showClearOption,
onClearSelection, onClearSelection,
title = "Browse Models",
}: ModelSearchDialogProps) { }: ModelSearchDialogProps) {
const { const {
addNode, addNode,
@ -577,7 +580,7 @@ export function ModelSearchDialog({
{/* Header */} {/* Header */}
<div className="flex items-center justify-between px-6 py-4 border-b border-neutral-700"> <div className="flex items-center justify-between px-6 py-4 border-b border-neutral-700">
<h2 className="text-lg font-semibold text-neutral-100"> <h2 className="text-lg font-semibold text-neutral-100">
Browse Models {title}
</h2> </h2>
<button <button
onClick={onClose} onClick={onClose}

Loading…
Cancel
Save