Skip to content
20 changes: 16 additions & 4 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,12 @@ const SettingsView = React.memo(() => <SettingsPanel />);
SettingsView.displayName = 'SettingsView';

function App() {
const {
isAuthenticated,
currentView,
const {
isAuthenticated,
currentView,
selectedCategory,
theme,
hasHydrated,
searchResults,
repositories,
setSelectedCategory,
Expand Down Expand Up @@ -107,7 +108,7 @@ function App() {
switch (currentView) {
case 'repositories':
return (
<RepositoriesView
<RepositoriesView
repositories={repositories}
searchResults={searchResults}
selectedCategory={selectedCategory}
Expand All @@ -129,6 +130,17 @@ function App() {
}
}, [currentView, repositories, searchResults, selectedCategory, handleCategorySelect]);

// Show loading state while store is hydrating to ensure correct theme is applied
if (!hasHydrated) {
return (
<div className="min-h-screen bg-light-bg dark:bg-marketing-black flex items-center justify-center">
<div className="text-gray-900 dark:text-text-primary text-lg font-medium animate-pulse">
Loading...
</div>
</div>
);
}

if (!isAuthenticated) {
return <LoginScreen />;
}
Expand Down
66 changes: 24 additions & 42 deletions src/components/MarkdownRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ interface MarkdownRendererProps {
enableHtml?: boolean;
baseUrl?: string;
headingIds?: Map<string, string>;
fontSize?: 'small' | 'medium' | 'large';
}

const REMARK_PLUGINS = [remarkGfm, remarkBreaks];
Expand Down Expand Up @@ -92,10 +93,6 @@ const CodeBlock: React.FC<{
}
}, [codeText, uiLanguage]);

const codeLines = codeText.split('\n');
const lineCount = codeLines.length;
const showLineNumbers = lineCount > 3;

const isBashLike = ['bash', 'sh', 'shell', 'zsh'].includes(normalizedLanguage);
const isPowerShell = ['powershell', 'ps1'].includes(normalizedLanguage);
const isCmdLike = ['cmd', 'bat'].includes(normalizedLanguage);
Expand Down Expand Up @@ -141,11 +138,6 @@ const CodeBlock: React.FC<{
)}
</div>
<div className="flex items-center gap-2">
{showLineNumbers && (
<span className="text-xs text-gray-400 dark:text-text-tertiary font-mono">
{lineCount} {uiLanguage === 'zh' ? '行' : 'lines'}
</span>
)}
<button
onClick={handleCopy}
className={`flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg text-xs font-medium transition-all ${
Expand Down Expand Up @@ -187,36 +179,11 @@ const CodeBlock: React.FC<{
? 'bg-gradient-to-br from-cyan-50/40 to-slate-100/20 dark:from-[#0d1117] dark:to-[#161b22]'
: 'bg-light-bg dark:bg-[#0d1117]'
}`}>
{showLineNumbers ? (
<div className="flex">
<div className={`flex-shrink-0 py-3 px-3 text-right select-none border-r ${
isBashLike
? 'border-black/[0.06] dark:border-[#30363d] bg-status-emerald/10 dark:bg-[#161b22]'
: isPowerShell
? 'border-black/[0.06] dark:border-[#30363d] bg-gray-100 dark:bg-[#161b22]'
: isCmdLike
? 'border-cyan-200 dark:border-[#30363d] bg-cyan-50/20 dark:bg-[#161b22]'
: 'border-black/[0.06] dark:border-[#30363d] bg-light-surface/50 dark:bg-[#161b22]'
}`}>
{codeLines.map((_, i) => (
<div key={i} className="text-xs leading-6 text-gray-400 dark:text-[#6e7681] font-mono tabular-nums">
{i + 1}
</div>
))}
</div>
<pre className={`flex-1 p-4 overflow-x-auto ${className || ''}`}>
<code ref={codeRef} className={`text-sm font-mono leading-6 text-gray-800 dark:text-[#e6edf3] ${normalizedLanguage ? `language-${normalizedLanguage}` : ''}`}>
{codeText}
</code>
</pre>
</div>
) : (
<pre className={`p-4 overflow-x-auto ${className || ''}`}>
<code ref={codeRef} className={`text-sm font-mono leading-6 text-gray-800 dark:text-[#e6edf3] ${normalizedLanguage ? `language-${normalizedLanguage}` : ''}`}>
{codeText}
</code>
</pre>
)}
<pre className={`p-4 overflow-x-auto ${className || ''}`}>
<code ref={codeRef} className={`text-sm font-mono leading-6 text-gray-800 dark:text-[#e6edf3] ${normalizedLanguage ? `language-${normalizedLanguage}` : ''}`}>
{codeText}
</code>
</pre>
</div>
</div>
);
Expand Down Expand Up @@ -481,7 +448,9 @@ const MarkdownImage: React.FC<{ src?: string; alt?: string; baseUrl?: string }>
setIsLoading(false);
}, []);

const handleRetry = useCallback(() => {
const handleRetry = useCallback((e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
setHasError(false);
setIsLoading(true);
setImageSizeKnown(false);
Expand Down Expand Up @@ -784,7 +753,8 @@ const MarkdownRenderer: React.FC<MarkdownRendererProps> = memo(({
shouldRender = true,
enableHtml = false,
baseUrl,
headingIds
headingIds,
fontSize = 'medium'
}) => {
const headingCounterRef = useRef(headingIds?.size ?? 0);
const headingTextCountMapRef = useRef(new Map<string, number>());
Expand All @@ -796,6 +766,18 @@ const MarkdownRenderer: React.FC<MarkdownRendererProps> = memo(({

const rehypePlugins = enableHtml ? REHYPE_PLUGINS_WITH_HTML : REHYPE_PLUGINS_NO_HTML;

const getProseClass = useCallback(() => {
switch (fontSize) {
case 'small':
return 'prose prose-sm dark:prose-invert';
case 'large':
return 'prose prose-lg dark:prose-invert';
case 'medium':
default:
return 'prose dark:prose-invert';
}
}, [fontSize]);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const getHeadingId = useCallback((children: React.ReactNode): string | undefined => {
if (headingIds && headingIds.size > 0) {
const text = extractTextFromChildren(children);
Expand Down Expand Up @@ -933,7 +915,7 @@ const MarkdownRenderer: React.FC<MarkdownRendererProps> = memo(({
}

return (
<div className={`prose prose-sm dark:prose-invert max-w-none ${className}`}>
<div className={`${getProseClass()} max-w-none ${className}`}>
<ReactMarkdown
remarkPlugins={REMARK_PLUGINS}
rehypePlugins={rehypePlugins}
Expand Down
13 changes: 13 additions & 0 deletions src/components/ReadmeModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ export const ReadmeModal: React.FC<ReadmeModalProps> = ({

const currentFontSize = FONT_SIZES[fontSizeIndex].value;

const getFontSizeType = useCallback((): 'small' | 'medium' | 'large' => {
switch (fontSizeIndex) {
case 0:
return 'small';
case 2:
return 'large';
case 1:
default:
return 'medium';
}
}, [fontSizeIndex]);

const extractToc = useCallback((content: string): { items: TocItem[], idMap: Map<string, string> } => {
const items: TocItem[] = [];
const idMap = new Map<string, string>();
Expand Down Expand Up @@ -477,6 +489,7 @@ export const ReadmeModal: React.FC<ReadmeModalProps> = ({
enableHtml={true}
baseUrl={repository?.html_url}
headingIds={headingIdMap}
fontSize={getFontSizeType()}
/>
) : (
<div className="flex flex-col items-center justify-center py-12">
Expand Down
16 changes: 8 additions & 8 deletions src/components/RepositoryCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -873,28 +873,28 @@ const RepositoryCardComponent: React.FC<RepositoryCardProps> = ({
</div>
</div>

{/* Description with Tooltip */}
{/* Description with Tooltip - Enhanced for Light Mode */}
<div className="mb-4 flex-1">
<div
className="relative"
className="relative group"
onMouseEnter={() => isTextTruncated && setShowTooltip(true)}
onMouseLeave={() => setShowTooltip(false)}
>
<p
ref={descriptionRef}
className="text-gray-900 dark:text-text-secondary text-sm leading-relaxed line-clamp-3 mb-2"
className="text-gray-800 dark:text-text-secondary text-[13px] leading-[1.625] line-clamp-3 mb-2 transition-colors duration-200 hover:text-gray-900 dark:hover:text-text-primary rounded px-1 -mx-1 hover:bg-gray-50/50 dark:hover:bg-white/[0.02]"
>
{highlightSearchTerm(displayContent.content, searchQuery)}
</p>

{/* Tooltip - Only show when text is actually truncated */}
{/* Enhanced Tooltip - Optimized for Light Mode Readability */}
{isTextTruncated && showTooltip && (
<div className="absolute z-50 bottom-full left-0 right-0 mb-2 p-3 bg-gray-900 dark:bg-surface-3 text-white dark:text-text-primary text-sm rounded-lg shadow-dialog border border-black/[0.06] dark:border-white/[0.04]">
<div className="whitespace-pre-wrap break-words">
<div className="absolute z-50 bottom-full left-0 right-0 mb-2 p-4 bg-white dark:bg-surface-3 text-gray-900 dark:text-text-primary text-[13px] leading-[1.625] rounded-xl shadow-dialog border border-gray-200/80 dark:border-white/[0.04] animate-fade-in max-h-[280px] overflow-y-auto scrollbar-auto">
<div className="whitespace-pre-wrap break-words pr-2">
{highlightSearchTerm(displayContent.content, searchQuery)}
</div>
{/* Arrow */}
<div className="absolute top-full left-4 w-0 h-0 border-l-4 border-r-4 border-t-4 border-l-transparent border-r-transparent border-t-gray-900 dark:border-t-surface-3"></div>
{/* Arrow with Light Mode Optimization */}
<div className="absolute top-full left-4 w-0 h-0 border-l-[6px] border-l-transparent border-r-[6px] border-r-transparent border-t-[6px] border-t-white dark:border-t-surface-3 drop-shadow-sm"></div>
</div>
)}
</div>
Expand Down
Loading
Loading