fixes
This commit is contained in:
@@ -25,11 +25,11 @@ function MessageBubble({ msg, index, authorAvatar, authorName, onReport, isFaile
|
||||
window.open(`https://duckduckgo.com/?q=${encodeURIComponent(msg.content.slice(0, 400))}`, "_blank", "noopener,noreferrer");
|
||||
}
|
||||
return (
|
||||
<div className={cn("flex gap-3", isUser && "flex-row-reverse")}>
|
||||
<div className={cn("flex gap-2 sm:gap-3", isUser && "flex-row-reverse")}>
|
||||
{isUser
|
||||
? <Avatar name={authorName} url={authorAvatar} size={32} />
|
||||
: <AssistantAvatar size={32} />}
|
||||
<div className={cn("min-w-0 max-w-[85%]", isUser && "flex flex-col items-end")}>
|
||||
<div className={cn("min-w-0 max-w-[88%] sm:max-w-[85%]", isUser && "flex flex-col items-end")}>
|
||||
<div className="mb-1 flex items-center gap-1.5 text-xs opacity-60">
|
||||
{isUser ? <UserIcon size={12} /> : <Bot size={12} />}
|
||||
{isUser ? t("chat.you") : "KisAssistant"}
|
||||
@@ -39,7 +39,7 @@ function MessageBubble({ msg, index, authorAvatar, authorName, onReport, isFaile
|
||||
<UserMessageContent content={msg.content} />
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-3xl rounded-tl-lg border border-stone-200/70 bg-white px-5 py-3.5 shadow-sm dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<div className="rounded-3xl rounded-tl-lg border border-stone-200/70 bg-white px-4 py-3 shadow-sm sm:px-5 sm:py-3.5 dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<Markdown text={msg.content} />
|
||||
</div>
|
||||
)}
|
||||
@@ -313,8 +313,8 @@ export function Chat({
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div ref={boxRef} className="flex-1 space-y-5 overflow-y-auto px-4 py-6 sm:px-8">
|
||||
<div className="mx-auto w-full max-w-3xl space-y-5">
|
||||
<div ref={boxRef} className="flex-1 space-y-4 overflow-y-auto px-4 py-4 sm:space-y-5 sm:px-8 sm:py-6">
|
||||
<div className="mx-auto w-full max-w-3xl space-y-4 sm:space-y-5">
|
||||
{messages.length === 0 && !streaming && (
|
||||
<div className="py-16 text-center">
|
||||
<FlowerMark size={56} dynamic={false} className="mx-auto mb-4" />
|
||||
@@ -326,11 +326,11 @@ export function Chat({
|
||||
<MessageBubble key={i} msg={m} index={i} authorAvatar={user.avatarUrl} authorName={user.displayName} onReport={readOnly ? undefined : setReportIndex} isFailed={failedIndex === i} retryCount={retryCount} maxRetries={maxRetries} retryDisabled={sending} onRetry={failedIndex === i && retryCount < maxRetries ? onRetry : undefined} />
|
||||
))}
|
||||
{streaming !== "" && (
|
||||
<div className="flex gap-3">
|
||||
<div className="flex gap-2 sm:gap-3">
|
||||
<AssistantAvatar size={32} />
|
||||
<div className="min-w-0 max-w-[85%]">
|
||||
<div className="min-w-0 max-w-[88%] sm:max-w-[85%]">
|
||||
<div className="mb-1 text-xs opacity-60">KisAssistant</div>
|
||||
<div className="rounded-3xl rounded-tl-lg border border-stone-200/70 bg-white px-5 py-3.5 shadow-sm dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<div className="rounded-3xl rounded-tl-lg border border-stone-200/70 bg-white px-4 py-3 shadow-sm sm:px-5 sm:py-3.5 dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<div className="typing-cursor"><Markdown text={streaming} /></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -36,6 +36,15 @@ export function Markdown({ text }: { text: string }) {
|
||||
code({ children }) {
|
||||
return <code>{children}</code>;
|
||||
},
|
||||
table({ children }) {
|
||||
// Wide tables would blow the bubble off-screen on mobile —
|
||||
// scroll them inside their own pane instead.
|
||||
return (
|
||||
<div className="overflow-x-auto pb-1">
|
||||
<table>{children}</table>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
|
||||
@@ -402,6 +402,11 @@ export function SettingsModal({
|
||||
<input
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
// The search field lives inside the settings <form>: Enter must
|
||||
// only filter, never submit (save + close the modal).
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") e.preventDefault();
|
||||
}}
|
||||
placeholder={t("settings.searchPh")}
|
||||
aria-label={t("settings.searchAria")}
|
||||
className="w-full rounded-2xl border border-stone-200/70 bg-stone-100 py-2 pl-9 pr-8 text-sm outline-none transition placeholder:text-stone-400 focus:border-accent-500/60 focus:bg-white dark:border-zinc-800 dark:bg-zinc-800/60 dark:placeholder:text-zinc-500 dark:focus:bg-zinc-900"
|
||||
@@ -419,7 +424,16 @@ export function SettingsModal({
|
||||
</div>
|
||||
|
||||
{search.trim() && visibleCats.length === 0 ? (
|
||||
<p className="px-2 py-4 text-center text-sm opacity-50">{t("settings.noMatch", { q: search.trim() })}</p>
|
||||
<div className="space-y-2 px-2 py-4 text-center">
|
||||
<p className="text-sm opacity-50">{t("settings.noMatch", { q: search.trim() })}</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setSearch("")}
|
||||
className="rounded-full border border-stone-200 bg-white px-3.5 py-1.5 text-sm transition hover:bg-stone-50 dark:border-zinc-700 dark:bg-zinc-900 dark:hover:bg-zinc-800"
|
||||
>
|
||||
{t("settings.clearSearch")}
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col gap-4 sm:flex-row">
|
||||
<nav aria-label="Settings categories" className="flex shrink-0 gap-1.5 overflow-x-auto sm:w-44 sm:flex-col">
|
||||
@@ -427,7 +441,10 @@ export function SettingsModal({
|
||||
<button
|
||||
key={c.id}
|
||||
type="button"
|
||||
onClick={() => setCat(c.id)}
|
||||
// Picking a category exits search mode: keeping the query
|
||||
// would leave the nav filtered (often down to this single
|
||||
// button, or empty) and trap the user in the search view.
|
||||
onClick={() => { setCat(c.id); setSearch(""); }}
|
||||
aria-current={activeCat === c.id}
|
||||
className={cn(
|
||||
"flex shrink-0 items-center gap-2.5 rounded-2xl px-3.5 py-2.5 text-sm transition",
|
||||
|
||||
@@ -465,7 +465,7 @@ const GRADIENTS = [
|
||||
export function Avatar({ name, url, size = 36 }: { name: string; url?: string; size?: number }) {
|
||||
const cdnV = useCdnVersion();
|
||||
if (url) {
|
||||
return <img src={withCdnVersion(url, cdnV)} alt={name} width={size} height={size} className="rounded-full object-cover" style={{ width: size, height: size }} />;
|
||||
return <img src={withCdnVersion(url, cdnV)} alt={name} width={size} height={size} className="shrink-0 rounded-full object-cover" style={{ width: size, height: size }} />;
|
||||
}
|
||||
const initial = (name.trim()[0] || "?").toUpperCase();
|
||||
const g = GRADIENTS[(name.charCodeAt(0) || 0) % GRADIENTS.length];
|
||||
|
||||
Reference in New Issue
Block a user