Extract Model Table to component (#4871)

* Extract Model Table to component
Add provider icons to header rows and installed models
Light mode supported
Mapping for model name id hints to provider
Update DMR to filter chat models by ability since not available via hub API

* linting + dev

* fix incorrect import
This commit is contained in:
Timothy Carambat 2026-01-16 16:34:58 -08:00 committed by GitHub
parent e07963d3fc
commit 607b5faf74
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 692 additions and 153 deletions

View File

@ -6,7 +6,7 @@ concurrency:
on:
push:
branches: ['fix-path-patch'] # put your current branch to create a build. Core team only.
branches: ['model-table-component'] # put your current branch to create a build. Core team only.
paths-ignore:
- '**.md'
- 'cloud-deployments/*'

View File

@ -12,6 +12,7 @@
"preview": "vite preview"
},
"dependencies": {
"@lobehub/icons": "^4.0.3",
"@microsoft/fetch-event-source": "^2.0.1",
"@mintplex-labs/piper-tts-web": "^1.0.4",
"@phosphor-icons/react": "^2.1.7",

View File

@ -1,20 +1,15 @@
import { useState, useEffect } from "react";
import System from "@/models/system";
import useProviderEndpointAutoDiscovery from "@/hooks/useProviderEndpointAutoDiscovery";
import {
ArrowClockwise,
CircleNotch,
MagnifyingGlass,
Info,
} from "@phosphor-icons/react";
import { CircleNotch, Info } from "@phosphor-icons/react";
import strDistance from "js-levenshtein";
import { LLM_PREFERENCE_CHANGED_EVENT } from "@/pages/GeneralSettings/LLMPreference";
import { DOCKER_MODEL_RUNNER_COMMON_URLS } from "@/utils/constants";
import { Tooltip } from "react-tooltip";
import { Link } from "react-router-dom";
import ModelTable from "./ModelTable";
import * as Skeleton from "react-loading-skeleton";
import "react-loading-skeleton/dist/skeleton.css";
import ModelTable from "@/components/lib/ModelTable";
import ModelTableLayout from "@/components/lib/ModelTable/layout";
import ModelTableLoadingSkeleton from "@/components/lib/ModelTable/loading";
export default function DockerModelRunnerOptions({ settings }) {
const {
@ -280,11 +275,12 @@ function DockerModelRunnerModelSelection({
});
const orderedMap = new Map();
const installedMap = new Map();
mapping
.get("installed")
.entries()
.forEach(([organization, models]) =>
orderedMap.set(organization, models)
installedMap.set(organization, models)
);
mapping
.get("not installed")
@ -292,7 +288,17 @@ function DockerModelRunnerModelSelection({
.forEach(([organization, models]) =>
orderedMap.set(organization, models)
);
return Object.fromEntries(orderedMap);
// Sort the models by organization/creator name alphabetically but keep the installed models at the top
return Object.fromEntries(
Array.from(installedMap.entries())
.sort((a, b) => a[0].localeCompare(b[0]))
.concat(
Array.from(orderedMap.entries()).sort((a, b) =>
a[0].localeCompare(b[0])
)
)
);
}
function handleSetActiveModel(modelId) {
@ -303,7 +309,7 @@ function DockerModelRunnerModelSelection({
const groupedModels = groupModelsByAlias(filteredModels);
return (
<Layout
<ModelTableLayout
fetchModels={fetchModels}
searchQuery={searchQuery}
setSearchQuery={setSearchQuery}
@ -321,7 +327,7 @@ function DockerModelRunnerModelSelection({
value={selectedModelId}
/>
{loading ? (
<LoadingSkeleton />
<ModelTableLoadingSkeleton />
) : filteredModels.length === 0 ? (
<div className="flex flex-col w-full gap-y-2 mt-4">
<p className="text-theme-text-secondary text-sm">No models found!</p>
@ -341,96 +347,6 @@ function DockerModelRunnerModelSelection({
/>
))
)}
</Layout>
);
}
function Layout({
children,
fetchModels = null,
searchQuery = "",
setSearchQuery = () => {},
loading = false,
}) {
const [isRefreshing, setIsRefreshing] = useState(false);
async function refreshModels() {
setIsRefreshing(true);
try {
await fetchModels?.();
} catch {
} finally {
setIsRefreshing(false);
}
}
return (
<div className="flex flex-col w-full">
<div className="flex gap-x-2 items-center pb-[8px]">
<label className="text-theme-text-primary text-base font-semibold">
Available Models
</label>
</div>
<div className="flex w-full items-center gap-x-[16px]">
<div className="relative flex-1 flex-grow">
<MagnifyingGlass
size={16}
weight="bold"
color="var(--theme-text-primary)"
className="absolute left-[9px] top-[10px] text-theme-settings-input-placeholder peer-focus:invisible"
/>
<input
type="search"
placeholder="Search models"
value={searchQuery}
disabled={loading}
className="min-h-[32px] border-none bg-theme-settings-input-bg text-white placeholder:text-theme-settings-input-placeholder text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5 pl-[30px] py-2 search-input disabled:opacity-50 disabled:cursor-not-allowed"
onChange={(e) => {
e.preventDefault();
e.stopPropagation();
setSearchQuery(e.target.value);
}}
/>
</div>
{!!fetchModels && (
<button
type="button"
onClick={refreshModels}
disabled={isRefreshing || loading}
className="border-none text-theme-text-secondary text-sm font-medium hover:bg-white/10 light:hover:bg-black/5 rounded-lg px-2 h-full flex items-center gap-x-1 disabled:opacity-50 disabled:cursor-not-allowed"
>
{isRefreshing ? (
<CircleNotch className="w-4 h-4 text-theme-text-secondary animate-spin" />
) : (
<ArrowClockwise
weight="bold"
className="w-4 h-4 text-theme-text-secondary"
/>
)}
<span
className={`text-sm font-medium ${isRefreshing ? "hidden" : "text-theme-text-secondary"}`}
>
Refresh Models
</span>
</button>
)}
</div>
{children}
</div>
);
}
function LoadingSkeleton() {
return (
<div className="flex flex-col w-full gap-y-4">
<Skeleton.default
height={100}
width="100%"
count={7}
highlightColor="var(--theme-settings-input-active)"
baseColor="var(--theme-settings-input-bg)"
enableAnimation={true}
containerClassName="w-fill flex gap-[8px] flex-col p-0"
/>
</div>
</ModelTableLayout>
);
}

View File

@ -6,10 +6,12 @@ import {
Circle,
DotsThreeVertical,
CloudArrowDown,
CircleNotch,
} from "@phosphor-icons/react";
import pluralize from "pluralize";
import { titleCase } from "text-case";
import { humanFileSize } from "@/utils/numbers";
import MonoProviderIcon from "../MonoProviderIcon";
/**
* @typedef {Object} ModelDefinition
@ -67,18 +69,27 @@ export default function ModelTable({
className="text-theme-text-secondary"
/>
)}
<h3 className="flex items-center gap-x-1 text-theme-text-primary text-base font-bold">
{titleCase(alias)}
<span className="text-theme-text-secondary font-normal text-sm">
({totalModels} {pluralize("Model", totalModels)})
</span>
</h3>
<div className="flex items-center gap-x-[4px]">
<MonoProviderIcon
provider={alias}
match="pattern"
size={16}
className="text-theme-text-primary"
/>
<p className="flex items-center gap-x-1 text-theme-text-primary text-base font-bold">
{titleCase(alias)}
<span className="text-theme-text-secondary font-normal text-sm">
({totalModels} {pluralize("Model", totalModels)})
</span>
</p>
</div>
</button>
<div hidden={!showAll} className="mt-[16px]">
<div className="w-full flex flex-col gap-y-[8px]">
{models.map((model) => (
<ModelRow
key={model.id}
alias={alias}
model={model}
downloadModel={downloadModel}
uninstallModel={uninstallModel}
@ -105,7 +116,7 @@ function DeviceTypeTag({ deviceType }) {
bgClass + " px-1.5 py-1 rounded-full flex items-center gap-x-1 w-fit"
}
>
<Cpu size={16} weight="bold" className={textClass} />
<Cpu size={14} weight="bold" className={textClass} />
<p className={textClass + " text-xs"}>{text}</p>
</div>
);
@ -116,32 +127,32 @@ function DeviceTypeTag({ deviceType }) {
return (
<Wrapper
text="CPU"
bgClass="bg-blue-600/20"
textClass="text-blue-300"
bgClass="bg-zinc-800 light:bg-zinc-200"
textClass="text-theme-text-primary"
/>
);
case "gpu":
return (
<Wrapper
text="GPU"
bgClass="bg-green-600/20"
textClass="text-green-300"
bgClass="bg-green-800 light:bg-green-200"
textClass="text-theme-text-primary"
/>
);
case "npu":
return (
<Wrapper
text="NPU"
bgClass="bg-indigo-600/20"
textClass="text-indigo-300"
bgClass="bg-indigo-800 light:bg-indigo-200"
textClass="text-theme-text-primary"
/>
);
default:
return (
<Wrapper
text="CPU"
bgClass="bg-blue-600/20"
textClass="text-blue-300"
bgClass="bg-zinc-800 light:bg-zinc-200"
textClass="text-theme-text-primary"
/>
);
}
@ -159,6 +170,7 @@ function DeviceTypeTag({ deviceType }) {
* @returns {React.ReactNode}
*/
function ModelRow({
alias,
model,
downloadModel = null,
uninstallModel = null,
@ -171,7 +183,7 @@ function ModelRow({
const modelRowRef = useRef(null);
const [showOptions, setShowOptions] = useState(false);
const [processing, setProcessing] = useState(false);
const [_downloadPercentage, setDownloadPercentage] = useState(0);
const [downloadPercentage, setDownloadPercentage] = useState(0);
const fileSize =
typeof model.size === "number"
? humanFileSize(model.size * 1e6, true, 2)
@ -187,9 +199,9 @@ function ModelRow({
try {
if (!downloadModel) return;
setProcessing(true);
await downloadModel(model.id, fileSize, (percentage) =>
setDownloadPercentage(percentage)
);
await downloadModel(model.id, fileSize, (percentage) => {
setDownloadPercentage(percentage);
});
} catch {
} finally {
setProcessing(false);
@ -233,31 +245,24 @@ function ModelRow({
onClick={handleSetActiveModel}
>
{ui.showRuntime && <DeviceTypeTag deviceType={model.deviceType} />}
<p className="text-theme-text-primary text-base px-2">{model.name}</p>
{!ui.showRuntime &&
model.downloaded &&
alias === "Downloaded Models" && (
<MonoProviderIcon
provider={model.organization}
match="pattern"
size={16}
className="text-theme-text-primary"
/>
)}
<p className="text-theme-text-primary text-base">{model.name}</p>
<p className="text-theme-text-secondary opacity-70 text-base">
{fileSize}
</p>
</button>
<div className="justify-self-start">
{isActiveModel && (
<div className="flex items-center justify-center gap-x-[10px] whitespace-nowrap">
<Circle size={8} weight="fill" className="text-green-500" />
<p className="text-theme-text-primary text-sm">Active</p>
</div>
)}
{!isActiveModel && model.downloaded && !uninstallModel && (
<p className="text-theme-text-secondary text-sm italic whitespace-nowrap">
Installed
</p>
)}
{!model.downloaded && (
<p className="text-theme-text-secondary text-sm italic whitespace-nowrap">
Not Installed
</p>
)}
<RenderStatus model={model} isActiveModel={isActiveModel} />
</div>
<div className="relative justify-self-end">
@ -289,7 +294,7 @@ function ModelRow({
)}
</>
) : null}
{!model.downloaded ? (
{!model.downloaded && !processing && (
<button
type="button"
data-tooltip-id="docker-model-runner-install-model-tooltip"
@ -305,8 +310,50 @@ function ModelRow({
className="text-theme-text-primary"
/>
</button>
) : null}
)}
{!model.downloaded && processing && (
<div className="flex items-center justify-center gap-x-[10px] whitespace-nowrap">
{!downloadPercentage && (
<CircleNotch
size={16}
weight="bold"
className="text-theme-text-primary animate-spin"
/>
)}
<p className="text-theme-text-secondary text-sm">
{downloadPercentage}%
</p>
</div>
)}
</div>
</div>
);
}
function RenderStatus({ model, isActiveModel }) {
if (isActiveModel) {
return (
<div className="flex items-center justify-center gap-x-[10px] whitespace-nowrap">
<Circle size={8} weight="fill" className="text-green-500" />
<p className="text-theme-text-primary text-sm">Active</p>
</div>
);
}
if (!isActiveModel && model.downloaded) {
return (
<p className="text-theme-text-secondary text-sm italic whitespace-nowrap">
Installed
</p>
);
}
if (!model.downloaded) {
return (
<p className="text-theme-text-secondary text-sm italic whitespace-nowrap">
Not Installed
</p>
);
}
return null;
}

View File

@ -0,0 +1,80 @@
import { useState } from "react";
import {
ArrowClockwise,
CircleNotch,
MagnifyingGlass,
} from "@phosphor-icons/react";
export default function ModelTableLayout({
children,
fetchModels = null,
searchQuery = "",
setSearchQuery = () => {},
loading = false,
}) {
const [isRefreshing, setIsRefreshing] = useState(false);
async function refreshModels() {
setIsRefreshing(true);
try {
await fetchModels?.();
} catch {
} finally {
setIsRefreshing(false);
}
}
return (
<div className="flex flex-col w-full">
<div className="flex gap-x-2 items-center pb-[8px]">
<label className="text-theme-text-primary text-base font-semibold">
Available Models
</label>
</div>
<div className="flex w-full items-center gap-x-[16px]">
<div className="relative flex-1 flex-grow">
<MagnifyingGlass
size={16}
weight="bold"
color="var(--theme-text-primary)"
className="absolute left-[9px] top-[10px] text-theme-settings-input-placeholder peer-focus:invisible"
/>
<input
type="search"
placeholder="Search models"
value={searchQuery}
disabled={loading}
className="min-h-[32px] border-none bg-theme-settings-input-bg text-white placeholder:text-theme-settings-input-placeholder text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5 pl-[30px] py-2 search-input disabled:opacity-50 disabled:cursor-not-allowed"
onChange={(e) => {
e.preventDefault();
e.stopPropagation();
setSearchQuery(e.target.value);
}}
/>
</div>
{!!fetchModels && (
<button
type="button"
onClick={refreshModels}
disabled={isRefreshing || loading}
className="border-none text-theme-text-secondary text-sm font-medium hover:bg-white/10 light:hover:bg-black/5 rounded-lg px-2 h-full flex items-center gap-x-1 disabled:opacity-50 disabled:cursor-not-allowed"
>
{isRefreshing ? (
<CircleNotch className="w-4 h-4 text-theme-text-secondary animate-spin" />
) : (
<ArrowClockwise
weight="bold"
className="w-4 h-4 text-theme-text-secondary"
/>
)}
<span
className={`text-sm font-medium ${isRefreshing ? "hidden" : "text-theme-text-secondary"}`}
>
Refresh Models
</span>
</button>
)}
</div>
{children}
</div>
);
}

View File

@ -0,0 +1,18 @@
import * as Skeleton from "react-loading-skeleton";
import "react-loading-skeleton/dist/skeleton.css";
export default function ModelTableLoadingSkeleton() {
return (
<div className="flex flex-col w-full gap-y-4 pt-4">
<Skeleton.default
height={100}
width="100%"
count={7}
highlightColor="var(--theme-settings-input-active)"
baseColor="var(--theme-settings-input-bg)"
enableAnimation={true}
containerClassName="w-fill flex gap-[8px] flex-col p-0"
/>
</div>
);
}

View File

@ -0,0 +1,97 @@
// https://lobehub.com/icons for all the icons
import OpenAI from "@lobehub/icons/es/OpenAI/components/Mono";
import Anthropic from "@lobehub/icons/es/Anthropic/components/Mono";
import Google from "@lobehub/icons/es/Google/components/Mono";
import Gemma from "@lobehub/icons/es/Gemma/components/Mono";
import Gemini from "@lobehub/icons/es/Gemini/components/Mono";
import Microsoft from "@lobehub/icons/es/Microsoft/components/Mono";
import Meta from "@lobehub/icons/es/Meta/components/Mono";
import Mistral from "@lobehub/icons/es/Mistral/components/Mono";
import Azure from "@lobehub/icons/es/Azure/components/Mono";
import AzureAI from "@lobehub/icons/es/AzureAI/components/Mono";
import DeepSeek from "@lobehub/icons/es/DeepSeek/components/Mono";
import HuggingFace from "@lobehub/icons/es/HuggingFace/components/Mono";
import Qwen from "@lobehub/icons/es/Qwen/components/Mono";
import IBM from "@lobehub/icons/es/IBM/components/Mono";
import Bytedance from "@lobehub/icons/es/ByteDance/components/Mono";
import Kimi from "@lobehub/icons/es/Kimi/components/Mono";
import Snowflake from "@lobehub/icons/es/Snowflake/components/Mono";
// Direct provider key -> icon mapping for exact matches
const providerIcons = {
openai: OpenAI,
anthropic: Anthropic,
google: Google,
microsoft: Microsoft,
gemma: Gemma,
gemini: Gemini,
meta: Meta,
mistral: Mistral,
azure: Azure,
azureai: AzureAI,
deepseek: DeepSeek,
huggingface: HuggingFace,
qwen: Qwen,
qwq: Qwen,
ibm: IBM,
bytedance: Bytedance,
kimi: Kimi,
};
// Pattern matching rules: regex pattern -> icon component
// These are checked in order, first match wins
const modelPatterns = [
{ pattern: /^gpt/i, icon: OpenAI },
{ pattern: /^o\d+/i, icon: OpenAI }, // o1, o3, etc.
{ pattern: /^claude-/i, icon: Anthropic },
{ pattern: /^gemini-/i, icon: Gemini },
{ pattern: /gemma/i, icon: Gemma },
{ pattern: /^llama/i, icon: Meta },
{
pattern: /^(mistral|devstral|mixtral|magistral|codestral|ministral)/i,
icon: Mistral,
},
{ pattern: /^deepseek/i, icon: DeepSeek },
{ pattern: /^qwen/i, icon: Qwen },
{ pattern: /^qwq/i, icon: Qwen },
{ pattern: /^phi/i, icon: Microsoft },
{ pattern: /^granite/i, icon: IBM },
{ pattern: /^doubao/i, icon: Bytedance },
{ pattern: /^moonshot/i, icon: Kimi },
{ pattern: /^smol/i, icon: HuggingFace },
{ pattern: /^seed/i, icon: Bytedance },
{ pattern: /^kimi/i, icon: Kimi },
{ pattern: /^snowflake/i, icon: Snowflake },
];
/**
* Find icon by matching model name against known patterns
* @param {string} modelName - The model name to match
* @returns {React.ComponentType|null}
*/
function findIconByModelName(modelName) {
if (!modelName) return null;
const match = modelPatterns.find(({ pattern }) => pattern.test(modelName));
return match?.icon || null;
}
/**
* @param {object} props - The props of the component.
* @param {string} props.provider - The provider key (for exact match) or model name (for pattern match).
* @param {('exact'|'pattern')} props.match - Match mode: 'exact' for provider key, 'pattern' for model name matching.
* @param {number} props.size - The size of the icon.
* @returns {React.ReactNode}
*/
export default function MonoProviderIcon({
provider,
match = "exact",
size = 24,
className = "",
}) {
let Icon = null;
if (match === "exact") Icon = providerIcons[provider?.toLowerCase()];
else if (match === "pattern") Icon = findIconByModelName(provider);
if (!Icon) return null;
return <Icon size={size} className={className} />;
}

View File

@ -15,6 +15,28 @@
"@jridgewell/gen-mapping" "^0.3.5"
"@jridgewell/trace-mapping" "^0.3.24"
"@ant-design/cssinjs@^2.0.0":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-2.0.3.tgz#b6d35b7b00f76fa8a8894d157bc158429ec7b1ca"
integrity sha512-HAo8SZ3a6G8v6jT0suCz1270na6EA3obeJWM4uzRijBhdwdoMAXWK2f4WWkwB28yUufsfk3CAhN1coGPQq4kNQ==
dependencies:
"@babel/runtime" "^7.11.1"
"@emotion/hash" "^0.8.0"
"@emotion/unitless" "^0.7.5"
"@rc-component/util" "^1.4.0"
clsx "^2.1.1"
csstype "^3.1.3"
stylis "^4.3.4"
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.28.6":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.28.6.tgz#72499312ec58b1e2245ba4a4f550c132be4982f7"
integrity sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==
dependencies:
"@babel/helper-validator-identifier" "^7.28.5"
js-tokens "^4.0.0"
picocolors "^1.1.1"
"@babel/code-frame@^7.24.7":
version "7.24.7"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465"
@ -105,6 +127,17 @@
"@jridgewell/trace-mapping" "^0.3.28"
jsesc "^3.0.2"
"@babel/generator@^7.28.6":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.6.tgz#48dcc65d98fcc8626a48f72b62e263d25fc3c3f1"
integrity sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==
dependencies:
"@babel/parser" "^7.28.6"
"@babel/types" "^7.28.6"
"@jridgewell/gen-mapping" "^0.3.12"
"@jridgewell/trace-mapping" "^0.3.28"
jsesc "^3.0.2"
"@babel/helper-compilation-targets@^7.24.8":
version "7.24.8"
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.8.tgz#b607c3161cd9d1744977d4f97139572fe778c271"
@ -154,6 +187,14 @@
dependencies:
"@babel/types" "^7.24.7"
"@babel/helper-module-imports@^7.16.7":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz#60632cbd6ffb70b22823187201116762a03e2d5c"
integrity sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==
dependencies:
"@babel/traverse" "^7.28.6"
"@babel/types" "^7.28.6"
"@babel/helper-module-imports@^7.24.7":
version "7.24.7"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz#f2f980392de5b84c3328fc71d38bd81bbb83042b"
@ -278,6 +319,13 @@
dependencies:
"@babel/types" "^7.28.5"
"@babel/parser@^7.28.6":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.6.tgz#f01a8885b7fa1e56dd8a155130226cd698ef13fd"
integrity sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==
dependencies:
"@babel/types" "^7.28.6"
"@babel/plugin-transform-react-jsx-self@^7.24.5":
version "7.24.7"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.7.tgz#66bff0248ea0b549972e733516ffad577477bdab"
@ -292,6 +340,11 @@
dependencies:
"@babel/helper-plugin-utils" "^7.24.7"
"@babel/runtime@^7.11.1", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.24.1":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.6.tgz#d267a43cb1836dc4d182cce93ae75ba954ef6d2b"
integrity sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==
"@babel/runtime@^7.12.5":
version "7.28.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.4.tgz#a70226016fabe25c5783b2f22d3e1c9bc5ca3326"
@ -336,6 +389,15 @@
"@babel/parser" "^7.27.2"
"@babel/types" "^7.27.1"
"@babel/template@^7.28.6":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.28.6.tgz#0e7e56ecedb78aeef66ce7972b082fce76a23e57"
integrity sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==
dependencies:
"@babel/code-frame" "^7.28.6"
"@babel/parser" "^7.28.6"
"@babel/types" "^7.28.6"
"@babel/traverse@^7.24.7", "@babel/traverse@^7.24.8":
version "7.24.8"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.8.tgz#6c14ed5232b7549df3371d820fbd9abfcd7dfab7"
@ -365,6 +427,19 @@
"@babel/types" "^7.28.5"
debug "^4.3.1"
"@babel/traverse@^7.28.6":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.6.tgz#871ddc79a80599a5030c53b1cc48cbe3a5583c2e"
integrity sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==
dependencies:
"@babel/code-frame" "^7.28.6"
"@babel/generator" "^7.28.6"
"@babel/helper-globals" "^7.28.0"
"@babel/parser" "^7.28.6"
"@babel/template" "^7.28.6"
"@babel/types" "^7.28.6"
debug "^4.3.1"
"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.24.7", "@babel/types@^7.24.8", "@babel/types@^7.24.9":
version "7.24.9"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.9.tgz#228ce953d7b0d16646e755acf204f4cf3d08cc73"
@ -382,6 +457,63 @@
"@babel/helper-string-parser" "^7.27.1"
"@babel/helper-validator-identifier" "^7.28.5"
"@babel/types@^7.28.6":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.6.tgz#c3e9377f1b155005bcc4c46020e7e394e13089df"
integrity sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==
dependencies:
"@babel/helper-string-parser" "^7.27.1"
"@babel/helper-validator-identifier" "^7.28.5"
"@emotion/babel-plugin@^11.13.5":
version "11.13.5"
resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz#eab8d65dbded74e0ecfd28dc218e75607c4e7bc0"
integrity sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==
dependencies:
"@babel/helper-module-imports" "^7.16.7"
"@babel/runtime" "^7.18.3"
"@emotion/hash" "^0.9.2"
"@emotion/memoize" "^0.9.0"
"@emotion/serialize" "^1.3.3"
babel-plugin-macros "^3.1.0"
convert-source-map "^1.5.0"
escape-string-regexp "^4.0.0"
find-root "^1.1.0"
source-map "^0.5.7"
stylis "4.2.0"
"@emotion/cache@^11.11.0", "@emotion/cache@^11.13.5", "@emotion/cache@^11.14.0":
version "11.14.0"
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.14.0.tgz#ee44b26986eeb93c8be82bb92f1f7a9b21b2ed76"
integrity sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==
dependencies:
"@emotion/memoize" "^0.9.0"
"@emotion/sheet" "^1.4.0"
"@emotion/utils" "^1.4.2"
"@emotion/weak-memoize" "^0.4.0"
stylis "4.2.0"
"@emotion/css@^11.11.2":
version "11.13.5"
resolved "https://registry.yarnpkg.com/@emotion/css/-/css-11.13.5.tgz#db2d3be6780293640c082848e728a50544b9dfa4"
integrity sha512-wQdD0Xhkn3Qy2VNcIzbLP9MR8TafI0MJb7BEAXKp+w4+XqErksWR4OXomuDzPsN4InLdGhVe6EYcn2ZIUCpB8w==
dependencies:
"@emotion/babel-plugin" "^11.13.5"
"@emotion/cache" "^11.13.5"
"@emotion/serialize" "^1.3.3"
"@emotion/sheet" "^1.4.0"
"@emotion/utils" "^1.4.2"
"@emotion/hash@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413"
integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==
"@emotion/hash@^0.9.2":
version "0.9.2"
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.2.tgz#ff9221b9f58b4dfe61e619a7788734bd63f6898b"
integrity sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==
"@emotion/is-prop-valid@^0.7.3":
version "0.7.3"
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.7.3.tgz#a6bf4fa5387cbba59d44e698a4680f481a8da6cc"
@ -394,6 +526,66 @@
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.1.tgz#e93c13942592cf5ef01aa8297444dc192beee52f"
integrity sha512-Qv4LTqO11jepd5Qmlp3M1YEjBumoTHcHFdgPTQ+sFlIL5myi/7xu/POwP7IRu6odBdmLXdtIs1D6TuW6kbwbbg==
"@emotion/memoize@^0.9.0":
version "0.9.0"
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.9.0.tgz#745969d649977776b43fc7648c556aaa462b4102"
integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==
"@emotion/react@^11.11.4":
version "11.14.0"
resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.14.0.tgz#cfaae35ebc67dd9ef4ea2e9acc6cd29e157dd05d"
integrity sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==
dependencies:
"@babel/runtime" "^7.18.3"
"@emotion/babel-plugin" "^11.13.5"
"@emotion/cache" "^11.14.0"
"@emotion/serialize" "^1.3.3"
"@emotion/use-insertion-effect-with-fallbacks" "^1.2.0"
"@emotion/utils" "^1.4.2"
"@emotion/weak-memoize" "^0.4.0"
hoist-non-react-statics "^3.3.1"
"@emotion/serialize@^1.1.3", "@emotion/serialize@^1.3.3":
version "1.3.3"
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.3.3.tgz#d291531005f17d704d0463a032fe679f376509e8"
integrity sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==
dependencies:
"@emotion/hash" "^0.9.2"
"@emotion/memoize" "^0.9.0"
"@emotion/unitless" "^0.10.0"
"@emotion/utils" "^1.4.2"
csstype "^3.0.2"
"@emotion/sheet@^1.4.0":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.4.0.tgz#c9299c34d248bc26e82563735f78953d2efca83c"
integrity sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==
"@emotion/unitless@^0.10.0":
version "0.10.0"
resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.10.0.tgz#2af2f7c7e5150f497bdabd848ce7b218a27cf745"
integrity sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==
"@emotion/unitless@^0.7.5":
version "0.7.5"
resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed"
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
"@emotion/use-insertion-effect-with-fallbacks@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz#8a8cb77b590e09affb960f4ff1e9a89e532738bf"
integrity sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==
"@emotion/utils@^1.2.1", "@emotion/utils@^1.4.2":
version "1.4.2"
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.4.2.tgz#6df6c45881fcb1c412d6688a311a98b7f59c1b52"
integrity sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==
"@emotion/weak-memoize@^0.4.0":
version "0.4.0"
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz#5e13fac887f08c44f76b0ccaf3370eb00fec9bb6"
integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==
"@esbuild-plugins/node-globals-polyfill@^0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.1.1.tgz#a313ab3efbb2c17c8ce376aa216c627c9b40f9d7"
@ -722,6 +914,15 @@
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"
"@lobehub/icons@^4.0.3":
version "4.0.3"
resolved "https://registry.yarnpkg.com/@lobehub/icons/-/icons-4.0.3.tgz#85e14dd2684eed8324d8a0ed77fe8bd54b199504"
integrity sha512-gEQI4Y2WmceBjE3l+3VEquZJb56NeCQEiBWtNXOcth28tVF0kL5FEtkXdtzpKuf21DQkFnzB1lb5oOgtEaBGMg==
dependencies:
antd-style "^4.1.0"
lucide-react "^0.469.0"
polished "^4.3.1"
"@microsoft/fetch-event-source@^2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@microsoft/fetch-event-source/-/fetch-event-source-2.0.1.tgz#9ceecc94b49fbaa15666e38ae8587f64acce007d"
@ -821,6 +1022,14 @@
resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==
"@rc-component/util@^1.4.0":
version "1.7.0"
resolved "https://registry.yarnpkg.com/@rc-component/util/-/util-1.7.0.tgz#c6eb178e0b1c48c5ae6325b21c60aeaf4f3d8d04"
integrity sha512-tIvIGj4Vl6fsZFvWSkYw9sAfiCKUXMyhVz6kpKyZbwyZyRPqv2vxYZROdaO1VB4gqTNvUZFXh6i3APUiterw5g==
dependencies:
is-mobile "^5.0.0"
react-is "^18.2.0"
"@remix-run/router@1.18.0":
version "1.18.0"
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.18.0.tgz#20b033d1f542a100c1d57cfd18ecf442d1784732"
@ -966,6 +1175,11 @@
dependencies:
undici-types "~6.13.0"
"@types/parse-json@^4.0.0":
version "4.0.2"
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239"
integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==
"@types/prop-types@*":
version "15.7.12"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.12.tgz#12bb1e2be27293c1406acb6af1c3f3a1481d98c6"
@ -1073,6 +1287,20 @@ ansi-styles@^6.1.0:
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
antd-style@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/antd-style/-/antd-style-4.1.0.tgz#f2a7955946b87c3368e10de18edd5452d8b3058a"
integrity sha512-vnPBGg0OVlSz90KRYZhxd89aZiOImTiesF+9MQqN8jsLGZUQTjbP04X9jTdEfsztKUuMbBWg/RmB/wHTakbtMQ==
dependencies:
"@ant-design/cssinjs" "^2.0.0"
"@babel/runtime" "^7.24.1"
"@emotion/cache" "^11.11.0"
"@emotion/css" "^11.11.2"
"@emotion/react" "^11.11.4"
"@emotion/serialize" "^1.1.3"
"@emotion/utils" "^1.2.1"
use-merge-value "^1.2.0"
any-promise@^1.0.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
@ -1225,6 +1453,15 @@ available-typed-arrays@^1.0.7:
dependencies:
possible-typed-array-names "^1.0.0"
babel-plugin-macros@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1"
integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==
dependencies:
"@babel/runtime" "^7.12.5"
cosmiconfig "^7.0.0"
resolve "^1.19.0"
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
@ -1414,7 +1651,7 @@ clsx@^1.1.1:
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12"
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
clsx@^2.0.0:
clsx@^2.0.0, clsx@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999"
integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
@ -1453,11 +1690,27 @@ concat-map@0.0.1:
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
convert-source-map@^1.5.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f"
integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==
convert-source-map@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
cosmiconfig@^7.0.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6"
integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==
dependencies:
"@types/parse-json" "^4.0.0"
import-fresh "^3.2.1"
parse-json "^5.0.0"
path-type "^4.0.0"
yaml "^1.10.0"
cross-env@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf"
@ -1524,6 +1777,11 @@ csstype@^3.0.2:
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
csstype@^3.1.3:
version "3.2.3"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a"
integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==
"d3-array@2 - 3", "d3-array@2.10.0 - 3", d3-array@^3.1.6:
version "3.2.4"
resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.4.tgz#15fec33b237f97ac5d7c986dc77da273a8ed0bb5"
@ -1763,6 +2021,13 @@ entities@~3.0.1:
resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4"
integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==
error-ex@^1.3.1:
version "1.3.4"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.4.tgz#b3a8d8bb6f92eecc1629e3e27d3c8607a8a32414"
integrity sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==
dependencies:
is-arrayish "^0.2.1"
es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.2, es-abstract@^1.23.3:
version "1.23.3"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0"
@ -2246,6 +2511,11 @@ fill-range@^7.1.1:
dependencies:
to-regex-range "^5.0.1"
find-root@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==
find-up@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
@ -2597,7 +2867,7 @@ highlight.js@^11.9.0:
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.10.0.tgz#6e3600dc4b33d6dc23d5bd94fbf72405f5892b92"
integrity sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==
hoist-non-react-statics@^3.2.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
hoist-non-react-statics@^3.2.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
@ -2701,6 +2971,11 @@ is-array-buffer@^3.0.5:
call-bound "^1.0.3"
get-intrinsic "^1.2.6"
is-arrayish@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==
is-async-function@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.0.0.tgz#8e4418efd3e5d3a6ebb0164c05ef5afb69aa9646"
@ -2757,6 +3032,13 @@ is-core-module@^2.13.0:
dependencies:
hasown "^2.0.2"
is-core-module@^2.16.1:
version "2.16.1"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4"
integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==
dependencies:
hasown "^2.0.2"
is-data-view@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f"
@ -2834,6 +3116,11 @@ is-map@^2.0.3:
resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e"
integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==
is-mobile@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/is-mobile/-/is-mobile-5.0.0.tgz#1e08a0ef2c38a67bff84a52af68d67bcef445333"
integrity sha512-Tz/yndySvLAEXh+Uk8liFCxOwVH6YutuR74utvOcu7I9Di+DwM0mtdPVZNaVvvBUM2OXxne/NhOs1zAO7riusQ==
is-negative-zero@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747"
@ -3043,6 +3330,11 @@ json-buffer@3.0.1:
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
json-parse-even-better-errors@^2.3.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
json-schema-traverse@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
@ -3292,6 +3584,11 @@ lru-cache@^5.1.1:
dependencies:
yallist "^3.0.2"
lucide-react@^0.469.0:
version "0.469.0"
resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.469.0.tgz#f16936ca6521482fef754a7eabb310e6c68e1482"
integrity sha512-28vvUnnKQ/dBwiCQtwJw7QauYnE7yd2Cyp4tTTJpvglX4EMpbflcdBgrgToX2j71B3YvugK/NH3BGUk+E/p/Fw==
markdown-it@^13.0.1:
version "13.0.2"
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-13.0.2.tgz#1bc22e23379a6952e5d56217fbed881e0c94d536"
@ -3573,6 +3870,16 @@ parent-module@^1.0.0:
dependencies:
callsites "^3.0.0"
parse-json@^5.0.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
dependencies:
"@babel/code-frame" "^7.0.0"
error-ex "^1.3.1"
json-parse-even-better-errors "^2.3.0"
lines-and-columns "^1.1.6"
path-exists@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
@ -3596,6 +3903,11 @@ path-scurry@^1.11.1:
lru-cache "^10.2.0"
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
path-type@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
picocolors@^1.0.0, picocolors@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1"
@ -3638,6 +3950,13 @@ pluralize@^8.0.0:
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1"
integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==
polished@^4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/polished/-/polished-4.3.1.tgz#5a00ae32715609f83d89f6f31d0f0261c6170548"
integrity sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==
dependencies:
"@babel/runtime" "^7.17.8"
possible-typed-array-names@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f"
@ -3848,6 +4167,11 @@ react-is@^17.0.2:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
react-is@^18.2.0:
version "18.3.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e"
integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==
react-jss@^10.9.2:
version "10.10.0"
resolved "https://registry.yarnpkg.com/react-jss/-/react-jss-10.10.0.tgz#d08ab3257b0eed01e15d6d8275840055c279b0da"
@ -4067,6 +4391,15 @@ resolve@^1.1.7, resolve@^1.22.2:
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
resolve@^1.19.0:
version "1.22.11"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.11.tgz#aad857ce1ffb8bfa9b0b1ac29f1156383f68c262"
integrity sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==
dependencies:
is-core-module "^2.16.1"
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
resolve@^2.0.0-next.5:
version "2.0.0-next.5"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c"
@ -4272,6 +4605,11 @@ source-map-js@^1.2.0:
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af"
integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==
source-map@^0.5.7:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==
source-map@^0.7.4:
version "0.7.4"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
@ -4421,6 +4759,16 @@ strip-json-comments@^3.1.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
stylis@4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51"
integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==
stylis@^4.3.4:
version "4.3.6"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.6.tgz#7c7b97191cb4f195f03ecab7d52f7902ed378320"
integrity sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==
sucrase@^3.32.0:
version "3.35.0"
resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.0.tgz#57f17a3d7e19b36d8995f06679d121be914ae263"
@ -4876,6 +5224,11 @@ use-memo-one@^1.1.1:
resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.3.tgz#2fd2e43a2169eabc7496960ace8c79efef975e99"
integrity sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==
use-merge-value@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/use-merge-value/-/use-merge-value-1.2.0.tgz#45410846c23e490f404c9cbd17d67db9c8c0efcd"
integrity sha512-DXgG0kkgJN45TcyoXL49vJnn55LehnrmoHc7MbKi+QDBvr8dsesqws8UlyIWGHMR+JXgxc1nvY+jDGMlycsUcw==
util-deprecate@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
@ -5058,6 +5411,11 @@ yallist@^3.0.2:
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
yaml@^1.10.0:
version "1.10.2"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
yaml@^2.3.4:
version "2.4.5"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.5.tgz#60630b206dd6d84df97003d33fc1ddf6296cca5e"

View File

@ -253,11 +253,29 @@ function parseDockerModelRunnerEndpoint(basePath = null, to = "openai") {
* @property {string} config?.gguf['*.context_length'] - The context length of the model. will be something like qwen3.context_length
*/
function filterByTask(task = "chat", models = {}) {
const possibleEmbed = [{ pattern: /^all-mini/i }, { pattern: /embed/i }];
const isEmbedModel = (strTag) =>
possibleEmbed.some((p) => p.pattern.test(strTag));
const filteredModels = {};
for (const [modelName, tags] of Object.entries(models)) {
if (task === "chat") {
if (isEmbedModel(modelName)) continue;
filteredModels[modelName] = tags;
} else if (task === "embedding") {
if (!isEmbedModel(modelName)) continue;
filteredModels[modelName] = tags;
}
}
return filteredModels;
}
/**
* Fetch the remote models from the Docker Hub and cache the results.
* @param {'chat' | 'embedding'} task - The task to fetch the models for.
* @returns {Promise<Record<string, {id: string, name: string, size: string, organization: string}[]>>}
*/
async function fetchRemoteModels() {
async function fetchRemoteModels(task = "chat") {
const cachePath = path.resolve(
DockerModelRunnerLLM.cacheFolder,
"models.json"
@ -271,7 +289,10 @@ async function fetchRemoteModels() {
if (fs.existsSync(cachePath) && fs.existsSync(cachedAtPath)) {
cacheTime = Number(fs.readFileSync(cachedAtPath, "utf8"));
if (Date.now() - cacheTime < DockerModelRunnerLLM.cacheTime)
return safeJsonParse(fs.readFileSync(cachePath, "utf8"));
return filterByTask(
task,
safeJsonParse(fs.readFileSync(cachePath, "utf8"))
);
}
DockerModelRunnerLLM.slog(`Refreshing remote models from Docker Hub`);
@ -367,15 +388,16 @@ async function fetchRemoteModels() {
fs.writeFileSync(cachedAtPath, String(Number(new Date())), {
encoding: "utf8",
});
return availableRemoteModels;
return filterByTask(task, availableRemoteModels);
}
/**
* This function will fetch the remote models from the Docker Hub as well
* as the local models installed on the system.
* @param {string} basePath - The base path of the Docker Model Runner endpoint.
* @param {'chat' | 'embedding'} task - The task to fetch the models for.
*/
async function getDockerModels(basePath = null) {
async function getDockerModels(basePath = null, task = "chat") {
let availableModels = {};
/** @type {Array<DockerRunnerInstalledModel>} */
let installedModels = {};
@ -393,7 +415,7 @@ async function getDockerModels(basePath = null) {
await fetch(dmrUrl.toString())
.then((res) => res.json())
.then((data) => {
data?.map((model) => {
data?.forEach((model) => {
const id = model.tags.at(0);
// eg: ai/qwen3:latest -> qwen3
const tag =
@ -411,7 +433,7 @@ async function getDockerModels(basePath = null) {
});
// Now hit the Docker Hub API to get the remote model namespace and root tags
const remoteModels = await fetchRemoteModels();
const remoteModels = await fetchRemoteModels(task);
for (const [modelName, tags] of Object.entries(remoteModels)) {
availableModels[modelName] = { tags: [] };
for (const tag of tags) {