Vendor can show more
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { Search, Filter, Plus, Trash2, Key, Server, Ear, Globe, Languages, Pencil, Mic, Square, Upload } from 'lucide-react';
|
import { Search, Filter, Plus, Trash2, Key, Server, Ear, Globe, Languages, Pencil, Mic, Square, Upload } from 'lucide-react';
|
||||||
import { Button, Input, Select, TableHeader, TableRow, TableHead, TableCell, Dialog, Badge, LibraryPageShell, TableStatusRow, LibraryActionCell } from '../components/UI';
|
import { Button, Input, Select, TableHeader, TableRow, TableHead, TableCell, Dialog, Badge, LibraryPageShell, TableStatusRow, LibraryActionCell } from '../components/UI';
|
||||||
import { ASRModel } from '../types';
|
import { ASRModel } from '../types';
|
||||||
@@ -85,7 +85,7 @@ const convertRecordedBlobToWav = async (blob: Blob): Promise<File> => {
|
|||||||
export const ASRLibraryPage: React.FC = () => {
|
export const ASRLibraryPage: React.FC = () => {
|
||||||
const [models, setModels] = useState<ASRModel[]>([]);
|
const [models, setModels] = useState<ASRModel[]>([]);
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [vendorFilter, setVendorFilter] = useState<string>('OpenAI Compatible');
|
const [vendorFilter, setVendorFilter] = useState<string>('all');
|
||||||
const [langFilter, setLangFilter] = useState<string>('all');
|
const [langFilter, setLangFilter] = useState<string>('all');
|
||||||
const [isAddModalOpen, setIsAddModalOpen] = useState(false);
|
const [isAddModalOpen, setIsAddModalOpen] = useState(false);
|
||||||
const [editingModel, setEditingModel] = useState<ASRModel | null>(null);
|
const [editingModel, setEditingModel] = useState<ASRModel | null>(null);
|
||||||
@@ -108,10 +108,15 @@ export const ASRLibraryPage: React.FC = () => {
|
|||||||
loadModels();
|
loadModels();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const vendorOptions = useMemo(
|
||||||
|
() => Array.from(new Set(models.map((m) => String(m.vendor || '').trim()).filter(Boolean))).sort(),
|
||||||
|
[models]
|
||||||
|
);
|
||||||
|
|
||||||
const filteredModels = models.filter((m) => {
|
const filteredModels = models.filter((m) => {
|
||||||
const q = searchTerm.toLowerCase();
|
const q = searchTerm.toLowerCase();
|
||||||
const matchesSearch = m.name.toLowerCase().includes(q) || (m.modelName || '').toLowerCase().includes(q);
|
const matchesSearch = m.name.toLowerCase().includes(q) || (m.modelName || '').toLowerCase().includes(q);
|
||||||
const matchesVendor = m.vendor === vendorFilter;
|
const matchesVendor = vendorFilter === 'all' || m.vendor === vendorFilter;
|
||||||
const matchesLang = langFilter === 'all' || m.language === langFilter || (langFilter !== 'all' && m.language === 'Multi-lingual');
|
const matchesLang = langFilter === 'all' || m.language === langFilter || (langFilter !== 'all' && m.language === 'Multi-lingual');
|
||||||
return matchesSearch && matchesVendor && matchesLang;
|
return matchesSearch && matchesVendor && matchesLang;
|
||||||
});
|
});
|
||||||
@@ -159,7 +164,10 @@ export const ASRLibraryPage: React.FC = () => {
|
|||||||
value={vendorFilter}
|
value={vendorFilter}
|
||||||
onChange={(e) => setVendorFilter(e.target.value)}
|
onChange={(e) => setVendorFilter(e.target.value)}
|
||||||
>
|
>
|
||||||
<option value="OpenAI Compatible">OpenAI Compatible</option>
|
<option value="all">所有厂商</option>
|
||||||
|
{vendorOptions.map((vendor) => (
|
||||||
|
<option key={vendor} value={vendor}>{vendor}</option>
|
||||||
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import { Search, Filter, Plus, BrainCircuit, Trash2, Key, Settings2, Server, Thermometer, Pencil, Play } from 'lucide-react';
|
import { Search, Filter, Plus, BrainCircuit, Trash2, Key, Settings2, Server, Thermometer, Pencil, Play } from 'lucide-react';
|
||||||
import { Button, Input, Select, TableHeader, TableRow, TableHead, TableCell, Dialog, Badge, LibraryPageShell, TableStatusRow, LibraryActionCell } from '../components/UI';
|
import { Button, Input, Select, TableHeader, TableRow, TableHead, TableCell, Dialog, Badge, LibraryPageShell, TableStatusRow, LibraryActionCell } from '../components/UI';
|
||||||
import { LLMModel } from '../types';
|
import { LLMModel } from '../types';
|
||||||
@@ -13,7 +13,7 @@ const maskApiKey = (key?: string) => {
|
|||||||
export const LLMLibraryPage: React.FC = () => {
|
export const LLMLibraryPage: React.FC = () => {
|
||||||
const [models, setModels] = useState<LLMModel[]>([]);
|
const [models, setModels] = useState<LLMModel[]>([]);
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [vendorFilter, setVendorFilter] = useState<string>('OpenAI Compatible');
|
const [vendorFilter, setVendorFilter] = useState<string>('all');
|
||||||
const [typeFilter, setTypeFilter] = useState<string>('all');
|
const [typeFilter, setTypeFilter] = useState<string>('all');
|
||||||
const [isAddModalOpen, setIsAddModalOpen] = useState(false);
|
const [isAddModalOpen, setIsAddModalOpen] = useState(false);
|
||||||
const [editingModel, setEditingModel] = useState<LLMModel | null>(null);
|
const [editingModel, setEditingModel] = useState<LLMModel | null>(null);
|
||||||
@@ -35,13 +35,18 @@ export const LLMLibraryPage: React.FC = () => {
|
|||||||
load();
|
load();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const vendorOptions = useMemo(
|
||||||
|
() => Array.from(new Set(models.map((m) => String(m.vendor || '').trim()).filter(Boolean))).sort(),
|
||||||
|
[models]
|
||||||
|
);
|
||||||
|
|
||||||
const filteredModels = models.filter((m) => {
|
const filteredModels = models.filter((m) => {
|
||||||
const q = searchTerm.toLowerCase();
|
const q = searchTerm.toLowerCase();
|
||||||
const matchesSearch =
|
const matchesSearch =
|
||||||
m.name.toLowerCase().includes(q) ||
|
m.name.toLowerCase().includes(q) ||
|
||||||
(m.modelName || '').toLowerCase().includes(q) ||
|
(m.modelName || '').toLowerCase().includes(q) ||
|
||||||
(m.baseUrl || '').toLowerCase().includes(q);
|
(m.baseUrl || '').toLowerCase().includes(q);
|
||||||
const matchesVendor = m.vendor === vendorFilter;
|
const matchesVendor = vendorFilter === 'all' || m.vendor === vendorFilter;
|
||||||
const matchesType = typeFilter === 'all' || m.type === typeFilter;
|
const matchesType = typeFilter === 'all' || m.type === typeFilter;
|
||||||
return matchesSearch && matchesVendor && matchesType;
|
return matchesSearch && matchesVendor && matchesType;
|
||||||
});
|
});
|
||||||
@@ -89,7 +94,10 @@ export const LLMLibraryPage: React.FC = () => {
|
|||||||
value={vendorFilter}
|
value={vendorFilter}
|
||||||
onChange={(e) => setVendorFilter(e.target.value)}
|
onChange={(e) => setVendorFilter(e.target.value)}
|
||||||
>
|
>
|
||||||
<option value="OpenAI Compatible">OpenAI Compatible</option>
|
<option value="all">所有厂商</option>
|
||||||
|
{vendorOptions.map((vendor) => (
|
||||||
|
<option key={vendor} value={vendor}>{vendor}</option>
|
||||||
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState, useRef } from 'react';
|
import React, { useEffect, useMemo, useState, useRef } from 'react';
|
||||||
import { Search, Mic2, Play, Pause, Upload, Filter, Plus, Volume2, Pencil, Trash2 } from 'lucide-react';
|
import { Search, Mic2, Play, Pause, Upload, Filter, Plus, Volume2, Pencil, Trash2 } from 'lucide-react';
|
||||||
import { Button, Input, Select, TableHeader, TableRow, TableHead, TableCell, Dialog, Badge, LibraryPageShell, TableStatusRow, LibraryActionCell } from '../components/UI';
|
import { Button, Input, Select, TableHeader, TableRow, TableHead, TableCell, Dialog, Badge, LibraryPageShell, TableStatusRow, LibraryActionCell } from '../components/UI';
|
||||||
import { Voice } from '../types';
|
import { Voice } from '../types';
|
||||||
@@ -15,7 +15,7 @@ const buildOpenAICompatibleVoiceKey = (rawId: string, model: string): string =>
|
|||||||
export const VoiceLibraryPage: React.FC = () => {
|
export const VoiceLibraryPage: React.FC = () => {
|
||||||
const [voices, setVoices] = useState<Voice[]>([]);
|
const [voices, setVoices] = useState<Voice[]>([]);
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [vendorFilter, setVendorFilter] = useState<'OpenAI Compatible'>('OpenAI Compatible');
|
const [vendorFilter, setVendorFilter] = useState<string>('all');
|
||||||
const [genderFilter, setGenderFilter] = useState<'all' | 'Male' | 'Female'>('all');
|
const [genderFilter, setGenderFilter] = useState<'all' | 'Male' | 'Female'>('all');
|
||||||
const [langFilter, setLangFilter] = useState<'all' | 'zh' | 'en'>('all');
|
const [langFilter, setLangFilter] = useState<'all' | 'zh' | 'en'>('all');
|
||||||
|
|
||||||
@@ -42,9 +42,14 @@ export const VoiceLibraryPage: React.FC = () => {
|
|||||||
loadVoices();
|
loadVoices();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const vendorOptions = useMemo(
|
||||||
|
() => Array.from(new Set(voices.map((v) => String(v.vendor || '').trim()).filter(Boolean))).sort(),
|
||||||
|
[voices]
|
||||||
|
);
|
||||||
|
|
||||||
const filteredVoices = voices.filter((voice) => {
|
const filteredVoices = voices.filter((voice) => {
|
||||||
const matchesSearch = voice.name.toLowerCase().includes(searchTerm.toLowerCase());
|
const matchesSearch = voice.name.toLowerCase().includes(searchTerm.toLowerCase());
|
||||||
const matchesVendor = voice.vendor === vendorFilter;
|
const matchesVendor = vendorFilter === 'all' || voice.vendor === vendorFilter;
|
||||||
const matchesGender = genderFilter === 'all' || voice.gender === genderFilter;
|
const matchesGender = genderFilter === 'all' || voice.gender === genderFilter;
|
||||||
const matchesLang = langFilter === 'all' || voice.language === langFilter;
|
const matchesLang = langFilter === 'all' || voice.language === langFilter;
|
||||||
return matchesSearch && matchesVendor && matchesGender && matchesLang;
|
return matchesSearch && matchesVendor && matchesGender && matchesLang;
|
||||||
@@ -135,9 +140,12 @@ export const VoiceLibraryPage: React.FC = () => {
|
|||||||
<Filter className="h-4 w-4 text-muted-foreground" />
|
<Filter className="h-4 w-4 text-muted-foreground" />
|
||||||
<Select
|
<Select
|
||||||
value={vendorFilter}
|
value={vendorFilter}
|
||||||
onChange={(e) => setVendorFilter(e.target.value as any)}
|
onChange={(e) => setVendorFilter(e.target.value)}
|
||||||
>
|
>
|
||||||
<option value="OpenAI Compatible">OpenAI Compatible</option>
|
<option value="all">所有厂商</option>
|
||||||
|
{vendorOptions.map((vendor) => (
|
||||||
|
<option key={vendor} value={vendor}>{vendor}</option>
|
||||||
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
|
|||||||
Reference in New Issue
Block a user