feat: Improve DiffView theme and color consistency
Browse files- Added dark/light theme support for syntax highlighting
- Enhanced color styles for added/removed lines and characters
- Integrated theme store to dynamically adjust syntax highlighter theme
- Refined color contrast for better readability across themes
app/components/workbench/DiffView.tsx
CHANGED
|
@@ -10,6 +10,7 @@ import { diffFiles, extractRelativePath } from '~/utils/diff';
|
|
| 10 |
import { ActionRunner } from '~/lib/runtime/action-runner';
|
| 11 |
import type { FileHistory } from '~/types/actions';
|
| 12 |
import { getLanguageFromExtension } from '~/utils/getLanguageFromExtension';
|
|
|
|
| 13 |
|
| 14 |
interface CodeComparisonProps {
|
| 15 |
beforeCode: string;
|
|
@@ -302,12 +303,20 @@ const processChanges = (beforeCode: string, afterCode: string) => {
|
|
| 302 |
const lineNumberStyles = "w-9 shrink-0 pl-2 py-1 text-left font-mono text-bolt-elements-textTertiary border-r border-bolt-elements-borderColor bg-bolt-elements-background-depth-1";
|
| 303 |
const lineContentStyles = "px-1 py-1 font-mono whitespace-pre flex-1 group-hover:bg-bolt-elements-background-depth-2 text-bolt-elements-textPrimary";
|
| 304 |
const diffPanelStyles = "h-full overflow-auto diff-panel-content";
|
|
|
|
|
|
|
| 305 |
const diffLineStyles = {
|
| 306 |
-
added: 'bg-green-500/20 border-l-4 border-green-500',
|
| 307 |
-
removed: 'bg-red-500/20 border-l-4 border-red-500',
|
| 308 |
unchanged: ''
|
| 309 |
};
|
| 310 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 311 |
const renderContentWarning = (type: 'binary' | 'error') => (
|
| 312 |
<div className="h-full flex items-center justify-center p-4">
|
| 313 |
<div className="text-center text-bolt-elements-textTertiary">
|
|
@@ -324,10 +333,11 @@ const renderContentWarning = (type: 'binary' | 'error') => (
|
|
| 324 |
</div>
|
| 325 |
);
|
| 326 |
|
| 327 |
-
const NoChangesView = memo(({ beforeCode, language, highlighter }: {
|
| 328 |
beforeCode: string;
|
| 329 |
language: string;
|
| 330 |
highlighter: any;
|
|
|
|
| 331 |
}) => (
|
| 332 |
<div className="h-full flex flex-col items-center justify-center p-4">
|
| 333 |
<div className="text-center text-bolt-elements-textTertiary">
|
|
@@ -347,7 +357,7 @@ const NoChangesView = memo(({ beforeCode, language, highlighter }: {
|
|
| 347 |
<span className="mr-2"> </span>
|
| 348 |
<span dangerouslySetInnerHTML={{
|
| 349 |
__html: highlighter ?
|
| 350 |
-
highlighter.codeToHtml(line, { lang: language, theme: 'github-dark' })
|
| 351 |
.replace(/<\/?pre[^>]*>/g, '')
|
| 352 |
.replace(/<\/?code[^>]*>/g, '')
|
| 353 |
: line
|
|
@@ -372,7 +382,8 @@ const CodeLine = memo(({
|
|
| 372 |
type,
|
| 373 |
highlighter,
|
| 374 |
language,
|
| 375 |
-
block
|
|
|
|
| 376 |
}: {
|
| 377 |
lineNumber: number;
|
| 378 |
content: string;
|
|
@@ -380,17 +391,14 @@ const CodeLine = memo(({
|
|
| 380 |
highlighter: any;
|
| 381 |
language: string;
|
| 382 |
block: DiffBlock;
|
|
|
|
| 383 |
}) => {
|
| 384 |
-
const bgColor =
|
| 385 |
-
added: 'bg-green-500/20 border-l-4 border-green-500',
|
| 386 |
-
removed: 'bg-red-500/20 border-l-4 border-red-500',
|
| 387 |
-
unchanged: ''
|
| 388 |
-
}[type];
|
| 389 |
|
| 390 |
const renderContent = () => {
|
| 391 |
if (type === 'unchanged' || !block.charChanges) {
|
| 392 |
const highlightedCode = highlighter ?
|
| 393 |
-
highlighter.codeToHtml(content, { lang: language, theme: 'github-dark' })
|
| 394 |
.replace(/<\/?pre[^>]*>/g, '')
|
| 395 |
.replace(/<\/?code[^>]*>/g, '')
|
| 396 |
: content;
|
|
@@ -400,14 +408,10 @@ const CodeLine = memo(({
|
|
| 400 |
return (
|
| 401 |
<>
|
| 402 |
{block.charChanges.map((change, index) => {
|
| 403 |
-
const changeClass =
|
| 404 |
-
added: 'text-green-500 bg-green-500/20',
|
| 405 |
-
removed: 'text-red-500 bg-red-500/20',
|
| 406 |
-
unchanged: ''
|
| 407 |
-
}[change.type];
|
| 408 |
|
| 409 |
const highlightedCode = highlighter ?
|
| 410 |
-
highlighter.codeToHtml(change.value, { lang: language, theme: 'github-dark' })
|
| 411 |
.replace(/<\/?pre[^>]*>/g, '')
|
| 412 |
.replace(/<\/?code[^>]*>/g, '')
|
| 413 |
: change.value;
|
|
@@ -429,8 +433,8 @@ const CodeLine = memo(({
|
|
| 429 |
<div className={lineNumberStyles}>{lineNumber + 1}</div>
|
| 430 |
<div className={`${lineContentStyles} ${bgColor}`}>
|
| 431 |
<span className="mr-2 text-bolt-elements-textTertiary">
|
| 432 |
-
{type === 'added' &&
|
| 433 |
-
{type === 'removed' &&
|
| 434 |
{type === 'unchanged' && ' '}
|
| 435 |
</span>
|
| 436 |
{renderContent()}
|
|
@@ -488,20 +492,20 @@ const FileInfo = memo(({
|
|
| 488 |
{showStats && (
|
| 489 |
<div className="flex items-center gap-1 text-xs">
|
| 490 |
{additions > 0 && (
|
| 491 |
-
<span className="text-green-500">+{additions}</span>
|
| 492 |
)}
|
| 493 |
{deletions > 0 && (
|
| 494 |
-
<span className="text-red-500">-{deletions}</span>
|
| 495 |
)}
|
| 496 |
</div>
|
| 497 |
)}
|
| 498 |
-
<span className="text-yellow-400">Modified</span>
|
| 499 |
<span className="text-bolt-elements-textTertiary text-xs">
|
| 500 |
{new Date().toLocaleTimeString()}
|
| 501 |
</span>
|
| 502 |
</>
|
| 503 |
) : (
|
| 504 |
-
<span className="text-green-400">No Changes</span>
|
| 505 |
)}
|
| 506 |
<FullscreenButton onClick={onToggleFullscreen} isFullscreen={isFullscreen} />
|
| 507 |
</span>
|
|
@@ -512,6 +516,7 @@ const FileInfo = memo(({
|
|
| 512 |
const InlineDiffComparison = memo(({ beforeCode, afterCode, filename, language, lightTheme, darkTheme }: CodeComparisonProps) => {
|
| 513 |
const [isFullscreen, setIsFullscreen] = useState(false);
|
| 514 |
const [highlighter, setHighlighter] = useState<any>(null);
|
|
|
|
| 515 |
|
| 516 |
const toggleFullscreen = useCallback(() => {
|
| 517 |
setIsFullscreen(prev => !prev);
|
|
@@ -521,7 +526,7 @@ const InlineDiffComparison = memo(({ beforeCode, afterCode, filename, language,
|
|
| 521 |
|
| 522 |
useEffect(() => {
|
| 523 |
getHighlighter({
|
| 524 |
-
themes: ['github-dark'],
|
| 525 |
langs: ['typescript', 'javascript', 'json', 'html', 'css', 'jsx', 'tsx']
|
| 526 |
}).then(setHighlighter);
|
| 527 |
}, []);
|
|
@@ -551,6 +556,7 @@ const InlineDiffComparison = memo(({ beforeCode, afterCode, filename, language,
|
|
| 551 |
highlighter={highlighter}
|
| 552 |
language={language}
|
| 553 |
block={block}
|
|
|
|
| 554 |
/>
|
| 555 |
))}
|
| 556 |
</div>
|
|
@@ -559,6 +565,7 @@ const InlineDiffComparison = memo(({ beforeCode, afterCode, filename, language,
|
|
| 559 |
beforeCode={beforeCode}
|
| 560 |
language={language}
|
| 561 |
highlighter={highlighter}
|
|
|
|
| 562 |
/>
|
| 563 |
)}
|
| 564 |
</div>
|
|
|
|
| 10 |
import { ActionRunner } from '~/lib/runtime/action-runner';
|
| 11 |
import type { FileHistory } from '~/types/actions';
|
| 12 |
import { getLanguageFromExtension } from '~/utils/getLanguageFromExtension';
|
| 13 |
+
import { themeStore } from '~/lib/stores/theme';
|
| 14 |
|
| 15 |
interface CodeComparisonProps {
|
| 16 |
beforeCode: string;
|
|
|
|
| 303 |
const lineNumberStyles = "w-9 shrink-0 pl-2 py-1 text-left font-mono text-bolt-elements-textTertiary border-r border-bolt-elements-borderColor bg-bolt-elements-background-depth-1";
|
| 304 |
const lineContentStyles = "px-1 py-1 font-mono whitespace-pre flex-1 group-hover:bg-bolt-elements-background-depth-2 text-bolt-elements-textPrimary";
|
| 305 |
const diffPanelStyles = "h-full overflow-auto diff-panel-content";
|
| 306 |
+
|
| 307 |
+
// Updated color styles for better consistency
|
| 308 |
const diffLineStyles = {
|
| 309 |
+
added: 'bg-green-500/10 dark:bg-green-500/20 border-l-4 border-green-500',
|
| 310 |
+
removed: 'bg-red-500/10 dark:bg-red-500/20 border-l-4 border-red-500',
|
| 311 |
unchanged: ''
|
| 312 |
};
|
| 313 |
|
| 314 |
+
const changeColorStyles = {
|
| 315 |
+
added: 'text-green-700 dark:text-green-500 bg-green-500/10 dark:bg-green-500/20',
|
| 316 |
+
removed: 'text-red-700 dark:text-red-500 bg-red-500/10 dark:bg-red-500/20',
|
| 317 |
+
unchanged: 'text-bolt-elements-textPrimary'
|
| 318 |
+
};
|
| 319 |
+
|
| 320 |
const renderContentWarning = (type: 'binary' | 'error') => (
|
| 321 |
<div className="h-full flex items-center justify-center p-4">
|
| 322 |
<div className="text-center text-bolt-elements-textTertiary">
|
|
|
|
| 333 |
</div>
|
| 334 |
);
|
| 335 |
|
| 336 |
+
const NoChangesView = memo(({ beforeCode, language, highlighter, theme }: {
|
| 337 |
beforeCode: string;
|
| 338 |
language: string;
|
| 339 |
highlighter: any;
|
| 340 |
+
theme: string;
|
| 341 |
}) => (
|
| 342 |
<div className="h-full flex flex-col items-center justify-center p-4">
|
| 343 |
<div className="text-center text-bolt-elements-textTertiary">
|
|
|
|
| 357 |
<span className="mr-2"> </span>
|
| 358 |
<span dangerouslySetInnerHTML={{
|
| 359 |
__html: highlighter ?
|
| 360 |
+
highlighter.codeToHtml(line, { lang: language, theme: theme === 'dark' ? 'github-dark' : 'github-light' })
|
| 361 |
.replace(/<\/?pre[^>]*>/g, '')
|
| 362 |
.replace(/<\/?code[^>]*>/g, '')
|
| 363 |
: line
|
|
|
|
| 382 |
type,
|
| 383 |
highlighter,
|
| 384 |
language,
|
| 385 |
+
block,
|
| 386 |
+
theme
|
| 387 |
}: {
|
| 388 |
lineNumber: number;
|
| 389 |
content: string;
|
|
|
|
| 391 |
highlighter: any;
|
| 392 |
language: string;
|
| 393 |
block: DiffBlock;
|
| 394 |
+
theme: string;
|
| 395 |
}) => {
|
| 396 |
+
const bgColor = diffLineStyles[type];
|
|
|
|
|
|
|
|
|
|
|
|
|
| 397 |
|
| 398 |
const renderContent = () => {
|
| 399 |
if (type === 'unchanged' || !block.charChanges) {
|
| 400 |
const highlightedCode = highlighter ?
|
| 401 |
+
highlighter.codeToHtml(content, { lang: language, theme: theme === 'dark' ? 'github-dark' : 'github-light' })
|
| 402 |
.replace(/<\/?pre[^>]*>/g, '')
|
| 403 |
.replace(/<\/?code[^>]*>/g, '')
|
| 404 |
: content;
|
|
|
|
| 408 |
return (
|
| 409 |
<>
|
| 410 |
{block.charChanges.map((change, index) => {
|
| 411 |
+
const changeClass = changeColorStyles[change.type];
|
|
|
|
|
|
|
|
|
|
|
|
|
| 412 |
|
| 413 |
const highlightedCode = highlighter ?
|
| 414 |
+
highlighter.codeToHtml(change.value, { lang: language, theme: theme === 'dark' ? 'github-dark' : 'github-light' })
|
| 415 |
.replace(/<\/?pre[^>]*>/g, '')
|
| 416 |
.replace(/<\/?code[^>]*>/g, '')
|
| 417 |
: change.value;
|
|
|
|
| 433 |
<div className={lineNumberStyles}>{lineNumber + 1}</div>
|
| 434 |
<div className={`${lineContentStyles} ${bgColor}`}>
|
| 435 |
<span className="mr-2 text-bolt-elements-textTertiary">
|
| 436 |
+
{type === 'added' && <span className="text-green-700 dark:text-green-500">+</span>}
|
| 437 |
+
{type === 'removed' && <span className="text-red-700 dark:text-red-500">-</span>}
|
| 438 |
{type === 'unchanged' && ' '}
|
| 439 |
</span>
|
| 440 |
{renderContent()}
|
|
|
|
| 492 |
{showStats && (
|
| 493 |
<div className="flex items-center gap-1 text-xs">
|
| 494 |
{additions > 0 && (
|
| 495 |
+
<span className="text-green-700 dark:text-green-500">+{additions}</span>
|
| 496 |
)}
|
| 497 |
{deletions > 0 && (
|
| 498 |
+
<span className="text-red-700 dark:text-red-500">-{deletions}</span>
|
| 499 |
)}
|
| 500 |
</div>
|
| 501 |
)}
|
| 502 |
+
<span className="text-yellow-600 dark:text-yellow-400">Modified</span>
|
| 503 |
<span className="text-bolt-elements-textTertiary text-xs">
|
| 504 |
{new Date().toLocaleTimeString()}
|
| 505 |
</span>
|
| 506 |
</>
|
| 507 |
) : (
|
| 508 |
+
<span className="text-green-700 dark:text-green-400">No Changes</span>
|
| 509 |
)}
|
| 510 |
<FullscreenButton onClick={onToggleFullscreen} isFullscreen={isFullscreen} />
|
| 511 |
</span>
|
|
|
|
| 516 |
const InlineDiffComparison = memo(({ beforeCode, afterCode, filename, language, lightTheme, darkTheme }: CodeComparisonProps) => {
|
| 517 |
const [isFullscreen, setIsFullscreen] = useState(false);
|
| 518 |
const [highlighter, setHighlighter] = useState<any>(null);
|
| 519 |
+
const theme = useStore(themeStore);
|
| 520 |
|
| 521 |
const toggleFullscreen = useCallback(() => {
|
| 522 |
setIsFullscreen(prev => !prev);
|
|
|
|
| 526 |
|
| 527 |
useEffect(() => {
|
| 528 |
getHighlighter({
|
| 529 |
+
themes: ['github-dark', 'github-light'],
|
| 530 |
langs: ['typescript', 'javascript', 'json', 'html', 'css', 'jsx', 'tsx']
|
| 531 |
}).then(setHighlighter);
|
| 532 |
}, []);
|
|
|
|
| 556 |
highlighter={highlighter}
|
| 557 |
language={language}
|
| 558 |
block={block}
|
| 559 |
+
theme={theme}
|
| 560 |
/>
|
| 561 |
))}
|
| 562 |
</div>
|
|
|
|
| 565 |
beforeCode={beforeCode}
|
| 566 |
language={language}
|
| 567 |
highlighter={highlighter}
|
| 568 |
+
theme={theme}
|
| 569 |
/>
|
| 570 |
)}
|
| 571 |
</div>
|