tools and skills, etc in the containers

This commit is contained in:
2026-02-24 01:24:28 +00:00
parent 071e2decc3
commit d6ffe43a11
16 changed files with 996 additions and 66 deletions
@@ -4,6 +4,30 @@ import rehypeRaw from 'rehype-raw';
import type { ChatMessage } from '../types';
import { ToolActivity } from './ToolActivity';
import { QuestionActivity } from './QuestionActivity';
import { getRawUrl } from '../../FileViewer/file-types';
// Matches absolute image file paths, e.g. /home/user/pic.png or /tmp/photo.jpg
const IMAGE_PATH_RE = /(\/(?:home\/[^/\s]+\/)?[^\s`"'<>\n\r[\]()]+\.(?:png|jpg|jpeg|gif|webp|svg|bmp|ico))/gi;
function pathToImageUrl(filePath: string): string {
// Strip /home/{username} prefix — the file browser root=home serves from the user's home dir
const relative = filePath.replace(/^\/home\/[^/]+/, '') || '/';
return getRawUrl(relative, 'home');
}
function injectImages(text: string): string {
// Split on fenced code blocks and inline code so we don't touch paths inside backticks
const parts = text.split(/(```[\s\S]*?```|`[^`\n]+`)/g);
return parts
.map((part, i) => {
if (i % 2 === 1) return part; // inside code — leave untouched
return part.replace(IMAGE_PATH_RE, (match) => {
const filename = match.split('/').pop() ?? 'image';
return `![${filename}](${pathToImageUrl(match)})`;
});
})
.join('');
}
const FRONTMATTER_RE = /^<frontmatter>([\s\S]*?)<\/frontmatter>\s*/;
@@ -56,7 +80,7 @@ export const MessageBubble = ({ message, onAnswer }: MessageBubbleProps) => {
<div className="max-w-[85%] rounded-2xl rounded-tl-sm bg-muted/50 border border-border/50 px-4 py-2.5">
<div className="chat-md">
<ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>
{message.text}
{injectImages(message.text)}
</ReactMarkdown>
</div>
</div>
@@ -100,7 +124,7 @@ export const StreamingBubble = ({ text }: StreamingBubbleProps) => {
<div className="max-w-[85%] rounded-2xl rounded-tl-sm bg-muted/50 border border-border/50 px-4 py-2.5">
<div className="chat-md">
<ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>
{text}
{injectImages(text)}
</ReactMarkdown>
<span className="inline-block w-2 h-4 bg-duck-teal/60 animate-pulse ml-0.5 align-middle" />
</div>