This commit is contained in:
k
2026-09-21 22:53:25 +02:00
parent 9dd8e772a9
commit aaf9354b7f
7 changed files with 52 additions and 96 deletions
+3
View File
@@ -634,6 +634,7 @@ export function App() {
onClose={() => setEditConv(null)}
onSaved={(c) => {
setConvs((prev) => prev.map((x) => (x.id === c.id ? c : x)));
pushToast(t("toast.convUpdated", { title: c.title }), { icon: "edit" });
}}
/>
<ShareModal conv={shareConv} onClose={() => setShareConv(null)} />
@@ -650,6 +651,8 @@ export function App() {
onSaved={(u) => {
if (u.theme !== user?.theme) {
pushToast(t("toast.themeChanged"), { icon: "theme", tag: "theme" });
} else {
pushToast(t("toast.settingsSaved"), { icon: "check" });
}
setUser(u);
applyTheme(u.theme);
+3 -1
View File
@@ -1,5 +1,5 @@
import { useEffect, useState } from "react";
import { Archive, ArchiveRestore, Check, Cpu, Palette, SunMoon, Trash2, type LucideIcon } from "lucide-react";
import { Archive, ArchiveRestore, Check, Cpu, Palette, Pencil, SunMoon, Trash2, Unplug, type LucideIcon } from "lucide-react";
import { cn } from "../lib/cn.ts";
import { removeToast, TOAST_DISPLAY_MS, useToasts, type Toast, type ToastIcon } from "../lib/toasts.ts";
@@ -10,6 +10,8 @@ const ICONS: Record<ToastIcon, LucideIcon> = {
model: Cpu,
theme: SunMoon,
accent: Palette,
unshare: Unplug,
edit: Pencil,
check: Check,
};
+1
View File
@@ -229,6 +229,7 @@ export function ShareModal({ conv, onClose }: { conv: Conversation | null; onClo
async function unshare() {
if (!conv) return;
await api.unshare(conv.id);
pushToast(t("toast.unshared"), { icon: "unshare" });
onClose();
}
+23 -27
View File
@@ -536,30 +536,30 @@ export function FlowerMark({ size, className, dynamic = true }: { size?: number;
</radialGradient>
</defs>
{outer.map((p, i) => (
<ellipse
key={p.r}
cx="50"
cy="27"
rx="13.5"
ry={p.ry}
fill={`url(#${petalId})`}
transform={`rotate(${p.r} 50 50)`}
className={dynamic ? "flower-petal" : undefined}
style={dynamic ? { animationDelay: `${i * 0.2}s` } : undefined}
/>
<g key={p.r} transform={`rotate(${p.r} 50 50)`}>
<ellipse
cx="50"
cy="27"
rx="13.5"
ry={p.ry}
fill={`url(#${petalId})`}
className={dynamic ? "flower-petal" : undefined}
style={dynamic ? { animationDelay: `${i * 0.2}s` } : undefined}
/>
</g>
))}
{inner.map((p, i) => (
<ellipse
key={p.r}
cx="50"
cy="34"
rx="8"
ry={p.ry}
fill={`url(#${petalInId})`}
transform={`rotate(${p.r} 50 50)`}
className={dynamic ? "flower-petal-inner" : undefined}
style={dynamic ? { animationDelay: `${i * 0.2 + 0.6}s` } : undefined}
/>
<g key={p.r} transform={`rotate(${p.r} 50 50)`}>
<ellipse
cx="50"
cy="34"
rx="8"
ry={p.ry}
fill={`url(#${petalInId})`}
className={dynamic ? "flower-petal-inner" : undefined}
style={dynamic ? { animationDelay: `${i * 0.2 + 0.6}s` } : undefined}
/>
</g>
))}
<g className={dynamic ? "flower-core" : undefined}>
<circle cx="50" cy="50" r="10.5" fill="#f59e0b" opacity="0.22" />
@@ -659,13 +659,9 @@ export function LoadingScreen() {
return (
<div className="loading-enter grid min-h-full place-items-center" role="status" aria-label="Loading KisAssistant">
<div className="flex flex-col items-center gap-4 px-6 text-center">
<span className="loader-halo">
<span className="loader-orbit" aria-hidden="true" />
<FlowerMark className="h-16 w-16 sm:h-20 sm:w-20" />
</span>
<FlowerMark className="h-16 w-16 sm:h-20 sm:w-20" />
<p className="loader-title font-serif text-2xl font-bold tracking-tight">KisAssistant</p>
<p className="loading-dots text-sm opacity-60">{t("loading.tagline")}</p>
<span className="loader-bar" aria-hidden="true"><span /></span>
</div>
</div>
);
+2 -68
View File
@@ -126,48 +126,6 @@ body {
animation: loading-in 0.45s ease-out both;
}
/* Loader halo: expanding accent glow + revolving dashed orbit with a dot. */
.loader-halo {
position: relative;
display: grid;
place-items: center;
padding: 20px;
}
.loader-halo::before {
content: "";
position: absolute;
inset: 0;
border-radius: 9999px;
background: radial-gradient(circle, rgb(var(--ka-accent-500) / 0.28) 0%, transparent 65%);
animation: halo-ping 2.6s ease-out infinite;
}
@keyframes halo-ping {
0% { transform: scale(0.72); opacity: 0.6; }
70% { transform: scale(1.14); opacity: 0; }
100% { transform: scale(1.14); opacity: 0; }
}
.loader-orbit {
position: absolute;
inset: 8px;
border-radius: 9999px;
border: 1.5px dashed rgb(var(--ka-accent-500) / 0.45);
animation: orbit-spin 5s linear infinite;
}
.loader-orbit::after {
content: "";
position: absolute;
top: -3.5px;
left: 50%;
width: 7px;
height: 7px;
margin-left: -3.5px;
border-radius: 9999px;
background: rgb(var(--ka-accent-500));
box-shadow: 0 0 8px rgb(var(--ka-accent-500));
}
@keyframes orbit-spin {
to { transform: rotate(1turn); }
}
/* Loader title: soft accent glow pulse. */
.loader-title {
animation: title-glow 2.6s ease-in-out infinite;
@@ -176,30 +134,6 @@ body {
0%, 100% { opacity: 0.92; }
50% { opacity: 1; text-shadow: 0 0 18px rgb(var(--ka-accent-500) / 0.35); }
}
/* Indeterminate shimmer bar. */
.loader-bar {
display: block;
width: 11rem;
height: 3px;
border-radius: 9999px;
background: rgb(0 0 0 / 0.1);
overflow: hidden;
}
.dark .loader-bar {
background: rgb(255 255 255 / 0.12);
}
.loader-bar > span {
display: block;
height: 100%;
width: 35%;
border-radius: inherit;
background: linear-gradient(90deg, rgb(var(--ka-accent-400)), rgb(var(--ka-accent-600)));
animation: bar-slide 1.5s ease-in-out infinite;
}
@keyframes bar-slide {
0% { transform: translateX(-110%); }
100% { transform: translateX(320%); }
}
.loading-dots::after {
content: "";
@@ -229,8 +163,8 @@ body {
}
@media (prefers-reduced-motion: reduce) {
.flower-petal, .flower-core, .loading-dots::after, .loading-enter, .typing-cursor::after,
.toast-enter, .toast-exit {
.flower-petal, .flower-petal-inner, .flower-core, .loading-dots::after, .loading-enter, .typing-cursor::after,
.loader-title {
animation: none;
}
}
+18
View File
@@ -100,6 +100,9 @@ const en = {
"toast.modelChanged": "Model: {label}",
"toast.themeChanged": "Theme changed",
"toast.accentChanged": "Accent color changed",
"toast.unshared": "Share link removed",
"toast.settingsSaved": "Settings saved",
"toast.convUpdated": "Conversation “{title}” updated",
"chat.you": "You",
"chat.greet": "How can I help, {name}?",
"chat.greetSub": "Pick a model, then type below. Markdown is supported.",
@@ -647,6 +650,9 @@ const es: Dict = {
"toast.modelChanged": "Modelo: {label}",
"toast.themeChanged": "Tema cambiado",
"toast.accentChanged": "Color de acento cambiado",
"toast.unshared": "Enlace compartido eliminado",
"toast.settingsSaved": "Ajustes guardados",
"toast.convUpdated": "Conversación “{title}” actualizada",
"chat.you": "Tú",
"chat.greet": "¿En qué te ayudo, {name}?",
"chat.greetSub": "Elige un modelo y escribe abajo. Markdown soportado.",
@@ -1184,6 +1190,9 @@ const fr: Dict = {
"toast.modelChanged": "Modèle : {label}",
"toast.themeChanged": "Thème modifié",
"toast.accentChanged": "Couleur d’accent modifiée",
"toast.unshared": "Lien de partage retiré",
"toast.settingsSaved": "Réglages enregistrés",
"toast.convUpdated": "Conversation “{title}” mise à jour",
"chat.you": "Vous",
"chat.greet": "Comment puis-je aider, {name} ?",
"chat.greetSub":
@@ -1721,6 +1730,9 @@ const ru: Dict = {
"toast.modelChanged": "Модель: {label}",
"toast.themeChanged": "Тема изменена",
"toast.accentChanged": "Акцентный цвет изменён",
"toast.unshared": "Публичная ссылка удалена",
"toast.settingsSaved": "Настройки сохранены",
"toast.convUpdated": "Чат “{title}” обновлён",
"chat.you": "Вы",
"chat.greet": "Чем помочь, {name}?",
"chat.greetSub": "Выберите модель и пишите ниже. Поддерживается Markdown.",
@@ -2245,6 +2257,9 @@ const it: Dict = {
"toast.modelChanged": "Modello: {label}",
"toast.themeChanged": "Tema cambiato",
"toast.accentChanged": "Colore accento cambiato",
"toast.unshared": "Link condiviso rimosso",
"toast.settingsSaved": "Impostazioni salvate",
"toast.convUpdated": "Conversazione “{title}” aggiornata",
"chat.you": "Tu",
"chat.greet": "Come posso aiutare, {name}?",
"chat.greetSub": "Scegli un modello e scrivi qui sotto. Markdown supportato.",
@@ -2775,6 +2790,9 @@ const pt: Dict = {
"toast.modelChanged": "Modelo: {label}",
"toast.themeChanged": "Tema alterado",
"toast.accentChanged": "Cor de destaque alterada",
"toast.unshared": "Link partilhado removido",
"toast.settingsSaved": "Definições guardadas",
"toast.convUpdated": "Conversa “{title}” atualizada",
"chat.you": "Tu",
"chat.greet": "Como posso ajudar, {name}?",
"chat.greetSub": "Escolhe um modelo e escreve abaixo. Markdown suportado.",
+2
View File
@@ -14,6 +14,8 @@ export type ToastIcon =
| "model"
| "theme"
| "accent"
| "unshare"
| "edit"
| "check";
export interface Toast {