diff --git a/internal/agent/title_generate.go b/internal/agent/title_generate.go
index 92ef2da3..627a4534 100644
--- a/internal/agent/title_generate.go
+++ b/internal/agent/title_generate.go
@@ -9,7 +9,7 @@ import (
"github.com/nextlevelbuilder/goclaw/internal/providers"
)
-const titleGenerateTimeout = 30 * time.Second
+const titleGenerateTimeout = 120 * time.Second
const titleSystemPrompt = `Generate a short title (max 15 words) for this conversation based on the user's message. Reply with only the title, no quotes or punctuation wrapping.`
diff --git a/ui/web/src/components/chat/agent-selector.tsx b/ui/web/src/components/chat/agent-selector.tsx
index 8429929a..25cb9b78 100644
--- a/ui/web/src/components/chat/agent-selector.tsx
+++ b/ui/web/src/components/chat/agent-selector.tsx
@@ -34,7 +34,8 @@ export function AgentSelector({ value, onChange }: AgentSelectorProps) {
const active = (res.agents ?? []).filter((a) => a.status === "active");
setAgents(active);
if (active.length > 0 && !active.some((a) => a.agent_key === value)) {
- onChange(active[0]!.agent_key);
+ const defaultAgent = active.find((a) => a.is_default) ?? active[0]!;
+ onChange(defaultAgent.agent_key);
}
})
.catch(() => {});
@@ -92,7 +93,7 @@ export function AgentSelector({ value, onChange }: AgentSelectorProps) {
{agents.length === 0 && (
diff --git a/ui/web/src/components/chat/media-gallery.tsx b/ui/web/src/components/chat/media-gallery.tsx
index 084984b5..216ce7f0 100644
--- a/ui/web/src/components/chat/media-gallery.tsx
+++ b/ui/web/src/components/chat/media-gallery.tsx
@@ -72,19 +72,38 @@ export function MediaGallery({ items }: MediaGalleryProps) {
{images.length > 0 && (
{images.map((item, i) => (
-
+
+
+
+
+
))}
)}
@@ -121,6 +140,8 @@ export function MediaGallery({ items }: MediaGalleryProps) {
setLightboxIdx(null)}
/>
)}
diff --git a/ui/web/src/components/shared/image-lightbox.tsx b/ui/web/src/components/shared/image-lightbox.tsx
index 5441cc0e..a5ea6899 100644
--- a/ui/web/src/components/shared/image-lightbox.tsx
+++ b/ui/web/src/components/shared/image-lightbox.tsx
@@ -1,13 +1,16 @@
import { useEffect, useCallback } from "react";
-import { X } from "lucide-react";
+import { X, Download } from "lucide-react";
+import { formatSize } from "@/lib/file-helpers";
interface ImageLightboxProps {
src: string;
alt?: string;
+ fileName?: string;
+ size?: number;
onClose: () => void;
}
-export function ImageLightbox({ src, alt, onClose }: ImageLightboxProps) {
+export function ImageLightbox({ src, alt, fileName, size, onClose }: ImageLightboxProps) {
const handleKeyDown = useCallback(
(e: KeyboardEvent) => {
if (e.key === "Escape") onClose();
@@ -20,24 +23,47 @@ export function ImageLightbox({ src, alt, onClose }: ImageLightboxProps) {
return () => document.removeEventListener("keydown", handleKeyDown);
}, [handleKeyDown]);
+ const displayName = fileName || alt || "image";
+
return (
-
+

e.stopPropagation()}
/>
+ {(fileName || (size != null && size > 0)) && (
+
e.stopPropagation()}
+ >
+ {fileName && {fileName}}
+ {fileName && size != null && size > 0 && ยท}
+ {size != null && size > 0 && {formatSize(size)}}
+
+ )}
);
}
diff --git a/ui/web/src/components/shared/markdown-renderer.tsx b/ui/web/src/components/shared/markdown-renderer.tsx
index 857eab5a..222b4379 100644
--- a/ui/web/src/components/shared/markdown-renderer.tsx
+++ b/ui/web/src/components/shared/markdown-renderer.tsx
@@ -166,18 +166,32 @@ export function MarkdownRenderer({ content, className }: MarkdownRendererProps)
},
img({ src, alt, ...props }) {
const resolvedSrc = isFileLink(src) ? toFileUrl(src!, token) : src;
+ const displayName = alt || fileNameFromHref(src ?? "");
return (
-
{
- e.preventDefault();
- if (resolvedSrc) openLightbox(resolvedSrc, alt ?? "image");
- }}
- {...props}
- />
+
+
{
+ e.preventDefault();
+ if (resolvedSrc) openLightbox(resolvedSrc, alt ?? "image");
+ }}
+ {...props}
+ />
+ {resolvedSrc && (
+ e.stopPropagation()}
+ className="absolute top-1.5 right-1.5 flex items-center justify-center rounded-md bg-black/60 p-1 text-white shadow-md opacity-0 transition-opacity group-hover/img:opacity-100 hover:bg-black/80 cursor-pointer"
+ title="Download"
+ >
+
+
+ )}
+
);
},
table({ children, ...props }) {