repositories
loading repo index
repositories
loading repo index
repository
loading code, commits, and activity
The Living OS cockpit
stars
latest
clone command
git clone gitlawb://did:key:z6Mku78K...XywC/living-os-cockp...git clone gitlawb://did:key:z6Mku78K.../living-os-cockp...59751530feat: surface worker supervisor health in live work5h ago| #1 | 'use client'; |
| #2 | |
| #3 | import ReactMarkdown from 'react-markdown'; |
| #4 | import remarkBreaks from 'remark-breaks'; |
| #5 | import remarkGfm from 'remark-gfm'; |
| #6 | import { Copy } from 'lucide-react'; |
| #7 | |
| #8 | type MessageContentProps = { |
| #9 | content: string; |
| #10 | role: 'user' | 'assistant'; |
| #11 | }; |
| #12 | |
| #13 | function normalizeMessageContent(content: string): string { |
| #14 | return (content || '') |
| #15 | .replace(/\r\n/g, '\n') |
| #16 | .replace(/\r/g, '\n') |
| #17 | .replace(/\\r\\n/g, '\n') |
| #18 | .replace(/\\n/g, '\n'); |
| #19 | } |
| #20 | |
| #21 | function textFromChildren(children: any): string { |
| #22 | if (typeof children === 'string') return children; |
| #23 | if (Array.isArray(children)) return children.map(textFromChildren).join(''); |
| #24 | if (children?.props?.children) return textFromChildren(children.props.children); |
| #25 | return ''; |
| #26 | } |
| #27 | |
| #28 | function CodeBlock({ children, className }: { children: any; className?: string }) { |
| #29 | const code = textFromChildren(children).replace(/\n$/, ''); |
| #30 | const language = className?.replace('language-', '') || 'text'; |
| #31 | const copy = async () => navigator.clipboard.writeText(code); |
| #32 | |
| #33 | return ( |
| #34 | <div className="markdown-code-shell"> |
| #35 | <div className="markdown-code-toolbar"> |
| #36 | <span>{language}</span> |
| #37 | <button type="button" onClick={copy} aria-label="Copy code block"> |
| #38 | <Copy size={12} /> Copy |
| #39 | </button> |
| #40 | </div> |
| #41 | <pre><code className={className}>{code}</code></pre> |
| #42 | </div> |
| #43 | ); |
| #44 | } |
| #45 | |
| #46 | export default function MessageContent({ content, role }: MessageContentProps) { |
| #47 | const normalized = normalizeMessageContent(content); |
| #48 | |
| #49 | if (role === 'user') { |
| #50 | return <div className="message-content message-content-user whitespace-pre-wrap">{normalized}</div>; |
| #51 | } |
| #52 | |
| #53 | return ( |
| #54 | <div className="message-content message-content-assistant"> |
| #55 | <ReactMarkdown |
| #56 | remarkPlugins={[remarkGfm, remarkBreaks]} |
| #57 | components={{ |
| #58 | a: ({ href, children }) => ( |
| #59 | <a href={href} target="_blank" rel="noreferrer"> |
| #60 | {children} |
| #61 | </a> |
| #62 | ), |
| #63 | code: ({ inline, className, children, ...props }: any) => ( |
| #64 | inline ? <code className={className} {...props}>{children}</code> : <CodeBlock className={className}>{children}</CodeBlock> |
| #65 | ), |
| #66 | table: ({ children }) => ( |
| #67 | <div className="markdown-table-wrap"> |
| #68 | <table>{children}</table> |
| #69 | </div> |
| #70 | ), |
| #71 | }} |
| #72 | > |
| #73 | {normalized} |
| #74 | </ReactMarkdown> |
| #75 | </div> |
| #76 | ); |
| #77 | } |
| #78 |