Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from "react";
import { useLayoutEffect, useRef, useState } from "react";
import { Card, Button, Tag } from "@agentscope-ai/design";
import { AppstoreOutlined } from "@ant-design/icons";
import type { ProviderInfo } from "../../../../../api/types";
Expand All @@ -23,12 +23,44 @@ export function LocalProviderCard({
}: LocalProviderCardProps) {
const { t } = useTranslation();
const [modelManageOpen, setModelManageOpen] = useState(false);
const titleRef = useRef<HTMLSpanElement | null>(null);
const [isTitleWrapped, setIsTitleWrapped] = useState(false);

useLayoutEffect(() => {
const titleElement = titleRef.current;
if (!titleElement) {
return;
}

const checkWrapped = () => {
const computedStyle = window.getComputedStyle(titleElement);
const lineHeight = Number.parseFloat(computedStyle.lineHeight || "0");
if (!lineHeight) {
setIsTitleWrapped(false);
return;
}
const titleHeight = titleElement.getBoundingClientRect().height;
setIsTitleWrapped(titleHeight > lineHeight * 1.4);
};

checkWrapped();
Comment thread
EuanTop marked this conversation as resolved.
const resizeObserver = new ResizeObserver(checkWrapped);
resizeObserver.observe(titleElement);
return () => {
resizeObserver.disconnect();
};
}, [provider.name]);

const totalCount = provider.models.length + provider.extra_models.length;
const statusReady = totalCount > 0;
const statusLabel = statusReady
? t("models.available")
: t("models.unavailable");
const titleWords = provider.name.trim().split(/\s+/).filter(Boolean);
const wrappedTitleLine1 =
titleWords.length > 1 ? titleWords.slice(0, -1).join(" ") : provider.name;
const wrappedTitleLine2 =
titleWords.length > 1 ? titleWords[titleWords.length - 1] : provider.name;
Comment thread
EuanTop marked this conversation as resolved.
Outdated
Comment thread
EuanTop marked this conversation as resolved.
Outdated

return (
<Card
Expand All @@ -39,32 +71,65 @@ export function LocalProviderCard({
statusReady ? styles.enabledCard : ""
} ${isHover ? styles.hover : styles.normal}`}
>
<div style={{ marginBottom: 16 }}>
<div style={{ marginBottom: 16, paddingTop: 4 }}>
<div className={styles.cardHeader}>
<span className={styles.cardName}>
{provider.name}
<Tag color="purple" style={{ marginLeft: 8, fontSize: 11 }}>
{t("models.local")}
</Tag>
</span>
<div
className={`${styles.cardName} ${
isTitleWrapped ? styles.cardNameWrapped : ""
}`}
>
<span ref={titleRef} className={styles.cardNameMeasure}>
{provider.name}
</span>
{isTitleWrapped ? (
<>
<span className={styles.cardNameLine1} title={provider.name}>
{wrappedTitleLine1}
</span>
<span className={styles.cardNameLine2Wrap}>
<span className={styles.cardNameLine2} title={provider.name}>
{wrappedTitleLine2}
</span>
<span className={styles.cardTagRow}>
<Tag color="purple" style={{ fontSize: 11 }}>
{t("models.local")}
</Tag>
</span>
</span>
</>
) : (
<>
<span className={styles.cardNameText}>{provider.name}</span>
<span className={styles.cardTagRow}>
<Tag color="purple" style={{ fontSize: 11 }}>
{t("models.local")}
</Tag>
</span>
</>
)}
</div>
<div className={styles.statusContainer}>
<span
style={{
width: 8,
height: 8,
borderRadius: "50%",
backgroundColor: statusReady ? "#52c41a" : "#d9d9d9",
boxShadow: statusReady
? "0 0 0 2px rgba(82, 196, 26, 0.2)"
: "none",
}}
/>
<span
className={`${styles.statusText} ${
statusReady ? styles.enabled : styles.disabled
}`}
>
{statusLabel}
<span className={styles.statusMainRow}>
<span className={styles.statusTextMain}>{statusLabel}</span>
<span
className={styles.statusDot}
style={{
width: 8,
height: 8,
borderRadius: "50%",
backgroundColor: statusReady ? "#52c41a" : "#d9d9d9",
boxShadow: statusReady
? "0 0 0 2px rgba(82, 196, 26, 0.2)"
: "none",
}}
/>
</span>
<span className={styles.statusTextMeta}>{"\u00A0"}</span>
</span>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from "react";
import { useLayoutEffect, useRef, useState } from "react";
import { Card, Button, Tag, Modal, message } from "@agentscope-ai/design";
import {
EditOutlined,
Expand Down Expand Up @@ -32,6 +32,33 @@ export function RemoteProviderCard({
const { t } = useTranslation();
const [modalOpen, setModalOpen] = useState(false);
const [modelManageOpen, setModelManageOpen] = useState(false);
const titleRef = useRef<HTMLSpanElement | null>(null);
const [isTitleWrapped, setIsTitleWrapped] = useState(false);

useLayoutEffect(() => {
const titleElement = titleRef.current;
if (!titleElement) {
return;
}

const checkWrapped = () => {
const computedStyle = window.getComputedStyle(titleElement);
const lineHeight = Number.parseFloat(computedStyle.lineHeight || "0");
if (!lineHeight) {
setIsTitleWrapped(false);
return;
}
const titleHeight = titleElement.getBoundingClientRect().height;
setIsTitleWrapped(titleHeight > lineHeight * 1.4);
};

checkWrapped();
const resizeObserver = new ResizeObserver(checkWrapped);
resizeObserver.observe(titleElement);
Comment thread
EuanTop marked this conversation as resolved.
return () => {
resizeObserver.disconnect();
};
}, [provider.name]);

const handleDeleteProvider = (e: React.MouseEvent) => {
e.stopPropagation();
Expand Down Expand Up @@ -75,11 +102,11 @@ export function RemoteProviderCard({
const isAvailable = isConfigured && hasModels;

const providerTag = provider.is_custom ? (
<Tag color="blue" style={{ marginLeft: 8, fontSize: 11 }}>
<Tag color="blue" style={{ fontSize: 11 }}>
{t("models.custom")}
</Tag>
) : (
<Tag color="green" style={{ marginLeft: 8, fontSize: 11 }}>
<Tag color="green" style={{ fontSize: 11 }}>
{t("models.builtin")}
</Tag>
);
Expand All @@ -105,6 +132,16 @@ export function RemoteProviderCard({
? "0 0 0 2px rgba(250, 173, 20, 0.2)"
: "none";

const titleWords = provider.name.trim().split(/\s+/).filter(Boolean);
const wrappedTitleLine1 =
titleWords.length > 1 ? titleWords.slice(0, -1).join(" ") : provider.name;
const wrappedTitleLine2 =
titleWords.length > 1 ? titleWords[titleWords.length - 1] : "";

Comment thread
EuanTop marked this conversation as resolved.
const statusLabelMatch = statusLabel.match(/^(.+?)\s*[((](.+)[))]\s*$/);
const statusMainText = statusLabelMatch?.[1]?.trim() ?? statusLabel;
const statusMetaText = statusLabelMatch?.[2]?.trim();
Comment thread
EuanTop marked this conversation as resolved.
Outdated

return (
<Card
hoverable
Expand All @@ -114,22 +151,36 @@ export function RemoteProviderCard({
isAvailable ? styles.enabledCard : ""
} ${isHover ? styles.hover : styles.normal}`}
>
<div style={{ marginBottom: 16 }}>
<div style={{ marginBottom: 16, paddingTop: 4 }}>
<div className={styles.cardHeader}>
<span className={styles.cardName}>
{provider.name}
{providerTag}
</span>
<div
className={`${styles.cardName} ${
isTitleWrapped ? styles.cardNameWrapped : ""
}`}
>
<span ref={titleRef} className={styles.cardNameMeasure}>
{provider.name}
</span>
{isTitleWrapped ? (
<>
<span className={styles.cardNameLine1} title={provider.name}>
{wrappedTitleLine1}
</span>
<span className={styles.cardNameLine2Wrap}>
<span className={styles.cardNameLine2} title={provider.name}>
{wrappedTitleLine2}
</span>
<span className={styles.cardTagRow}>{providerTag}</span>
</span>
</>
) : (
<>
<span className={styles.cardNameText}>{provider.name}</span>
<span className={styles.cardTagRow}>{providerTag}</span>
</>
)}
</div>
<div className={styles.statusContainer}>
<span
style={{
width: 8,
height: 8,
borderRadius: "50%",
backgroundColor: statusDotColor,
boxShadow: statusDotShadow,
}}
/>
<span
className={`${styles.statusText} ${
statusType === "enabled"
Expand All @@ -139,7 +190,22 @@ export function RemoteProviderCard({
: styles.disabled
}`}
>
{statusLabel}
<span className={styles.statusMainRow}>
<span className={styles.statusTextMain}>{statusMainText}</span>
<span
className={styles.statusDot}
style={{
width: 8,
height: 8,
borderRadius: "50%",
backgroundColor: statusDotColor,
boxShadow: statusDotShadow,
}}
/>
</span>
<span className={styles.statusTextMeta}>
{statusMetaText ? `(${statusMetaText})` : "\u00A0"}
</span>
</span>
</div>
</div>
Expand Down Expand Up @@ -205,9 +271,13 @@ export function RemoteProviderCard({
size="small"
danger
onClick={handleDeleteProvider}
className={styles.configBtn}
icon={<DeleteOutlined />}
>
{t("models.deleteProvider")}
<span className={styles.deleteTextLong}>
{t("models.deleteProvider")}
</span>
<span className={styles.deleteTextShort}>{t("common.delete")}</span>
Comment thread
EuanTop marked this conversation as resolved.
</Button>
)}
</div>
Expand Down
Loading
Loading