feat: Size ollama listing, Help button
This commit is contained in:
parent
adcda8676d
commit
b0e610ca07
3 changed files with 94 additions and 28 deletions
|
|
@ -1,7 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { LayoutDashboard, Star, Brain, Settings, Palette } from "lucide-react";
|
||||
import { LayoutDashboard, Star, Brain, Settings, Palette, HelpCircle } from "lucide-react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
|
@ -105,11 +105,16 @@ const DashboardSidebar = () => {
|
|||
|
||||
<div className=" pt-5 border-t border-[#E1E1E5] font-syne "
|
||||
>
|
||||
<div className="mb-4">
|
||||
|
||||
<Link href="https://docs.presenton.ai/help" target="_blank" className="flex flex-col tex-center items-center gap-2 transition-colors"><HelpCircle className="w-4 h-4" /><span className="text-[11px] text-slate-800">Help</span></Link>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
|
||||
<Link href="https://discord.com/invite/9ZsKKxudNE" target="_blank" className="flex flex-col tex-center items-center gap-2 transition-colors"><img src="/discord.png" alt="Discord" className="w-5 h-5 rounded-full object-cover border border-[#EDEEEF]" /><span className="text-[11px] text-slate-800">Community</span></Link>
|
||||
</div>
|
||||
|
||||
|
||||
{BelongingNavItems.map(({ key, label: itemLabel, icon: Icon }) => {
|
||||
const isActive = activeTab === key;
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -17,6 +17,13 @@ interface OpenAIConfigProps {
|
|||
onInputChange: (value: string | boolean, field: string) => void;
|
||||
llmConfig: LLMConfig;
|
||||
}
|
||||
|
||||
interface ModelOption {
|
||||
value: string;
|
||||
label: string;
|
||||
size?: string;
|
||||
}
|
||||
|
||||
const TextProvider = ({
|
||||
|
||||
onInputChange,
|
||||
|
|
@ -26,7 +33,7 @@ const TextProvider = ({
|
|||
) => {
|
||||
const [openProviderSelect, setOpenProviderSelect] = useState(false);
|
||||
const [openModelSelect, setOpenModelSelect] = useState(false);
|
||||
const [availableModels, setAvailableModels] = useState<string[]>([]);
|
||||
const [availableModels, setAvailableModels] = useState<ModelOption[]>([]);
|
||||
const [modelsLoading, setModelsLoading] = useState(false);
|
||||
const [modelsChecked, setModelsChecked] = useState(false);
|
||||
const [showApiKey, setShowApiKey] = useState(false);
|
||||
|
|
@ -157,19 +164,48 @@ const TextProvider = ({
|
|||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
const normalizedModels: string[] = selectedProvider === 'ollama'
|
||||
const normalizedModels: ModelOption[] = selectedProvider === 'ollama'
|
||||
? Array.isArray(data)
|
||||
? data.map((model: { value?: string; label?: string }) => model.value || model.label || '').filter(Boolean)
|
||||
? data
|
||||
.map((model) => {
|
||||
if (typeof model === 'string') {
|
||||
return {
|
||||
value: model,
|
||||
label: model,
|
||||
};
|
||||
}
|
||||
|
||||
if (model && typeof model === 'object') {
|
||||
const typedModel = model as { value?: string; label?: string; size?: string };
|
||||
return {
|
||||
value: typedModel.value || typedModel.label || '',
|
||||
label: typedModel.label || typedModel.value || '',
|
||||
size: typedModel.size,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
value: '',
|
||||
label: '',
|
||||
};
|
||||
})
|
||||
.filter((model: ModelOption) => Boolean(model.value))
|
||||
: []
|
||||
: Array.isArray(data)
|
||||
? data
|
||||
.filter((model): model is string => typeof model === 'string')
|
||||
.map((model) => ({
|
||||
value: model,
|
||||
label: model,
|
||||
}))
|
||||
: [];
|
||||
|
||||
setAvailableModels(normalizedModels);
|
||||
setModelsChecked(true);
|
||||
|
||||
if (normalizedModels.length > 0 && currentModelField) {
|
||||
if (currentModel && normalizedModels.includes(currentModel)) {
|
||||
const modelValues = normalizedModels.map((model) => model.value);
|
||||
if (currentModel && modelValues.includes(currentModel)) {
|
||||
onInputChange(currentModel, currentModelField);
|
||||
return;
|
||||
}
|
||||
|
|
@ -181,9 +217,9 @@ const TextProvider = ({
|
|||
? 'models/gemini-2.5-flash'
|
||||
: selectedProvider === 'anthropic'
|
||||
? 'claude-sonnet-4-20250514'
|
||||
: normalizedModels[0];
|
||||
: modelValues[0];
|
||||
|
||||
const nextModel = normalizedModels.includes(preferredDefault) ? preferredDefault : normalizedModels[0];
|
||||
const nextModel = modelValues.includes(preferredDefault) ? preferredDefault : modelValues[0];
|
||||
onInputChange(nextModel, currentModelField);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -401,8 +437,6 @@ const TextProvider = ({
|
|||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{selectedProvider !== 'ollama' && selectedProvider !== 'codex' && (!modelsChecked || (modelsChecked && availableModels.length === 0)) && (
|
||||
|
||||
<button
|
||||
|
|
@ -451,9 +485,15 @@ const TextProvider = ({
|
|||
className="w-full h-12 px-4 py-4 outline-none border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-colors hover:border-gray-400 justify-between"
|
||||
>
|
||||
<span className="text-sm truncate font-medium text-gray-900">
|
||||
{currentModel
|
||||
? availableModels.find(model => model === currentModel) || currentModel
|
||||
: "Select a model"}
|
||||
{(() => {
|
||||
if (!currentModel) return "Select a model";
|
||||
const selectedModel = availableModels.find((model) => model.value === currentModel);
|
||||
if (!selectedModel) return currentModel;
|
||||
if (selectedProvider === 'ollama' && selectedModel.size) {
|
||||
return `${selectedModel.label} (${selectedModel.size})`;
|
||||
}
|
||||
return selectedModel.label;
|
||||
})()}
|
||||
</span>
|
||||
|
||||
<ChevronUp className="w-4 h-4 text-gray-500" />
|
||||
|
|
@ -469,13 +509,13 @@ const TextProvider = ({
|
|||
<CommandList>
|
||||
<CommandEmpty>No model found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{availableModels.map((model, index) => (
|
||||
{availableModels.map((model) => (
|
||||
<CommandItem
|
||||
key={index}
|
||||
value={model}
|
||||
onSelect={(value) => {
|
||||
key={model.value}
|
||||
value={model.value}
|
||||
onSelect={() => {
|
||||
if (currentModelField) {
|
||||
onInputChange(value, currentModelField);
|
||||
onInputChange(model.value, currentModelField);
|
||||
}
|
||||
setOpenModelSelect(false);
|
||||
}}
|
||||
|
|
@ -483,7 +523,7 @@ const TextProvider = ({
|
|||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
currentModel === model
|
||||
currentModel === model.value
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
)}
|
||||
|
|
@ -492,8 +532,13 @@ const TextProvider = ({
|
|||
<div className="flex flex-col space-y-1 flex-1">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<span className="text-sm font-medium text-gray-900">
|
||||
{model}
|
||||
{model.label}
|
||||
</span>
|
||||
{selectedProvider === 'ollama' && model.size ? (
|
||||
<span className="text-xs font-medium text-gray-500">
|
||||
{model.size}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -22,15 +22,23 @@ export const PRISM_CODE_BLOCK_STYLES = `
|
|||
white-space: inherit !important;
|
||||
}
|
||||
|
||||
.prism-code-block {
|
||||
--code-fg: var(--background-text, #dbe5ff);
|
||||
--code-accent: var(--primary-color, #7aa2ff);
|
||||
color: var(--code-fg);
|
||||
}
|
||||
|
||||
.prism-code-block .token.comment,
|
||||
.prism-code-block .token.prolog,
|
||||
.prism-code-block .token.doctype,
|
||||
.prism-code-block .token.cdata {
|
||||
color: #7b8ebf;
|
||||
color: var(--code-fg);
|
||||
opacity: 0.62;
|
||||
}
|
||||
|
||||
.prism-code-block .token.punctuation {
|
||||
color: #a8b7e0;
|
||||
color: var(--code-fg);
|
||||
opacity: 0.82;
|
||||
}
|
||||
|
||||
.prism-code-block .token.property,
|
||||
|
|
@ -38,12 +46,14 @@ export const PRISM_CODE_BLOCK_STYLES = `
|
|||
.prism-code-block .token.constant,
|
||||
.prism-code-block .token.symbol,
|
||||
.prism-code-block .token.deleted {
|
||||
color: #7bc4ff;
|
||||
color: var(--graph-0, #7bc4ff);
|
||||
color: color-mix(in srgb, var(--graph-0, #7bc4ff) 72%, var(--code-fg) 28%);
|
||||
}
|
||||
|
||||
.prism-code-block .token.boolean,
|
||||
.prism-code-block .token.number {
|
||||
color: #f5c97b;
|
||||
color: var(--graph-1, #f5c97b);
|
||||
color: color-mix(in srgb, var(--graph-1, #f5c97b) 68%, var(--code-fg) 32%);
|
||||
}
|
||||
|
||||
.prism-code-block .token.selector,
|
||||
|
|
@ -52,30 +62,36 @@ export const PRISM_CODE_BLOCK_STYLES = `
|
|||
.prism-code-block .token.char,
|
||||
.prism-code-block .token.builtin,
|
||||
.prism-code-block .token.inserted {
|
||||
color: #9fe6b8;
|
||||
color: var(--graph-2, #9fe6b8);
|
||||
color: color-mix(in srgb, var(--graph-2, #9fe6b8) 72%, var(--code-fg) 28%);
|
||||
}
|
||||
|
||||
.prism-code-block .token.operator,
|
||||
.prism-code-block .token.entity,
|
||||
.prism-code-block .token.url,
|
||||
.prism-code-block .token.variable {
|
||||
color: #f5a97f;
|
||||
color: var(--graph-3, #f5a97f);
|
||||
color: color-mix(in srgb, var(--graph-3, #f5a97f) 70%, var(--code-fg) 30%);
|
||||
}
|
||||
|
||||
.prism-code-block .token.atrule,
|
||||
.prism-code-block .token.attr-value,
|
||||
.prism-code-block .token.function,
|
||||
.prism-code-block .token.class-name {
|
||||
color: #b8a8ff;
|
||||
color: var(--graph-4, #b8a8ff);
|
||||
color: color-mix(in srgb, var(--graph-4, #b8a8ff) 74%, var(--code-fg) 26%);
|
||||
}
|
||||
|
||||
.prism-code-block .token.keyword {
|
||||
color: #7aa2ff;
|
||||
color: var(--code-accent);
|
||||
color: color-mix(in srgb, var(--code-accent, #7aa2ff) 78%, var(--code-fg) 22%);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.prism-code-block .token.regex,
|
||||
.prism-code-block .token.important {
|
||||
color: #f9e2af;
|
||||
color: var(--code-accent);
|
||||
color: color-mix(in srgb, var(--code-accent, #7aa2ff) 64%, var(--graph-1, #f5c97b) 36%);
|
||||
}
|
||||
`;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue