2025-12-06 12:46:40 +09:00
|
|
|
"use client"
|
2025-03-22 13:15:51 +00:00
|
|
|
|
2025-12-06 12:46:40 +09:00
|
|
|
import {
|
|
|
|
|
Download,
|
|
|
|
|
History,
|
|
|
|
|
Image as ImageIcon,
|
|
|
|
|
Loader2,
|
|
|
|
|
Send,
|
|
|
|
|
Trash2,
|
|
|
|
|
} from "lucide-react"
|
|
|
|
|
import type React from "react"
|
|
|
|
|
import { useCallback, useEffect, useRef, useState } from "react"
|
|
|
|
|
import { toast } from "sonner"
|
|
|
|
|
import { ButtonWithTooltip } from "@/components/button-with-tooltip"
|
|
|
|
|
import { ErrorToast } from "@/components/error-toast"
|
|
|
|
|
import { HistoryDialog } from "@/components/history-dialog"
|
feat: multi-provider model configuration with UI/UX improvements (#355)
* feat: add multi-provider model configuration
- Add model config dialog for managing multiple AI providers
- Support for OpenAI, Anthropic, Google, Azure, Bedrock, OpenRouter, DeepSeek, SiliconFlow, Ollama, and AI Gateway
- Add model selector dropdown in chat panel header
- Add API key validation endpoint
- Add custom model ID input with keyboard navigation
- Fix hover highlight in Command component
- Add suggested models for each provider including latest Claude 4.5 series
- Store configuration locally in browser
* feat: improve model config UI and move selector to chat input
- Move model selector from header to chat input (left of send button)
- Add per-model validation status (queued, running, valid, invalid)
- Filter model selector to only show verified models
- Add editable model IDs in config dialog
- Add custom model input field alongside suggested models dropdown
- Fix hover states on provider buttons and select triggers
- Update OpenAI suggested models with GPT-5 series
- Add alert-dialog component for delete confirmation
* refactor: revert shadcn component changes, apply hover fix at usage site
* feat: add AWS credentials support for Bedrock provider
- Add AWS Access Key ID, Secret Access Key, Region fields for Bedrock
- Show different credential fields based on provider type
- Update validation API to handle Bedrock with AWS credentials
- Add region selector with common AWS regions
* fix: reset Test button after validation completes
* fix: reset validation button to Test after success
* fix: complete bedrock support and UI/UX improvements
- Add bedrock to ALLOWED_CLIENT_PROVIDERS for client credentials
- Pass AWS credentials through full chain (headers → API → provider)
- Replace non-existent GPT-5 models with real ones (o1, o3-mini)
- Add accessibility: aria-labels, focus-visible rings, inline errors
- Add more AWS regions (Ohio, London, Paris, Mumbai, Seoul, São Paulo)
- Fix setTimeout cleanup with useRef on component unmount
- Fix TypeScript type consistency in getSelectedAIConfig fallback
* chore: remove unused code
- Remove unused setAccessCodeRequired state in chat-panel.tsx
- Remove unused getSelectedModel export in model-config.ts
* fix: UI/UX improvements for model configuration dialog
- Add gradient header styling with icon badge
- Change Configuration section icon from Key to Settings2
- Add duplicate model detection with warning banner and inline removal
- Filter out already-added models from suggestions dropdown
- Add type-to-confirm for deleting providers with 3+ models
- Enhance delete confirmation dialog with warning icon
- Improve model selector discoverability (show model name + chevron)
- Add truncation for long model names with title tooltip
- Remove AI provider settings from Settings dialog (now in Model Config)
- Extract ValidationButton into reusable component
* fix: prevent duplicate model IDs within same provider
- Block adding model if ID already exists in provider
- Block editing model ID to match existing model in provider
* fix: improve duplicate model ID notifications
- Add toast notification when trying to add duplicate model
- Allow free typing when editing model ID, validate on blur
- Show warning toast instead of blocking input
* fix: improve duplicate model validation UX in config dialog
- Add inline error display for duplicate model IDs
- Show red border on input when error exists
- Validate on blur with shake animation for edit errors
- Prevent saving empty model names
- Clear errors when user starts typing
- Simplify error styling (small red text, no heavy chips)
2025-12-22 22:36:36 +09:00
|
|
|
import { ModelSelector } from "@/components/model-selector"
|
2025-12-06 12:46:40 +09:00
|
|
|
import { ResetWarningModal } from "@/components/reset-warning-modal"
|
|
|
|
|
import { SaveDialog } from "@/components/save-dialog"
|
2025-12-28 18:46:10 +05:30
|
|
|
|
2025-12-06 12:46:40 +09:00
|
|
|
import { Button } from "@/components/ui/button"
|
|
|
|
|
import { Textarea } from "@/components/ui/textarea"
|
|
|
|
|
import { useDiagram } from "@/contexts/diagram-context"
|
2025-12-20 20:18:54 +05:30
|
|
|
import { useDictionary } from "@/hooks/use-dictionary"
|
|
|
|
|
import { formatMessage } from "@/lib/i18n/utils"
|
2025-12-10 21:32:35 +09:00
|
|
|
import { isPdfFile, isTextFile } from "@/lib/pdf-utils"
|
feat: multi-provider model configuration with UI/UX improvements (#355)
* feat: add multi-provider model configuration
- Add model config dialog for managing multiple AI providers
- Support for OpenAI, Anthropic, Google, Azure, Bedrock, OpenRouter, DeepSeek, SiliconFlow, Ollama, and AI Gateway
- Add model selector dropdown in chat panel header
- Add API key validation endpoint
- Add custom model ID input with keyboard navigation
- Fix hover highlight in Command component
- Add suggested models for each provider including latest Claude 4.5 series
- Store configuration locally in browser
* feat: improve model config UI and move selector to chat input
- Move model selector from header to chat input (left of send button)
- Add per-model validation status (queued, running, valid, invalid)
- Filter model selector to only show verified models
- Add editable model IDs in config dialog
- Add custom model input field alongside suggested models dropdown
- Fix hover states on provider buttons and select triggers
- Update OpenAI suggested models with GPT-5 series
- Add alert-dialog component for delete confirmation
* refactor: revert shadcn component changes, apply hover fix at usage site
* feat: add AWS credentials support for Bedrock provider
- Add AWS Access Key ID, Secret Access Key, Region fields for Bedrock
- Show different credential fields based on provider type
- Update validation API to handle Bedrock with AWS credentials
- Add region selector with common AWS regions
* fix: reset Test button after validation completes
* fix: reset validation button to Test after success
* fix: complete bedrock support and UI/UX improvements
- Add bedrock to ALLOWED_CLIENT_PROVIDERS for client credentials
- Pass AWS credentials through full chain (headers → API → provider)
- Replace non-existent GPT-5 models with real ones (o1, o3-mini)
- Add accessibility: aria-labels, focus-visible rings, inline errors
- Add more AWS regions (Ohio, London, Paris, Mumbai, Seoul, São Paulo)
- Fix setTimeout cleanup with useRef on component unmount
- Fix TypeScript type consistency in getSelectedAIConfig fallback
* chore: remove unused code
- Remove unused setAccessCodeRequired state in chat-panel.tsx
- Remove unused getSelectedModel export in model-config.ts
* fix: UI/UX improvements for model configuration dialog
- Add gradient header styling with icon badge
- Change Configuration section icon from Key to Settings2
- Add duplicate model detection with warning banner and inline removal
- Filter out already-added models from suggestions dropdown
- Add type-to-confirm for deleting providers with 3+ models
- Enhance delete confirmation dialog with warning icon
- Improve model selector discoverability (show model name + chevron)
- Add truncation for long model names with title tooltip
- Remove AI provider settings from Settings dialog (now in Model Config)
- Extract ValidationButton into reusable component
* fix: prevent duplicate model IDs within same provider
- Block adding model if ID already exists in provider
- Block editing model ID to match existing model in provider
* fix: improve duplicate model ID notifications
- Add toast notification when trying to add duplicate model
- Allow free typing when editing model ID, validate on blur
- Show warning toast instead of blocking input
* fix: improve duplicate model validation UX in config dialog
- Add inline error display for duplicate model IDs
- Show red border on input when error exists
- Validate on blur with shake animation for edit errors
- Prevent saving empty model names
- Clear errors when user starts typing
- Simplify error styling (small red text, no heavy chips)
2025-12-22 22:36:36 +09:00
|
|
|
import type { FlattenedModel } from "@/lib/types/model-config"
|
2025-12-06 12:46:40 +09:00
|
|
|
import { FilePreviewList } from "./file-preview-list"
|
2025-12-05 19:30:50 +09:00
|
|
|
|
2025-12-10 21:32:35 +09:00
|
|
|
const MAX_IMAGE_SIZE = 2 * 1024 * 1024 // 2MB
|
2025-12-06 12:46:40 +09:00
|
|
|
const MAX_FILES = 5
|
2025-12-05 19:30:50 +09:00
|
|
|
|
2025-12-10 21:32:35 +09:00
|
|
|
function isValidFileType(file: File): boolean {
|
|
|
|
|
return file.type.startsWith("image/") || isPdfFile(file) || isTextFile(file)
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-05 19:30:50 +09:00
|
|
|
function formatFileSize(bytes: number): string {
|
2025-12-06 12:46:40 +09:00
|
|
|
const mb = bytes / 1024 / 1024
|
|
|
|
|
if (mb < 0.01) return `${(bytes / 1024).toFixed(0)}KB`
|
|
|
|
|
return `${mb.toFixed(2)}MB`
|
2025-12-05 19:30:50 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function showErrorToast(message: React.ReactNode) {
|
|
|
|
|
toast.custom(
|
2025-12-06 12:46:40 +09:00
|
|
|
(t) => (
|
|
|
|
|
<ErrorToast message={message} onDismiss={() => toast.dismiss(t)} />
|
|
|
|
|
),
|
|
|
|
|
{ duration: 5000 },
|
|
|
|
|
)
|
2025-12-05 19:30:50 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface ValidationResult {
|
2025-12-06 12:46:40 +09:00
|
|
|
validFiles: File[]
|
|
|
|
|
errors: string[]
|
2025-12-05 19:30:50 +09:00
|
|
|
}
|
|
|
|
|
|
2025-12-06 12:46:40 +09:00
|
|
|
function validateFiles(
|
|
|
|
|
newFiles: File[],
|
|
|
|
|
existingCount: number,
|
2025-12-20 20:18:54 +05:30
|
|
|
dict: any,
|
2025-12-06 12:46:40 +09:00
|
|
|
): ValidationResult {
|
|
|
|
|
const errors: string[] = []
|
|
|
|
|
const validFiles: File[] = []
|
2025-12-05 19:30:50 +09:00
|
|
|
|
2025-12-06 12:46:40 +09:00
|
|
|
const availableSlots = MAX_FILES - existingCount
|
2025-12-05 19:30:50 +09:00
|
|
|
|
|
|
|
|
if (availableSlots <= 0) {
|
2025-12-20 20:18:54 +05:30
|
|
|
errors.push(formatMessage(dict.errors.maxFiles, { max: MAX_FILES }))
|
2025-12-06 12:46:40 +09:00
|
|
|
return { validFiles, errors }
|
2025-12-05 19:30:50 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const file of newFiles) {
|
|
|
|
|
if (validFiles.length >= availableSlots) {
|
2025-12-20 20:18:54 +05:30
|
|
|
errors.push(
|
|
|
|
|
formatMessage(dict.errors.onlyMoreAllowed, {
|
|
|
|
|
slots: availableSlots,
|
|
|
|
|
}),
|
|
|
|
|
)
|
2025-12-06 12:46:40 +09:00
|
|
|
break
|
2025-12-05 19:30:50 +09:00
|
|
|
}
|
2025-12-10 21:32:35 +09:00
|
|
|
if (!isValidFileType(file)) {
|
2025-12-20 20:18:54 +05:30
|
|
|
errors.push(
|
|
|
|
|
formatMessage(dict.errors.unsupportedType, { name: file.name }),
|
|
|
|
|
)
|
2025-12-10 21:32:35 +09:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
// Only check size for images (PDFs/text files are extracted client-side, so file size doesn't matter)
|
|
|
|
|
const isExtractedFile = isPdfFile(file) || isTextFile(file)
|
|
|
|
|
if (!isExtractedFile && file.size > MAX_IMAGE_SIZE) {
|
|
|
|
|
const maxSizeMB = MAX_IMAGE_SIZE / 1024 / 1024
|
2025-12-06 12:46:40 +09:00
|
|
|
errors.push(
|
2025-12-20 20:18:54 +05:30
|
|
|
formatMessage(dict.errors.fileExceeds, {
|
|
|
|
|
name: file.name,
|
|
|
|
|
size: formatFileSize(file.size),
|
|
|
|
|
max: maxSizeMB,
|
|
|
|
|
}),
|
2025-12-06 12:46:40 +09:00
|
|
|
)
|
2025-12-05 19:30:50 +09:00
|
|
|
} else {
|
2025-12-06 12:46:40 +09:00
|
|
|
validFiles.push(file)
|
2025-12-05 19:30:50 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-06 12:46:40 +09:00
|
|
|
return { validFiles, errors }
|
2025-12-05 19:30:50 +09:00
|
|
|
}
|
|
|
|
|
|
2025-12-20 20:18:54 +05:30
|
|
|
function showValidationErrors(errors: string[], dict: any) {
|
2025-12-06 12:46:40 +09:00
|
|
|
if (errors.length === 0) return
|
2025-12-05 19:30:50 +09:00
|
|
|
|
|
|
|
|
if (errors.length === 1) {
|
2025-12-06 12:46:40 +09:00
|
|
|
showErrorToast(
|
|
|
|
|
<span className="text-muted-foreground">{errors[0]}</span>,
|
|
|
|
|
)
|
2025-12-05 19:30:50 +09:00
|
|
|
} else {
|
|
|
|
|
showErrorToast(
|
|
|
|
|
<div className="flex flex-col gap-1">
|
2025-12-06 12:46:40 +09:00
|
|
|
<span className="font-medium">
|
2025-12-20 20:18:54 +05:30
|
|
|
{formatMessage(dict.errors.filesRejected, {
|
|
|
|
|
count: errors.length,
|
|
|
|
|
})}
|
2025-12-06 12:46:40 +09:00
|
|
|
</span>
|
2025-12-05 19:30:50 +09:00
|
|
|
<ul className="text-muted-foreground text-xs list-disc list-inside">
|
2025-12-06 16:18:26 +09:00
|
|
|
{errors.slice(0, 3).map((err) => (
|
|
|
|
|
<li key={err}>{err}</li>
|
2025-12-06 12:46:40 +09:00
|
|
|
))}
|
|
|
|
|
{errors.length > 3 && (
|
2025-12-20 20:18:54 +05:30
|
|
|
<li>
|
|
|
|
|
{formatMessage(dict.errors.andMore, {
|
|
|
|
|
count: errors.length - 3,
|
|
|
|
|
})}
|
|
|
|
|
</li>
|
2025-12-06 12:46:40 +09:00
|
|
|
)}
|
2025-12-05 19:30:50 +09:00
|
|
|
</ul>
|
2025-12-06 12:46:40 +09:00
|
|
|
</div>,
|
|
|
|
|
)
|
2025-12-05 19:30:50 +09:00
|
|
|
}
|
|
|
|
|
}
|
2025-03-26 00:30:00 +00:00
|
|
|
|
2025-03-22 13:15:51 +00:00
|
|
|
interface ChatInputProps {
|
2025-12-06 12:46:40 +09:00
|
|
|
input: string
|
|
|
|
|
status: "submitted" | "streaming" | "ready" | "error"
|
|
|
|
|
onSubmit: (e: React.FormEvent<HTMLFormElement>) => void
|
|
|
|
|
onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void
|
|
|
|
|
onClearChat: () => void
|
|
|
|
|
files?: File[]
|
|
|
|
|
onFileChange?: (files: File[]) => void
|
2025-12-10 21:32:35 +09:00
|
|
|
pdfData?: Map<
|
|
|
|
|
File,
|
|
|
|
|
{ text: string; charCount: number; isExtracting: boolean }
|
|
|
|
|
>
|
2025-12-28 18:46:10 +05:30
|
|
|
|
2025-12-06 12:46:40 +09:00
|
|
|
sessionId?: string
|
|
|
|
|
error?: Error | null
|
feat: multi-provider model configuration with UI/UX improvements (#355)
* feat: add multi-provider model configuration
- Add model config dialog for managing multiple AI providers
- Support for OpenAI, Anthropic, Google, Azure, Bedrock, OpenRouter, DeepSeek, SiliconFlow, Ollama, and AI Gateway
- Add model selector dropdown in chat panel header
- Add API key validation endpoint
- Add custom model ID input with keyboard navigation
- Fix hover highlight in Command component
- Add suggested models for each provider including latest Claude 4.5 series
- Store configuration locally in browser
* feat: improve model config UI and move selector to chat input
- Move model selector from header to chat input (left of send button)
- Add per-model validation status (queued, running, valid, invalid)
- Filter model selector to only show verified models
- Add editable model IDs in config dialog
- Add custom model input field alongside suggested models dropdown
- Fix hover states on provider buttons and select triggers
- Update OpenAI suggested models with GPT-5 series
- Add alert-dialog component for delete confirmation
* refactor: revert shadcn component changes, apply hover fix at usage site
* feat: add AWS credentials support for Bedrock provider
- Add AWS Access Key ID, Secret Access Key, Region fields for Bedrock
- Show different credential fields based on provider type
- Update validation API to handle Bedrock with AWS credentials
- Add region selector with common AWS regions
* fix: reset Test button after validation completes
* fix: reset validation button to Test after success
* fix: complete bedrock support and UI/UX improvements
- Add bedrock to ALLOWED_CLIENT_PROVIDERS for client credentials
- Pass AWS credentials through full chain (headers → API → provider)
- Replace non-existent GPT-5 models with real ones (o1, o3-mini)
- Add accessibility: aria-labels, focus-visible rings, inline errors
- Add more AWS regions (Ohio, London, Paris, Mumbai, Seoul, São Paulo)
- Fix setTimeout cleanup with useRef on component unmount
- Fix TypeScript type consistency in getSelectedAIConfig fallback
* chore: remove unused code
- Remove unused setAccessCodeRequired state in chat-panel.tsx
- Remove unused getSelectedModel export in model-config.ts
* fix: UI/UX improvements for model configuration dialog
- Add gradient header styling with icon badge
- Change Configuration section icon from Key to Settings2
- Add duplicate model detection with warning banner and inline removal
- Filter out already-added models from suggestions dropdown
- Add type-to-confirm for deleting providers with 3+ models
- Enhance delete confirmation dialog with warning icon
- Improve model selector discoverability (show model name + chevron)
- Add truncation for long model names with title tooltip
- Remove AI provider settings from Settings dialog (now in Model Config)
- Extract ValidationButton into reusable component
* fix: prevent duplicate model IDs within same provider
- Block adding model if ID already exists in provider
- Block editing model ID to match existing model in provider
* fix: improve duplicate model ID notifications
- Add toast notification when trying to add duplicate model
- Allow free typing when editing model ID, validate on blur
- Show warning toast instead of blocking input
* fix: improve duplicate model validation UX in config dialog
- Add inline error display for duplicate model IDs
- Show red border on input when error exists
- Validate on blur with shake animation for edit errors
- Prevent saving empty model names
- Clear errors when user starts typing
- Simplify error styling (small red text, no heavy chips)
2025-12-22 22:36:36 +09:00
|
|
|
// Model selector props
|
|
|
|
|
models?: FlattenedModel[]
|
|
|
|
|
selectedModelId?: string
|
|
|
|
|
onModelSelect?: (modelId: string | undefined) => void
|
2025-12-26 11:19:59 +08:00
|
|
|
showUnvalidatedModels?: boolean
|
feat: multi-provider model configuration with UI/UX improvements (#355)
* feat: add multi-provider model configuration
- Add model config dialog for managing multiple AI providers
- Support for OpenAI, Anthropic, Google, Azure, Bedrock, OpenRouter, DeepSeek, SiliconFlow, Ollama, and AI Gateway
- Add model selector dropdown in chat panel header
- Add API key validation endpoint
- Add custom model ID input with keyboard navigation
- Fix hover highlight in Command component
- Add suggested models for each provider including latest Claude 4.5 series
- Store configuration locally in browser
* feat: improve model config UI and move selector to chat input
- Move model selector from header to chat input (left of send button)
- Add per-model validation status (queued, running, valid, invalid)
- Filter model selector to only show verified models
- Add editable model IDs in config dialog
- Add custom model input field alongside suggested models dropdown
- Fix hover states on provider buttons and select triggers
- Update OpenAI suggested models with GPT-5 series
- Add alert-dialog component for delete confirmation
* refactor: revert shadcn component changes, apply hover fix at usage site
* feat: add AWS credentials support for Bedrock provider
- Add AWS Access Key ID, Secret Access Key, Region fields for Bedrock
- Show different credential fields based on provider type
- Update validation API to handle Bedrock with AWS credentials
- Add region selector with common AWS regions
* fix: reset Test button after validation completes
* fix: reset validation button to Test after success
* fix: complete bedrock support and UI/UX improvements
- Add bedrock to ALLOWED_CLIENT_PROVIDERS for client credentials
- Pass AWS credentials through full chain (headers → API → provider)
- Replace non-existent GPT-5 models with real ones (o1, o3-mini)
- Add accessibility: aria-labels, focus-visible rings, inline errors
- Add more AWS regions (Ohio, London, Paris, Mumbai, Seoul, São Paulo)
- Fix setTimeout cleanup with useRef on component unmount
- Fix TypeScript type consistency in getSelectedAIConfig fallback
* chore: remove unused code
- Remove unused setAccessCodeRequired state in chat-panel.tsx
- Remove unused getSelectedModel export in model-config.ts
* fix: UI/UX improvements for model configuration dialog
- Add gradient header styling with icon badge
- Change Configuration section icon from Key to Settings2
- Add duplicate model detection with warning banner and inline removal
- Filter out already-added models from suggestions dropdown
- Add type-to-confirm for deleting providers with 3+ models
- Enhance delete confirmation dialog with warning icon
- Improve model selector discoverability (show model name + chevron)
- Add truncation for long model names with title tooltip
- Remove AI provider settings from Settings dialog (now in Model Config)
- Extract ValidationButton into reusable component
* fix: prevent duplicate model IDs within same provider
- Block adding model if ID already exists in provider
- Block editing model ID to match existing model in provider
* fix: improve duplicate model ID notifications
- Add toast notification when trying to add duplicate model
- Allow free typing when editing model ID, validate on blur
- Show warning toast instead of blocking input
* fix: improve duplicate model validation UX in config dialog
- Add inline error display for duplicate model IDs
- Show red border on input when error exists
- Validate on blur with shake animation for edit errors
- Prevent saving empty model names
- Clear errors when user starts typing
- Simplify error styling (small red text, no heavy chips)
2025-12-22 22:36:36 +09:00
|
|
|
onConfigureModels?: () => void
|
2025-03-22 13:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
2025-03-23 11:03:25 +00:00
|
|
|
export function ChatInput({
|
|
|
|
|
input,
|
|
|
|
|
status,
|
|
|
|
|
onSubmit,
|
|
|
|
|
onChange,
|
2025-03-27 07:48:19 +00:00
|
|
|
onClearChat,
|
2025-03-27 08:02:03 +00:00
|
|
|
files = [],
|
|
|
|
|
onFileChange = () => {},
|
2025-12-10 21:32:35 +09:00
|
|
|
pdfData = new Map(),
|
2025-12-05 21:15:02 +09:00
|
|
|
sessionId,
|
2025-12-05 16:22:38 +09:00
|
|
|
error = null,
|
feat: multi-provider model configuration with UI/UX improvements (#355)
* feat: add multi-provider model configuration
- Add model config dialog for managing multiple AI providers
- Support for OpenAI, Anthropic, Google, Azure, Bedrock, OpenRouter, DeepSeek, SiliconFlow, Ollama, and AI Gateway
- Add model selector dropdown in chat panel header
- Add API key validation endpoint
- Add custom model ID input with keyboard navigation
- Fix hover highlight in Command component
- Add suggested models for each provider including latest Claude 4.5 series
- Store configuration locally in browser
* feat: improve model config UI and move selector to chat input
- Move model selector from header to chat input (left of send button)
- Add per-model validation status (queued, running, valid, invalid)
- Filter model selector to only show verified models
- Add editable model IDs in config dialog
- Add custom model input field alongside suggested models dropdown
- Fix hover states on provider buttons and select triggers
- Update OpenAI suggested models with GPT-5 series
- Add alert-dialog component for delete confirmation
* refactor: revert shadcn component changes, apply hover fix at usage site
* feat: add AWS credentials support for Bedrock provider
- Add AWS Access Key ID, Secret Access Key, Region fields for Bedrock
- Show different credential fields based on provider type
- Update validation API to handle Bedrock with AWS credentials
- Add region selector with common AWS regions
* fix: reset Test button after validation completes
* fix: reset validation button to Test after success
* fix: complete bedrock support and UI/UX improvements
- Add bedrock to ALLOWED_CLIENT_PROVIDERS for client credentials
- Pass AWS credentials through full chain (headers → API → provider)
- Replace non-existent GPT-5 models with real ones (o1, o3-mini)
- Add accessibility: aria-labels, focus-visible rings, inline errors
- Add more AWS regions (Ohio, London, Paris, Mumbai, Seoul, São Paulo)
- Fix setTimeout cleanup with useRef on component unmount
- Fix TypeScript type consistency in getSelectedAIConfig fallback
* chore: remove unused code
- Remove unused setAccessCodeRequired state in chat-panel.tsx
- Remove unused getSelectedModel export in model-config.ts
* fix: UI/UX improvements for model configuration dialog
- Add gradient header styling with icon badge
- Change Configuration section icon from Key to Settings2
- Add duplicate model detection with warning banner and inline removal
- Filter out already-added models from suggestions dropdown
- Add type-to-confirm for deleting providers with 3+ models
- Enhance delete confirmation dialog with warning icon
- Improve model selector discoverability (show model name + chevron)
- Add truncation for long model names with title tooltip
- Remove AI provider settings from Settings dialog (now in Model Config)
- Extract ValidationButton into reusable component
* fix: prevent duplicate model IDs within same provider
- Block adding model if ID already exists in provider
- Block editing model ID to match existing model in provider
* fix: improve duplicate model ID notifications
- Add toast notification when trying to add duplicate model
- Allow free typing when editing model ID, validate on blur
- Show warning toast instead of blocking input
* fix: improve duplicate model validation UX in config dialog
- Add inline error display for duplicate model IDs
- Show red border on input when error exists
- Validate on blur with shake animation for edit errors
- Prevent saving empty model names
- Clear errors when user starts typing
- Simplify error styling (small red text, no heavy chips)
2025-12-22 22:36:36 +09:00
|
|
|
models = [],
|
|
|
|
|
selectedModelId,
|
|
|
|
|
onModelSelect = () => {},
|
2025-12-26 11:19:59 +08:00
|
|
|
showUnvalidatedModels = false,
|
feat: multi-provider model configuration with UI/UX improvements (#355)
* feat: add multi-provider model configuration
- Add model config dialog for managing multiple AI providers
- Support for OpenAI, Anthropic, Google, Azure, Bedrock, OpenRouter, DeepSeek, SiliconFlow, Ollama, and AI Gateway
- Add model selector dropdown in chat panel header
- Add API key validation endpoint
- Add custom model ID input with keyboard navigation
- Fix hover highlight in Command component
- Add suggested models for each provider including latest Claude 4.5 series
- Store configuration locally in browser
* feat: improve model config UI and move selector to chat input
- Move model selector from header to chat input (left of send button)
- Add per-model validation status (queued, running, valid, invalid)
- Filter model selector to only show verified models
- Add editable model IDs in config dialog
- Add custom model input field alongside suggested models dropdown
- Fix hover states on provider buttons and select triggers
- Update OpenAI suggested models with GPT-5 series
- Add alert-dialog component for delete confirmation
* refactor: revert shadcn component changes, apply hover fix at usage site
* feat: add AWS credentials support for Bedrock provider
- Add AWS Access Key ID, Secret Access Key, Region fields for Bedrock
- Show different credential fields based on provider type
- Update validation API to handle Bedrock with AWS credentials
- Add region selector with common AWS regions
* fix: reset Test button after validation completes
* fix: reset validation button to Test after success
* fix: complete bedrock support and UI/UX improvements
- Add bedrock to ALLOWED_CLIENT_PROVIDERS for client credentials
- Pass AWS credentials through full chain (headers → API → provider)
- Replace non-existent GPT-5 models with real ones (o1, o3-mini)
- Add accessibility: aria-labels, focus-visible rings, inline errors
- Add more AWS regions (Ohio, London, Paris, Mumbai, Seoul, São Paulo)
- Fix setTimeout cleanup with useRef on component unmount
- Fix TypeScript type consistency in getSelectedAIConfig fallback
* chore: remove unused code
- Remove unused setAccessCodeRequired state in chat-panel.tsx
- Remove unused getSelectedModel export in model-config.ts
* fix: UI/UX improvements for model configuration dialog
- Add gradient header styling with icon badge
- Change Configuration section icon from Key to Settings2
- Add duplicate model detection with warning banner and inline removal
- Filter out already-added models from suggestions dropdown
- Add type-to-confirm for deleting providers with 3+ models
- Enhance delete confirmation dialog with warning icon
- Improve model selector discoverability (show model name + chevron)
- Add truncation for long model names with title tooltip
- Remove AI provider settings from Settings dialog (now in Model Config)
- Extract ValidationButton into reusable component
* fix: prevent duplicate model IDs within same provider
- Block adding model if ID already exists in provider
- Block editing model ID to match existing model in provider
* fix: improve duplicate model ID notifications
- Add toast notification when trying to add duplicate model
- Allow free typing when editing model ID, validate on blur
- Show warning toast instead of blocking input
* fix: improve duplicate model validation UX in config dialog
- Add inline error display for duplicate model IDs
- Show red border on input when error exists
- Validate on blur with shake animation for edit errors
- Prevent saving empty model names
- Clear errors when user starts typing
- Simplify error styling (small red text, no heavy chips)
2025-12-22 22:36:36 +09:00
|
|
|
onConfigureModels = () => {},
|
2025-03-23 11:03:25 +00:00
|
|
|
}: ChatInputProps) {
|
2025-12-20 20:18:54 +05:30
|
|
|
const dict = useDictionary()
|
2025-12-28 18:46:10 +05:30
|
|
|
const { diagramHistory, saveDiagramToFile } = useDiagram()
|
|
|
|
|
|
2025-12-06 12:46:40 +09:00
|
|
|
const textareaRef = useRef<HTMLTextAreaElement>(null)
|
|
|
|
|
const fileInputRef = useRef<HTMLInputElement>(null)
|
|
|
|
|
const [isDragging, setIsDragging] = useState(false)
|
|
|
|
|
const [showClearDialog, setShowClearDialog] = useState(false)
|
2025-12-28 18:46:10 +05:30
|
|
|
const [showHistory, setShowHistory] = useState(false)
|
|
|
|
|
const [showSaveDialog, setShowSaveDialog] = useState(false)
|
2025-12-05 16:22:38 +09:00
|
|
|
// Allow retry when there's an error (even if status is still "streaming" or "submitted")
|
2025-12-05 19:30:50 +09:00
|
|
|
const isDisabled =
|
2025-12-06 12:46:40 +09:00
|
|
|
(status === "streaming" || status === "submitted") && !error
|
2025-11-15 14:29:18 +09:00
|
|
|
|
2025-03-22 13:15:51 +00:00
|
|
|
const adjustTextareaHeight = useCallback(() => {
|
2025-12-06 12:46:40 +09:00
|
|
|
const textarea = textareaRef.current
|
2025-03-22 13:15:51 +00:00
|
|
|
if (textarea) {
|
2025-12-06 12:46:40 +09:00
|
|
|
textarea.style.height = "auto"
|
|
|
|
|
textarea.style.height = `${Math.min(textarea.scrollHeight, 200)}px`
|
2025-03-22 13:15:51 +00:00
|
|
|
}
|
2025-12-06 12:46:40 +09:00
|
|
|
}, [])
|
2025-12-06 16:18:26 +09:00
|
|
|
// Handle programmatic input changes (e.g., setInput("") after form submission)
|
2025-03-22 13:15:51 +00:00
|
|
|
useEffect(() => {
|
2025-12-06 12:46:40 +09:00
|
|
|
adjustTextareaHeight()
|
|
|
|
|
}, [input, adjustTextareaHeight])
|
2025-03-22 13:15:51 +00:00
|
|
|
|
2025-12-06 16:18:26 +09:00
|
|
|
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
|
|
|
onChange(e)
|
|
|
|
|
adjustTextareaHeight()
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-22 13:15:51 +00:00
|
|
|
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
|
|
|
if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
|
2025-12-06 12:46:40 +09:00
|
|
|
e.preventDefault()
|
|
|
|
|
const form = e.currentTarget.closest("form")
|
2025-11-15 14:29:18 +09:00
|
|
|
if (form && input.trim() && !isDisabled) {
|
2025-12-06 12:46:40 +09:00
|
|
|
form.requestSubmit()
|
2025-03-22 13:15:51 +00:00
|
|
|
}
|
|
|
|
|
}
|
2025-12-06 12:46:40 +09:00
|
|
|
}
|
2025-03-22 13:15:51 +00:00
|
|
|
|
2025-04-03 15:18:14 +00:00
|
|
|
const handlePaste = async (e: React.ClipboardEvent) => {
|
2025-12-06 12:46:40 +09:00
|
|
|
if (isDisabled) return
|
2025-04-03 15:18:14 +00:00
|
|
|
|
2025-12-06 12:46:40 +09:00
|
|
|
const items = e.clipboardData.items
|
2025-04-03 15:18:14 +00:00
|
|
|
const imageItems = Array.from(items).filter((item) =>
|
2025-12-06 12:46:40 +09:00
|
|
|
item.type.startsWith("image/"),
|
|
|
|
|
)
|
2025-04-03 15:18:14 +00:00
|
|
|
|
|
|
|
|
if (imageItems.length > 0) {
|
2025-12-06 12:46:40 +09:00
|
|
|
const imageFiles = (
|
|
|
|
|
await Promise.all(
|
|
|
|
|
imageItems.map(async (item, index) => {
|
|
|
|
|
const file = item.getAsFile()
|
|
|
|
|
if (!file) return null
|
|
|
|
|
return new File(
|
|
|
|
|
[file],
|
|
|
|
|
`pasted-image-${Date.now()}-${index}.${file.type.split("/")[1]}`,
|
|
|
|
|
{ type: file.type },
|
|
|
|
|
)
|
|
|
|
|
}),
|
|
|
|
|
)
|
|
|
|
|
).filter((f): f is File => f !== null)
|
|
|
|
|
|
|
|
|
|
const { validFiles, errors } = validateFiles(
|
|
|
|
|
imageFiles,
|
|
|
|
|
files.length,
|
2025-12-20 20:18:54 +05:30
|
|
|
dict,
|
2025-12-06 12:46:40 +09:00
|
|
|
)
|
2025-12-20 20:18:54 +05:30
|
|
|
showValidationErrors(errors, dict)
|
2025-04-03 15:18:14 +00:00
|
|
|
if (validFiles.length > 0) {
|
2025-12-06 12:46:40 +09:00
|
|
|
onFileChange([...files, ...validFiles])
|
2025-04-03 15:18:14 +00:00
|
|
|
}
|
|
|
|
|
}
|
2025-12-06 12:46:40 +09:00
|
|
|
}
|
2025-04-03 15:18:14 +00:00
|
|
|
|
2025-03-23 11:03:25 +00:00
|
|
|
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
2025-12-06 12:46:40 +09:00
|
|
|
const newFiles = Array.from(e.target.files || [])
|
2025-12-20 20:18:54 +05:30
|
|
|
const { validFiles, errors } = validateFiles(
|
|
|
|
|
newFiles,
|
|
|
|
|
files.length,
|
|
|
|
|
dict,
|
|
|
|
|
)
|
|
|
|
|
showValidationErrors(errors, dict)
|
2025-12-05 19:30:50 +09:00
|
|
|
if (validFiles.length > 0) {
|
2025-12-06 12:46:40 +09:00
|
|
|
onFileChange([...files, ...validFiles])
|
2025-12-05 19:30:50 +09:00
|
|
|
}
|
2025-12-20 20:18:54 +05:30
|
|
|
|
2025-12-05 19:30:50 +09:00
|
|
|
if (fileInputRef.current) {
|
2025-12-06 12:46:40 +09:00
|
|
|
fileInputRef.current.value = ""
|
2025-12-05 19:30:50 +09:00
|
|
|
}
|
2025-12-06 12:46:40 +09:00
|
|
|
}
|
2025-03-27 08:02:03 +00:00
|
|
|
|
|
|
|
|
const handleRemoveFile = (fileToRemove: File) => {
|
2025-12-06 12:46:40 +09:00
|
|
|
onFileChange(files.filter((file) => file !== fileToRemove))
|
2025-03-27 08:02:03 +00:00
|
|
|
if (fileInputRef.current) {
|
2025-12-06 12:46:40 +09:00
|
|
|
fileInputRef.current.value = ""
|
2025-03-23 11:03:25 +00:00
|
|
|
}
|
2025-12-06 12:46:40 +09:00
|
|
|
}
|
2025-03-23 11:03:25 +00:00
|
|
|
|
|
|
|
|
const triggerFileInput = () => {
|
2025-12-06 12:46:40 +09:00
|
|
|
fileInputRef.current?.click()
|
|
|
|
|
}
|
2025-03-23 11:03:25 +00:00
|
|
|
|
2025-03-23 12:56:47 +00:00
|
|
|
const handleDragOver = (e: React.DragEvent<HTMLFormElement>) => {
|
2025-12-06 12:46:40 +09:00
|
|
|
e.preventDefault()
|
|
|
|
|
e.stopPropagation()
|
|
|
|
|
setIsDragging(true)
|
|
|
|
|
}
|
2025-03-23 12:56:47 +00:00
|
|
|
|
|
|
|
|
const handleDragLeave = (e: React.DragEvent<HTMLFormElement>) => {
|
2025-12-06 12:46:40 +09:00
|
|
|
e.preventDefault()
|
|
|
|
|
e.stopPropagation()
|
|
|
|
|
setIsDragging(false)
|
|
|
|
|
}
|
2025-03-23 12:56:47 +00:00
|
|
|
|
|
|
|
|
const handleDrop = (e: React.DragEvent<HTMLFormElement>) => {
|
2025-12-06 12:46:40 +09:00
|
|
|
e.preventDefault()
|
|
|
|
|
e.stopPropagation()
|
|
|
|
|
setIsDragging(false)
|
2025-03-23 12:56:47 +00:00
|
|
|
|
2025-12-06 12:46:40 +09:00
|
|
|
if (isDisabled) return
|
2025-03-23 12:56:47 +00:00
|
|
|
|
2025-12-06 12:46:40 +09:00
|
|
|
const droppedFiles = e.dataTransfer.files
|
2025-12-10 21:32:35 +09:00
|
|
|
const supportedFiles = Array.from(droppedFiles).filter((file) =>
|
|
|
|
|
isValidFileType(file),
|
2025-12-06 12:46:40 +09:00
|
|
|
)
|
2025-03-27 08:02:03 +00:00
|
|
|
|
2025-12-10 21:32:35 +09:00
|
|
|
const { validFiles, errors } = validateFiles(
|
|
|
|
|
supportedFiles,
|
|
|
|
|
files.length,
|
2025-12-20 20:18:54 +05:30
|
|
|
dict,
|
2025-12-10 21:32:35 +09:00
|
|
|
)
|
2025-12-20 20:18:54 +05:30
|
|
|
showValidationErrors(errors, dict)
|
2025-12-05 19:30:50 +09:00
|
|
|
if (validFiles.length > 0) {
|
2025-12-06 12:46:40 +09:00
|
|
|
onFileChange([...files, ...validFiles])
|
2025-03-23 12:56:47 +00:00
|
|
|
}
|
2025-12-06 12:46:40 +09:00
|
|
|
}
|
2025-03-23 12:56:47 +00:00
|
|
|
|
2025-03-23 13:31:26 +00:00
|
|
|
const handleClear = () => {
|
2025-12-06 12:46:40 +09:00
|
|
|
onClearChat()
|
|
|
|
|
setShowClearDialog(false)
|
|
|
|
|
}
|
2025-03-23 13:31:26 +00:00
|
|
|
|
2025-03-22 13:15:51 +00:00
|
|
|
return (
|
2025-03-23 12:56:47 +00:00
|
|
|
<form
|
|
|
|
|
onSubmit={onSubmit}
|
2025-12-03 21:49:34 +09:00
|
|
|
className={`w-full transition-all duration-200 ${
|
2025-03-23 12:56:47 +00:00
|
|
|
isDragging
|
2025-12-03 21:49:34 +09:00
|
|
|
? "ring-2 ring-primary ring-offset-2 rounded-2xl"
|
2025-03-23 12:56:47 +00:00
|
|
|
: ""
|
|
|
|
|
}`}
|
|
|
|
|
onDragOver={handleDragOver}
|
|
|
|
|
onDragLeave={handleDragLeave}
|
|
|
|
|
onDrop={handleDrop}
|
|
|
|
|
>
|
2025-12-03 21:49:34 +09:00
|
|
|
{/* File previews */}
|
|
|
|
|
{files.length > 0 && (
|
|
|
|
|
<div className="mb-3">
|
2025-12-05 19:30:50 +09:00
|
|
|
<FilePreviewList
|
|
|
|
|
files={files}
|
|
|
|
|
onRemoveFile={handleRemoveFile}
|
2025-12-10 21:32:35 +09:00
|
|
|
pdfData={pdfData}
|
2025-12-05 19:30:50 +09:00
|
|
|
/>
|
2025-03-23 12:04:33 +00:00
|
|
|
</div>
|
2025-12-03 21:49:34 +09:00
|
|
|
)}
|
|
|
|
|
<div className="relative rounded-2xl border border-border bg-background shadow-sm focus-within:ring-2 focus-within:ring-primary/20 focus-within:border-primary/50 transition-all duration-200">
|
|
|
|
|
<Textarea
|
|
|
|
|
ref={textareaRef}
|
|
|
|
|
value={input}
|
2025-12-06 16:18:26 +09:00
|
|
|
onChange={handleChange}
|
2025-12-03 21:49:34 +09:00
|
|
|
onKeyDown={handleKeyDown}
|
|
|
|
|
onPaste={handlePaste}
|
2025-12-20 20:18:54 +05:30
|
|
|
placeholder={dict.chat.placeholder}
|
2025-12-03 21:49:34 +09:00
|
|
|
disabled={isDisabled}
|
|
|
|
|
aria-label="Chat input"
|
|
|
|
|
className="min-h-[60px] max-h-[200px] resize-none border-0 bg-transparent px-4 py-3 text-sm focus-visible:ring-0 focus-visible:ring-offset-0 placeholder:text-muted-foreground/60"
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<div className="flex items-center justify-between px-3 py-2 border-t border-border/50">
|
2025-12-18 20:16:32 +08:00
|
|
|
<div className="flex items-center gap-1 overflow-x-hidden">
|
2025-12-03 21:49:34 +09:00
|
|
|
<ButtonWithTooltip
|
|
|
|
|
type="button"
|
|
|
|
|
variant="ghost"
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={() => setShowClearDialog(true)}
|
2025-12-20 20:18:54 +05:30
|
|
|
tooltipContent={dict.chat.clearConversation}
|
2025-12-03 21:49:34 +09:00
|
|
|
className="h-8 w-8 p-0 text-muted-foreground hover:text-destructive hover:bg-destructive/10"
|
|
|
|
|
>
|
|
|
|
|
<Trash2 className="h-4 w-4" />
|
|
|
|
|
</ButtonWithTooltip>
|
|
|
|
|
|
|
|
|
|
<ResetWarningModal
|
|
|
|
|
open={showClearDialog}
|
|
|
|
|
onOpenChange={setShowClearDialog}
|
|
|
|
|
onClear={handleClear}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
2025-12-18 20:16:32 +08:00
|
|
|
<div className="flex items-center gap-1 overflow-hidden justify-end">
|
2025-12-28 18:46:10 +05:30
|
|
|
<div className="flex items-center gap-1 overflow-x-hidden">
|
|
|
|
|
<ButtonWithTooltip
|
|
|
|
|
type="button"
|
|
|
|
|
variant="ghost"
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={() => setShowHistory(true)}
|
|
|
|
|
disabled={
|
|
|
|
|
isDisabled || diagramHistory.length === 0
|
|
|
|
|
}
|
|
|
|
|
tooltipContent={dict.chat.diagramHistory}
|
|
|
|
|
className="h-8 w-8 p-0 text-muted-foreground hover:text-foreground"
|
|
|
|
|
>
|
|
|
|
|
<History className="h-4 w-4" />
|
|
|
|
|
</ButtonWithTooltip>
|
|
|
|
|
|
|
|
|
|
<ButtonWithTooltip
|
|
|
|
|
type="button"
|
|
|
|
|
variant="ghost"
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={() => setShowSaveDialog(true)}
|
|
|
|
|
disabled={isDisabled}
|
|
|
|
|
tooltipContent={dict.chat.saveDiagram}
|
|
|
|
|
className="h-8 w-8 p-0 text-muted-foreground hover:text-foreground"
|
|
|
|
|
>
|
|
|
|
|
<Download className="h-4 w-4" />
|
|
|
|
|
</ButtonWithTooltip>
|
|
|
|
|
|
|
|
|
|
<ButtonWithTooltip
|
|
|
|
|
type="button"
|
|
|
|
|
variant="ghost"
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={triggerFileInput}
|
|
|
|
|
disabled={isDisabled}
|
|
|
|
|
tooltipContent={dict.chat.uploadFile}
|
|
|
|
|
className="h-8 w-8 p-0 text-muted-foreground hover:text-foreground"
|
|
|
|
|
>
|
|
|
|
|
<ImageIcon className="h-4 w-4" />
|
|
|
|
|
</ButtonWithTooltip>
|
|
|
|
|
|
|
|
|
|
<input
|
|
|
|
|
type="file"
|
|
|
|
|
ref={fileInputRef}
|
|
|
|
|
className="hidden"
|
|
|
|
|
onChange={handleFileChange}
|
|
|
|
|
accept="image/*,.pdf,application/pdf,text/*,.md,.markdown,.json,.csv,.xml,.yaml,.yml,.toml"
|
|
|
|
|
multiple
|
|
|
|
|
disabled={isDisabled}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
feat: multi-provider model configuration with UI/UX improvements (#355)
* feat: add multi-provider model configuration
- Add model config dialog for managing multiple AI providers
- Support for OpenAI, Anthropic, Google, Azure, Bedrock, OpenRouter, DeepSeek, SiliconFlow, Ollama, and AI Gateway
- Add model selector dropdown in chat panel header
- Add API key validation endpoint
- Add custom model ID input with keyboard navigation
- Fix hover highlight in Command component
- Add suggested models for each provider including latest Claude 4.5 series
- Store configuration locally in browser
* feat: improve model config UI and move selector to chat input
- Move model selector from header to chat input (left of send button)
- Add per-model validation status (queued, running, valid, invalid)
- Filter model selector to only show verified models
- Add editable model IDs in config dialog
- Add custom model input field alongside suggested models dropdown
- Fix hover states on provider buttons and select triggers
- Update OpenAI suggested models with GPT-5 series
- Add alert-dialog component for delete confirmation
* refactor: revert shadcn component changes, apply hover fix at usage site
* feat: add AWS credentials support for Bedrock provider
- Add AWS Access Key ID, Secret Access Key, Region fields for Bedrock
- Show different credential fields based on provider type
- Update validation API to handle Bedrock with AWS credentials
- Add region selector with common AWS regions
* fix: reset Test button after validation completes
* fix: reset validation button to Test after success
* fix: complete bedrock support and UI/UX improvements
- Add bedrock to ALLOWED_CLIENT_PROVIDERS for client credentials
- Pass AWS credentials through full chain (headers → API → provider)
- Replace non-existent GPT-5 models with real ones (o1, o3-mini)
- Add accessibility: aria-labels, focus-visible rings, inline errors
- Add more AWS regions (Ohio, London, Paris, Mumbai, Seoul, São Paulo)
- Fix setTimeout cleanup with useRef on component unmount
- Fix TypeScript type consistency in getSelectedAIConfig fallback
* chore: remove unused code
- Remove unused setAccessCodeRequired state in chat-panel.tsx
- Remove unused getSelectedModel export in model-config.ts
* fix: UI/UX improvements for model configuration dialog
- Add gradient header styling with icon badge
- Change Configuration section icon from Key to Settings2
- Add duplicate model detection with warning banner and inline removal
- Filter out already-added models from suggestions dropdown
- Add type-to-confirm for deleting providers with 3+ models
- Enhance delete confirmation dialog with warning icon
- Improve model selector discoverability (show model name + chevron)
- Add truncation for long model names with title tooltip
- Remove AI provider settings from Settings dialog (now in Model Config)
- Extract ValidationButton into reusable component
* fix: prevent duplicate model IDs within same provider
- Block adding model if ID already exists in provider
- Block editing model ID to match existing model in provider
* fix: improve duplicate model ID notifications
- Add toast notification when trying to add duplicate model
- Allow free typing when editing model ID, validate on blur
- Show warning toast instead of blocking input
* fix: improve duplicate model validation UX in config dialog
- Add inline error display for duplicate model IDs
- Show red border on input when error exists
- Validate on blur with shake animation for edit errors
- Prevent saving empty model names
- Clear errors when user starts typing
- Simplify error styling (small red text, no heavy chips)
2025-12-22 22:36:36 +09:00
|
|
|
<ModelSelector
|
|
|
|
|
models={models}
|
|
|
|
|
selectedModelId={selectedModelId}
|
|
|
|
|
onSelect={onModelSelect}
|
|
|
|
|
onConfigure={onConfigureModels}
|
|
|
|
|
disabled={isDisabled}
|
2025-12-26 11:19:59 +08:00
|
|
|
showUnvalidatedModels={showUnvalidatedModels}
|
feat: multi-provider model configuration with UI/UX improvements (#355)
* feat: add multi-provider model configuration
- Add model config dialog for managing multiple AI providers
- Support for OpenAI, Anthropic, Google, Azure, Bedrock, OpenRouter, DeepSeek, SiliconFlow, Ollama, and AI Gateway
- Add model selector dropdown in chat panel header
- Add API key validation endpoint
- Add custom model ID input with keyboard navigation
- Fix hover highlight in Command component
- Add suggested models for each provider including latest Claude 4.5 series
- Store configuration locally in browser
* feat: improve model config UI and move selector to chat input
- Move model selector from header to chat input (left of send button)
- Add per-model validation status (queued, running, valid, invalid)
- Filter model selector to only show verified models
- Add editable model IDs in config dialog
- Add custom model input field alongside suggested models dropdown
- Fix hover states on provider buttons and select triggers
- Update OpenAI suggested models with GPT-5 series
- Add alert-dialog component for delete confirmation
* refactor: revert shadcn component changes, apply hover fix at usage site
* feat: add AWS credentials support for Bedrock provider
- Add AWS Access Key ID, Secret Access Key, Region fields for Bedrock
- Show different credential fields based on provider type
- Update validation API to handle Bedrock with AWS credentials
- Add region selector with common AWS regions
* fix: reset Test button after validation completes
* fix: reset validation button to Test after success
* fix: complete bedrock support and UI/UX improvements
- Add bedrock to ALLOWED_CLIENT_PROVIDERS for client credentials
- Pass AWS credentials through full chain (headers → API → provider)
- Replace non-existent GPT-5 models with real ones (o1, o3-mini)
- Add accessibility: aria-labels, focus-visible rings, inline errors
- Add more AWS regions (Ohio, London, Paris, Mumbai, Seoul, São Paulo)
- Fix setTimeout cleanup with useRef on component unmount
- Fix TypeScript type consistency in getSelectedAIConfig fallback
* chore: remove unused code
- Remove unused setAccessCodeRequired state in chat-panel.tsx
- Remove unused getSelectedModel export in model-config.ts
* fix: UI/UX improvements for model configuration dialog
- Add gradient header styling with icon badge
- Change Configuration section icon from Key to Settings2
- Add duplicate model detection with warning banner and inline removal
- Filter out already-added models from suggestions dropdown
- Add type-to-confirm for deleting providers with 3+ models
- Enhance delete confirmation dialog with warning icon
- Improve model selector discoverability (show model name + chevron)
- Add truncation for long model names with title tooltip
- Remove AI provider settings from Settings dialog (now in Model Config)
- Extract ValidationButton into reusable component
* fix: prevent duplicate model IDs within same provider
- Block adding model if ID already exists in provider
- Block editing model ID to match existing model in provider
* fix: improve duplicate model ID notifications
- Add toast notification when trying to add duplicate model
- Allow free typing when editing model ID, validate on blur
- Show warning toast instead of blocking input
* fix: improve duplicate model validation UX in config dialog
- Add inline error display for duplicate model IDs
- Show red border on input when error exists
- Validate on blur with shake animation for edit errors
- Prevent saving empty model names
- Clear errors when user starts typing
- Simplify error styling (small red text, no heavy chips)
2025-12-22 22:36:36 +09:00
|
|
|
/>
|
2025-12-03 21:49:34 +09:00
|
|
|
<div className="w-px h-5 bg-border mx-1" />
|
|
|
|
|
<Button
|
|
|
|
|
type="submit"
|
|
|
|
|
disabled={isDisabled || !input.trim()}
|
|
|
|
|
size="sm"
|
|
|
|
|
className="h-8 px-4 rounded-xl font-medium shadow-sm"
|
2025-12-05 19:30:50 +09:00
|
|
|
aria-label={
|
2025-12-20 20:18:54 +05:30
|
|
|
isDisabled ? dict.chat.sending : dict.chat.send
|
2025-12-05 19:30:50 +09:00
|
|
|
}
|
2025-12-03 21:49:34 +09:00
|
|
|
>
|
|
|
|
|
{isDisabled ? (
|
|
|
|
|
<Loader2 className="h-4 w-4 animate-spin" />
|
|
|
|
|
) : (
|
|
|
|
|
<>
|
|
|
|
|
<Send className="h-4 w-4 mr-1.5" />
|
2025-12-20 20:18:54 +05:30
|
|
|
{dict.chat.send}
|
2025-12-03 21:49:34 +09:00
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
2025-03-23 11:03:25 +00:00
|
|
|
</div>
|
2025-03-22 13:15:51 +00:00
|
|
|
</div>
|
2025-12-28 18:46:10 +05:30
|
|
|
<HistoryDialog
|
|
|
|
|
showHistory={showHistory}
|
|
|
|
|
onToggleHistory={setShowHistory}
|
|
|
|
|
/>
|
|
|
|
|
<SaveDialog
|
|
|
|
|
open={showSaveDialog}
|
|
|
|
|
onOpenChange={setShowSaveDialog}
|
|
|
|
|
onSave={(filename, format) =>
|
|
|
|
|
saveDiagramToFile(filename, format, sessionId)
|
|
|
|
|
}
|
|
|
|
|
defaultFilename={`diagram-${new Date()
|
|
|
|
|
.toISOString()
|
|
|
|
|
.slice(0, 10)}`}
|
|
|
|
|
/>
|
2025-03-22 13:15:51 +00:00
|
|
|
</form>
|
2025-12-06 12:46:40 +09:00
|
|
|
)
|
2025-03-22 13:15:51 +00:00
|
|
|
}
|