fix(webui): polish responsive layout

This commit is contained in:
chengyongru
2026-07-23 18:22:02 +08:00
committed by chengyongru
parent 4b1547db7d
commit 6c0f151f6e
20 changed files with 372 additions and 80 deletions
+71 -3
View File
@@ -226,6 +226,76 @@ const HERO_GREETING_KEYS = [
"thread.empty.greetings.tackle",
] as const;
function HeroGreeting({ text }: { text: string }) {
const containerRef = useRef<HTMLDivElement>(null);
const headingRef = useRef<HTMLHeadingElement>(null);
useLayoutEffect(() => {
const container = containerRef.current;
const heading = headingRef.current;
if (!container || !heading) return;
const fitToWidth = () => {
heading.style.removeProperty("font-size");
const availableWidth = container.clientWidth;
if (availableWidth <= 0) return;
const naturalWidth = heading.scrollWidth;
const maximumFontSize = Number.parseFloat(window.getComputedStyle(heading).fontSize);
if (
naturalWidth <= availableWidth
|| !Number.isFinite(maximumFontSize)
|| maximumFontSize <= 0
) {
return;
}
const fittedFontSize = Math.max(
12,
Math.floor(maximumFontSize * ((availableWidth - 2) / naturalWidth) * 100) / 100,
);
heading.style.fontSize = `${fittedFontSize}px`;
};
fitToWidth();
let lastObservedWidth = container.clientWidth;
const resizeObserver = typeof ResizeObserver === "undefined"
? null
: new ResizeObserver(([entry]) => {
const nextWidth = entry?.contentRect.width ?? container.clientWidth;
if (nextWidth === lastObservedWidth) return;
lastObservedWidth = nextWidth;
fitToWidth();
});
resizeObserver?.observe(container);
window.addEventListener("resize", fitToWidth);
let cancelled = false;
void document.fonts?.ready.then(() => {
if (!cancelled) fitToWidth();
});
return () => {
cancelled = true;
resizeObserver?.disconnect();
window.removeEventListener("resize", fitToWidth);
};
}, [text]);
return (
<div ref={containerRef} className="min-w-0 w-full max-w-[44rem]">
<h1
ref={headingRef}
data-testid="hero-greeting"
className="whitespace-nowrap text-[34px] font-normal leading-[1.08] tracking-normal text-foreground sm:text-[48px] sm:leading-tight"
>
{text}
</h1>
</div>
);
}
function randomHeroGreetingKey(): (typeof HERO_GREETING_KEYS)[number] {
const index = Math.floor(Math.random() * HERO_GREETING_KEYS.length);
return HERO_GREETING_KEYS[index] ?? HERO_GREETING_KEYS[0];
@@ -890,9 +960,7 @@ export function ThreadShell({
</div>
) : (
<div className="flex w-full flex-col items-center text-center animate-in fade-in-0 slide-in-from-bottom-2 duration-500">
<h1 className="max-w-[44rem] text-balance text-[34px] font-normal leading-[1.08] tracking-normal text-foreground sm:text-[48px] sm:leading-tight">
{t(heroGreetingKey)}
</h1>
<HeroGreeting text={t(heroGreetingKey)} />
</div>
);
const sessionInfoAction = historyKey ? (