f.
This commit is contained in:
@@ -634,6 +634,7 @@ export function App() {
|
|||||||
onClose={() => setEditConv(null)}
|
onClose={() => setEditConv(null)}
|
||||||
onSaved={(c) => {
|
onSaved={(c) => {
|
||||||
setConvs((prev) => prev.map((x) => (x.id === c.id ? c : x)));
|
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)} />
|
<ShareModal conv={shareConv} onClose={() => setShareConv(null)} />
|
||||||
@@ -650,6 +651,8 @@ export function App() {
|
|||||||
onSaved={(u) => {
|
onSaved={(u) => {
|
||||||
if (u.theme !== user?.theme) {
|
if (u.theme !== user?.theme) {
|
||||||
pushToast(t("toast.themeChanged"), { icon: "theme", tag: "theme" });
|
pushToast(t("toast.themeChanged"), { icon: "theme", tag: "theme" });
|
||||||
|
} else {
|
||||||
|
pushToast(t("toast.settingsSaved"), { icon: "check" });
|
||||||
}
|
}
|
||||||
setUser(u);
|
setUser(u);
|
||||||
applyTheme(u.theme);
|
applyTheme(u.theme);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from "react";
|
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 { cn } from "../lib/cn.ts";
|
||||||
import { removeToast, TOAST_DISPLAY_MS, useToasts, type Toast, type ToastIcon } from "../lib/toasts.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,
|
model: Cpu,
|
||||||
theme: SunMoon,
|
theme: SunMoon,
|
||||||
accent: Palette,
|
accent: Palette,
|
||||||
|
unshare: Unplug,
|
||||||
|
edit: Pencil,
|
||||||
check: Check,
|
check: Check,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -229,6 +229,7 @@ export function ShareModal({ conv, onClose }: { conv: Conversation | null; onClo
|
|||||||
async function unshare() {
|
async function unshare() {
|
||||||
if (!conv) return;
|
if (!conv) return;
|
||||||
await api.unshare(conv.id);
|
await api.unshare(conv.id);
|
||||||
|
pushToast(t("toast.unshared"), { icon: "unshare" });
|
||||||
onClose();
|
onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -536,30 +536,30 @@ export function FlowerMark({ size, className, dynamic = true }: { size?: number;
|
|||||||
</radialGradient>
|
</radialGradient>
|
||||||
</defs>
|
</defs>
|
||||||
{outer.map((p, i) => (
|
{outer.map((p, i) => (
|
||||||
<ellipse
|
<g key={p.r} transform={`rotate(${p.r} 50 50)`}>
|
||||||
key={p.r}
|
<ellipse
|
||||||
cx="50"
|
cx="50"
|
||||||
cy="27"
|
cy="27"
|
||||||
rx="13.5"
|
rx="13.5"
|
||||||
ry={p.ry}
|
ry={p.ry}
|
||||||
fill={`url(#${petalId})`}
|
fill={`url(#${petalId})`}
|
||||||
transform={`rotate(${p.r} 50 50)`}
|
className={dynamic ? "flower-petal" : undefined}
|
||||||
className={dynamic ? "flower-petal" : undefined}
|
style={dynamic ? { animationDelay: `${i * 0.2}s` } : undefined}
|
||||||
style={dynamic ? { animationDelay: `${i * 0.2}s` } : undefined}
|
/>
|
||||||
/>
|
</g>
|
||||||
))}
|
))}
|
||||||
{inner.map((p, i) => (
|
{inner.map((p, i) => (
|
||||||
<ellipse
|
<g key={p.r} transform={`rotate(${p.r} 50 50)`}>
|
||||||
key={p.r}
|
<ellipse
|
||||||
cx="50"
|
cx="50"
|
||||||
cy="34"
|
cy="34"
|
||||||
rx="8"
|
rx="8"
|
||||||
ry={p.ry}
|
ry={p.ry}
|
||||||
fill={`url(#${petalInId})`}
|
fill={`url(#${petalInId})`}
|
||||||
transform={`rotate(${p.r} 50 50)`}
|
className={dynamic ? "flower-petal-inner" : undefined}
|
||||||
className={dynamic ? "flower-petal-inner" : undefined}
|
style={dynamic ? { animationDelay: `${i * 0.2 + 0.6}s` } : undefined}
|
||||||
style={dynamic ? { animationDelay: `${i * 0.2 + 0.6}s` } : undefined}
|
/>
|
||||||
/>
|
</g>
|
||||||
))}
|
))}
|
||||||
<g className={dynamic ? "flower-core" : undefined}>
|
<g className={dynamic ? "flower-core" : undefined}>
|
||||||
<circle cx="50" cy="50" r="10.5" fill="#f59e0b" opacity="0.22" />
|
<circle cx="50" cy="50" r="10.5" fill="#f59e0b" opacity="0.22" />
|
||||||
@@ -659,13 +659,9 @@ export function LoadingScreen() {
|
|||||||
return (
|
return (
|
||||||
<div className="loading-enter grid min-h-full place-items-center" role="status" aria-label="Loading KisAssistant">
|
<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">
|
<div className="flex flex-col items-center gap-4 px-6 text-center">
|
||||||
<span className="loader-halo">
|
<FlowerMark className="h-16 w-16 sm:h-20 sm:w-20" />
|
||||||
<span className="loader-orbit" aria-hidden="true" />
|
|
||||||
<FlowerMark className="h-16 w-16 sm:h-20 sm:w-20" />
|
|
||||||
</span>
|
|
||||||
<p className="loader-title font-serif text-2xl font-bold tracking-tight">KisAssistant</p>
|
<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>
|
<p className="loading-dots text-sm opacity-60">{t("loading.tagline")}</p>
|
||||||
<span className="loader-bar" aria-hidden="true"><span /></span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
+2
-68
@@ -126,48 +126,6 @@ body {
|
|||||||
animation: loading-in 0.45s ease-out both;
|
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: soft accent glow pulse. */
|
||||||
.loader-title {
|
.loader-title {
|
||||||
animation: title-glow 2.6s ease-in-out infinite;
|
animation: title-glow 2.6s ease-in-out infinite;
|
||||||
@@ -176,30 +134,6 @@ body {
|
|||||||
0%, 100% { opacity: 0.92; }
|
0%, 100% { opacity: 0.92; }
|
||||||
50% { opacity: 1; text-shadow: 0 0 18px rgb(var(--ka-accent-500) / 0.35); }
|
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 {
|
.loading-dots::after {
|
||||||
content: "";
|
content: "";
|
||||||
@@ -229,8 +163,8 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
.flower-petal, .flower-core, .loading-dots::after, .loading-enter, .typing-cursor::after,
|
.flower-petal, .flower-petal-inner, .flower-core, .loading-dots::after, .loading-enter, .typing-cursor::after,
|
||||||
.toast-enter, .toast-exit {
|
.loader-title {
|
||||||
animation: none;
|
animation: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,6 +100,9 @@ const en = {
|
|||||||
"toast.modelChanged": "Model: {label}",
|
"toast.modelChanged": "Model: {label}",
|
||||||
"toast.themeChanged": "Theme changed",
|
"toast.themeChanged": "Theme changed",
|
||||||
"toast.accentChanged": "Accent color changed",
|
"toast.accentChanged": "Accent color changed",
|
||||||
|
"toast.unshared": "Share link removed",
|
||||||
|
"toast.settingsSaved": "Settings saved",
|
||||||
|
"toast.convUpdated": "Conversation “{title}” updated",
|
||||||
"chat.you": "You",
|
"chat.you": "You",
|
||||||
"chat.greet": "How can I help, {name}?",
|
"chat.greet": "How can I help, {name}?",
|
||||||
"chat.greetSub": "Pick a model, then type below. Markdown is supported.",
|
"chat.greetSub": "Pick a model, then type below. Markdown is supported.",
|
||||||
@@ -647,6 +650,9 @@ const es: Dict = {
|
|||||||
"toast.modelChanged": "Modelo: {label}",
|
"toast.modelChanged": "Modelo: {label}",
|
||||||
"toast.themeChanged": "Tema cambiado",
|
"toast.themeChanged": "Tema cambiado",
|
||||||
"toast.accentChanged": "Color de acento 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.you": "Tú",
|
||||||
"chat.greet": "¿En qué te ayudo, {name}?",
|
"chat.greet": "¿En qué te ayudo, {name}?",
|
||||||
"chat.greetSub": "Elige un modelo y escribe abajo. Markdown soportado.",
|
"chat.greetSub": "Elige un modelo y escribe abajo. Markdown soportado.",
|
||||||
@@ -1184,6 +1190,9 @@ const fr: Dict = {
|
|||||||
"toast.modelChanged": "Modèle : {label}",
|
"toast.modelChanged": "Modèle : {label}",
|
||||||
"toast.themeChanged": "Thème modifié",
|
"toast.themeChanged": "Thème modifié",
|
||||||
"toast.accentChanged": "Couleur d’accent modifiée",
|
"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.you": "Vous",
|
||||||
"chat.greet": "Comment puis-je aider, {name} ?",
|
"chat.greet": "Comment puis-je aider, {name} ?",
|
||||||
"chat.greetSub":
|
"chat.greetSub":
|
||||||
@@ -1721,6 +1730,9 @@ const ru: Dict = {
|
|||||||
"toast.modelChanged": "Модель: {label}",
|
"toast.modelChanged": "Модель: {label}",
|
||||||
"toast.themeChanged": "Тема изменена",
|
"toast.themeChanged": "Тема изменена",
|
||||||
"toast.accentChanged": "Акцентный цвет изменён",
|
"toast.accentChanged": "Акцентный цвет изменён",
|
||||||
|
"toast.unshared": "Публичная ссылка удалена",
|
||||||
|
"toast.settingsSaved": "Настройки сохранены",
|
||||||
|
"toast.convUpdated": "Чат “{title}” обновлён",
|
||||||
"chat.you": "Вы",
|
"chat.you": "Вы",
|
||||||
"chat.greet": "Чем помочь, {name}?",
|
"chat.greet": "Чем помочь, {name}?",
|
||||||
"chat.greetSub": "Выберите модель и пишите ниже. Поддерживается Markdown.",
|
"chat.greetSub": "Выберите модель и пишите ниже. Поддерживается Markdown.",
|
||||||
@@ -2245,6 +2257,9 @@ const it: Dict = {
|
|||||||
"toast.modelChanged": "Modello: {label}",
|
"toast.modelChanged": "Modello: {label}",
|
||||||
"toast.themeChanged": "Tema cambiato",
|
"toast.themeChanged": "Tema cambiato",
|
||||||
"toast.accentChanged": "Colore accento cambiato",
|
"toast.accentChanged": "Colore accento cambiato",
|
||||||
|
"toast.unshared": "Link condiviso rimosso",
|
||||||
|
"toast.settingsSaved": "Impostazioni salvate",
|
||||||
|
"toast.convUpdated": "Conversazione “{title}” aggiornata",
|
||||||
"chat.you": "Tu",
|
"chat.you": "Tu",
|
||||||
"chat.greet": "Come posso aiutare, {name}?",
|
"chat.greet": "Come posso aiutare, {name}?",
|
||||||
"chat.greetSub": "Scegli un modello e scrivi qui sotto. Markdown supportato.",
|
"chat.greetSub": "Scegli un modello e scrivi qui sotto. Markdown supportato.",
|
||||||
@@ -2775,6 +2790,9 @@ const pt: Dict = {
|
|||||||
"toast.modelChanged": "Modelo: {label}",
|
"toast.modelChanged": "Modelo: {label}",
|
||||||
"toast.themeChanged": "Tema alterado",
|
"toast.themeChanged": "Tema alterado",
|
||||||
"toast.accentChanged": "Cor de destaque alterada",
|
"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.you": "Tu",
|
||||||
"chat.greet": "Como posso ajudar, {name}?",
|
"chat.greet": "Como posso ajudar, {name}?",
|
||||||
"chat.greetSub": "Escolhe um modelo e escreve abaixo. Markdown suportado.",
|
"chat.greetSub": "Escolhe um modelo e escreve abaixo. Markdown suportado.",
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ export type ToastIcon =
|
|||||||
| "model"
|
| "model"
|
||||||
| "theme"
|
| "theme"
|
||||||
| "accent"
|
| "accent"
|
||||||
|
| "unshare"
|
||||||
|
| "edit"
|
||||||
| "check";
|
| "check";
|
||||||
|
|
||||||
export interface Toast {
|
export interface Toast {
|
||||||
|
|||||||
Reference in New Issue
Block a user