feat: add reusable ToolHealthBadge and ToolHealthBanner components
- Create ToolHealthBadge component in @tpmjs/ui for broken tool indicator - Create ToolHealthBanner component in @tpmjs/ui for detailed health warnings - Integrate both components into playground ToolsSidebar: - Badge shows in tool cards in left sidebar - Banner shows in tool detail modal - Update search-registry to include health fields in API responses - Add package.json exports for new health components These components provide consistent UI for displaying broken tool status across the application (tool search, tool detail pages, playground). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
54ee6439cb
commit
a9e91c02d1
24 changed files with 729 additions and 405 deletions
|
|
@ -1,5 +1,7 @@
|
|||
import { cn } from '@tpmjs/utils/cn';
|
||||
import { forwardRef, useState } from 'react';
|
||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
||||
import { Icon } from '../Icon/Icon';
|
||||
import type { CodeBlockProps } from './types';
|
||||
import {
|
||||
|
|
@ -8,11 +10,75 @@ import {
|
|||
codeBlockCopyButtonVariants,
|
||||
} from './variants';
|
||||
|
||||
// Custom light theme with better contrast
|
||||
const customLightTheme = {
|
||||
'code[class*="language-"]': {
|
||||
color: '#24292e',
|
||||
fontFamily: 'Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace',
|
||||
fontSize: '1em',
|
||||
textAlign: 'left',
|
||||
whiteSpace: 'pre',
|
||||
wordSpacing: 'normal',
|
||||
wordBreak: 'normal',
|
||||
wordWrap: 'normal',
|
||||
lineHeight: '1.5',
|
||||
tabSize: '4',
|
||||
hyphens: 'none',
|
||||
},
|
||||
'pre[class*="language-"]': {
|
||||
color: '#24292e',
|
||||
fontFamily: 'Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace',
|
||||
fontSize: '1em',
|
||||
textAlign: 'left',
|
||||
whiteSpace: 'pre',
|
||||
wordSpacing: 'normal',
|
||||
wordBreak: 'normal',
|
||||
wordWrap: 'normal',
|
||||
lineHeight: '1.5',
|
||||
tabSize: '4',
|
||||
hyphens: 'none',
|
||||
padding: '1em',
|
||||
margin: '0.5em 0',
|
||||
overflow: 'auto',
|
||||
},
|
||||
comment: { color: '#6a737d', fontStyle: 'italic' },
|
||||
prolog: { color: '#6a737d' },
|
||||
doctype: { color: '#6a737d' },
|
||||
cdata: { color: '#6a737d' },
|
||||
punctuation: { color: '#24292e' },
|
||||
property: { color: '#005cc5' },
|
||||
tag: { color: '#22863a' },
|
||||
boolean: { color: '#005cc5' },
|
||||
number: { color: '#005cc5' },
|
||||
constant: { color: '#005cc5' },
|
||||
symbol: { color: '#005cc5' },
|
||||
deleted: { color: '#b31d28' },
|
||||
selector: { color: '#22863a' },
|
||||
'attr-name': { color: '#6f42c1' },
|
||||
string: { color: '#032f62' },
|
||||
char: { color: '#032f62' },
|
||||
builtin: { color: '#005cc5' },
|
||||
inserted: { color: '#22863a' },
|
||||
operator: { color: '#d73a49' },
|
||||
entity: { color: '#6f42c1' },
|
||||
url: { color: '#032f62' },
|
||||
variable: { color: '#e36209' },
|
||||
atrule: { color: '#d73a49' },
|
||||
'attr-value': { color: '#032f62' },
|
||||
function: { color: '#6f42c1' },
|
||||
'class-name': { color: '#6f42c1' },
|
||||
keyword: { color: '#d73a49' },
|
||||
regex: { color: '#032f62' },
|
||||
important: { color: '#d73a49', fontWeight: 'bold' },
|
||||
bold: { fontWeight: 'bold' },
|
||||
italic: { fontStyle: 'italic' },
|
||||
};
|
||||
|
||||
/**
|
||||
* CodeBlock component
|
||||
*
|
||||
* Displays formatted code with optional copy functionality.
|
||||
* Includes syntax-highlighted display and copy-to-clipboard button.
|
||||
* Displays formatted code with syntax highlighting and optional copy functionality.
|
||||
* Uses Prism syntax highlighter for beautiful code display.
|
||||
* Built with React and JSX.
|
||||
*
|
||||
* @example
|
||||
|
|
@ -32,7 +98,10 @@ import {
|
|||
* ```
|
||||
*/
|
||||
export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
|
||||
({ className, code, language = 'text', size = 'md', showCopy = true, ...props }, ref) => {
|
||||
(
|
||||
{ className, code, language = 'text', size = 'md', showCopy = true, theme = 'light', ...props },
|
||||
ref
|
||||
) => {
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const handleCopy = async () => {
|
||||
|
|
@ -46,16 +115,42 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
|
|||
}
|
||||
};
|
||||
|
||||
// Map language aliases to supported languages
|
||||
const languageMap: Record<string, string> = {
|
||||
js: 'javascript',
|
||||
ts: 'typescript',
|
||||
jsx: 'jsx',
|
||||
tsx: 'tsx',
|
||||
py: 'python',
|
||||
rb: 'ruby',
|
||||
sh: 'bash',
|
||||
yml: 'yaml',
|
||||
};
|
||||
|
||||
const normalizedLanguage = languageMap[language] || language;
|
||||
|
||||
return (
|
||||
<div ref={ref} className={cn(codeBlockContainerVariants(), className)} {...props}>
|
||||
<code
|
||||
className={codeBlockCodeVariants({
|
||||
size,
|
||||
})}
|
||||
data-language={language}
|
||||
>
|
||||
{code}
|
||||
</code>
|
||||
<div className={codeBlockCodeVariants({ size })} data-language={normalizedLanguage}>
|
||||
<SyntaxHighlighter
|
||||
language={normalizedLanguage}
|
||||
style={theme === 'dark' ? oneDark : oneLight}
|
||||
customStyle={{
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
background: 'transparent',
|
||||
fontSize: 'inherit',
|
||||
lineHeight: 'inherit',
|
||||
}}
|
||||
codeTagProps={{
|
||||
style: {
|
||||
fontFamily: 'inherit',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{code}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
{showCopy && (
|
||||
<button
|
||||
type="button"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue