diff --git a/packages/widgets/src/open-webui/chat.module.css b/packages/widgets/src/open-webui/chat.module.css
index 3aa564197..844944f73 100644
--- a/packages/widgets/src/open-webui/chat.module.css
+++ b/packages/widgets/src/open-webui/chat.module.css
@@ -34,6 +34,58 @@
margin-top: 2px;
}
+.typing {
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
+ padding: 4px 2px;
+}
+
+.typingDot {
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background-color: var(--mantine-color-dimmed);
+ animation: owui-typing 1.2s infinite ease-in-out both;
+}
+
+.typingDot:nth-child(2) {
+ animation-delay: 0.15s;
+}
+
+.typingDot:nth-child(3) {
+ animation-delay: 0.3s;
+}
+
+@keyframes owui-typing {
+ 0%,
+ 60%,
+ 100% {
+ transform: translateY(0);
+ opacity: 0.35;
+ }
+ 30% {
+ transform: translateY(-4px);
+ opacity: 1;
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .typingDot {
+ animation: owui-typing-fade 1.2s infinite ease-in-out both;
+ }
+
+ @keyframes owui-typing-fade {
+ 0%,
+ 100% {
+ opacity: 0.35;
+ }
+ 50% {
+ opacity: 1;
+ }
+ }
+}
+
.bubble {
max-width: 85%;
}
diff --git a/packages/widgets/src/open-webui/chat.tsx b/packages/widgets/src/open-webui/chat.tsx
index 2a701cf27..771e91359 100644
--- a/packages/widgets/src/open-webui/chat.tsx
+++ b/packages/widgets/src/open-webui/chat.tsx
@@ -535,7 +535,7 @@ export function Chat({ options, integrationIds, isEditMode }: WidgetComponentPro
{messages.map((message, index) => (
))}
- {isStreaming ? : null}
+ {isStreaming ? : null}
{error ? (
@@ -1158,7 +1158,27 @@ function AttachmentBadge({
);
}
-function MessageBubble({ from, content, images }: { from: ChatMessage["role"]; content: string; images?: string[] }) {
+function TypingDots() {
+ return (
+
+
+
+
+
+ );
+}
+
+function MessageBubble({
+ from,
+ content,
+ images,
+ loading,
+}: {
+ from: ChatMessage["role"];
+ content: string;
+ images?: string[];
+ loading?: boolean;
+}) {
const isUser = from === "user";
const avatar = (
@@ -1182,6 +1202,7 @@ function MessageBubble({ from, content, images }: { from: ChatMessage["role"]; c
))}
) : null}
+ {loading && !content ? : null}
{content ? (
{content}