refactor(chat): remove dead syntaxTheme plumbing

The Prism syntaxTheme prop is no longer read after code highlighting moved to
the Shiki worker. Remove the now-dead prop threading and its source.

- Drop syntaxTheme from interfaces, destructures, prop passes, and React.memo
  comparators across ChatMessage, MessageBody, ProgressiveGroup, ToolPart,
  TurnActivity, ToolOutputDialog, and ChatInput.
- Drop the unused _syntaxTheme param from renderWebSearchOutput.
- Remove the dead generateSyntaxTheme usages (ChatMessage memo, PlanView
  unassigned memo) and delete the now-unimported syntaxThemeGenerator module.
This commit is contained in:
Bohdan Triapitsyn
2026-06-16 01:07:43 +03:00
parent e41e5bac91
commit c22e1cbb15
10 changed files with 3 additions and 250 deletions
@@ -4557,7 +4557,6 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
<ToolOutputDialog
popup={attachmentPreview}
onOpenChange={handleAttachmentPreviewOpenChange}
syntaxTheme={{}}
isMobile={isMobile}
/>
</>
@@ -2,7 +2,6 @@ import React from 'react';
import type { Message, Part } from '@opencode-ai/sdk/v2';
import { useShallow } from 'zustand/react/shallow';
import { defaultCodeDark, defaultCodeLight } from '@/lib/codeTheme';
import { MessageFreshnessDetector } from '@/lib/messageFreshness';
import { useConfigStore } from '@/stores/useConfigStore';
import { useFeatureFlagsStore } from '@/stores/useFeatureFlagsStore';
@@ -12,7 +11,6 @@ import { useSessionUIStore } from '@/sync/session-ui-store';
import { useSelectionStore } from '@/sync/selection-store';
import { useDeviceInfo } from '@/lib/device';
import { useThemeSystem } from '@/contexts/useThemeSystem';
import { generateSyntaxTheme } from '@/lib/theme/syntaxThemeGenerator';
import { cn } from '@/lib/utils';
import type { AnimationHandlers, ContentChangeReason } from '@/hooks/useChatAutoFollow';
@@ -548,13 +546,6 @@ const ChatMessage: React.FC<ChatMessageProps> = ({
return false;
}, [themeVariant]);
const syntaxTheme = React.useMemo(() => {
if (currentTheme) {
return generateSyntaxTheme(currentTheme);
}
return isDarkTheme ? defaultCodeDark : defaultCodeLight;
}, [currentTheme, isDarkTheme]);
const shouldAnimateMessage = React.useMemo(() => {
if (isUser) return false;
const freshnessDetector = MessageFreshnessDetector.getInstance();
@@ -1026,7 +1017,6 @@ const ChatMessage: React.FC<ChatMessageProps> = ({
isUser={isUser}
isMessageCompleted={isMessageCompleted}
messageFinish={messageFinish}
syntaxTheme={syntaxTheme}
isMobile={isMobile}
alwaysShowActions={alwaysShowMessageActions}
hasTouchInput={hasTouchInput}
@@ -1060,7 +1050,6 @@ const ChatMessage: React.FC<ChatMessageProps> = ({
isUser={isUser}
isMessageCompleted={isMessageCompleted}
messageFinish={messageFinish}
syntaxTheme={syntaxTheme}
isMobile={isMobile}
alwaysShowActions={alwaysShowMessageActions}
hasTouchInput={hasTouchInput}
@@ -1113,7 +1102,6 @@ const ChatMessage: React.FC<ChatMessageProps> = ({
messageFinish={messageFinish}
messageCompletedAt={messageCompletedAt ?? undefined}
messageCreatedAt={messageCreatedAt ?? undefined}
syntaxTheme={syntaxTheme}
isMobile={isMobile}
alwaysShowActions={alwaysShowMessageActions}
hasTouchInput={hasTouchInput}
@@ -1146,7 +1134,6 @@ const ChatMessage: React.FC<ChatMessageProps> = ({
<ToolOutputDialog
popup={popupContent}
onOpenChange={handlePopupChange}
syntaxTheme={syntaxTheme}
isMobile={isMobile}
/>
</React.Suspense>
@@ -17,7 +17,6 @@ interface TurnActivityProps {
isExpanded: boolean;
collapsedPreviewCount?: number;
onToggle: () => void;
syntaxTheme: Record<string, React.CSSProperties>;
isMobile: boolean;
expandedTools: Set<string>;
onToggleTool: (toolId: string) => void;
@@ -337,7 +337,6 @@ interface MessageBodyProps {
messageCompletedAt?: number;
messageCreatedAt?: number;
syntaxTheme: { [key: string]: React.CSSProperties };
isMobile: boolean;
alwaysShowActions?: boolean;
@@ -950,7 +949,6 @@ const AssistantMessageBody = React.memo(({
messageCompletedAt,
messageCreatedAt,
syntaxTheme,
isMobile,
alwaysShowActions,
hasTouchInput,
@@ -1642,7 +1640,6 @@ const AssistantMessageBody = React.memo(({
isExpanded={turnGroupingContext.isGroupExpanded === true}
collapsedPreviewCount={collapsedPreviewCount}
onToggle={toggleActivityGroup}
syntaxTheme={syntaxTheme}
isMobile={isMobile}
expandedTools={expandedTools}
onToggleTool={onToggleTool}
@@ -1794,7 +1791,6 @@ const AssistantMessageBody = React.memo(({
part={toolPart}
isExpanded={expandedTools.has(toolPart.id)}
onToggle={onToggleTool}
syntaxTheme={syntaxTheme}
isMobile={isMobile}
alwaysShowActions={alwaysShowMessageActions}
onContentChange={onContentChange}
@@ -1866,7 +1862,6 @@ const AssistantMessageBody = React.memo(({
effectiveStreamPhase,
showReasoningTraces,
shouldDeferSortedInlineText,
syntaxTheme,
toggleActivityGroup,
turnGroupingContext,
visibleParts,
@@ -32,7 +32,6 @@ import { runtimeFetch } from '@/lib/runtime-fetch';
interface ToolOutputDialogProps {
popup: ToolPopupContent;
onOpenChange: (open: boolean) => void;
syntaxTheme: { [key: string]: React.CSSProperties };
isMobile: boolean;
}
@@ -984,7 +983,7 @@ const MermaidPreviewDialog: React.FC<{
return createPortal(content, document.body);
};
const ToolOutputDialog: React.FC<ToolOutputDialogProps> = ({ popup, onOpenChange, syntaxTheme, isMobile }) => {
const ToolOutputDialog: React.FC<ToolOutputDialogProps> = ({ popup, onOpenChange, isMobile }) => {
const { t } = useI18n();
const [diffViewMode, setDiffViewMode] = React.useState<DiffViewMode>('unified');
const pierreThemeConfig = usePierreThemeConfig();
@@ -1170,7 +1169,7 @@ const ToolOutputDialog: React.FC<ToolOutputDialogProps> = ({ popup, onOpenChange
if (tool === 'web-search' || tool === 'websearch' || tool === 'search_web') {
return (
renderWebSearchOutput(popup.content, syntaxTheme) || (
renderWebSearchOutput(popup.content) || (
<WorkerHighlightedCode
language="text"
code={popup.content}
@@ -35,7 +35,6 @@ interface ProgressiveGroupProps {
isExpanded: boolean;
collapsedPreviewCount?: number;
onToggle: () => void;
syntaxTheme: Record<string, React.CSSProperties>;
isMobile: boolean;
expandedTools: Set<string>;
onToggleTool: (toolId: string) => void;
@@ -373,7 +372,6 @@ type AggregatedRow =
interface ExpandableToolRowProps {
activity: TurnActivityPart;
isExpanded: boolean;
syntaxTheme: Record<string, React.CSSProperties>;
isMobile: boolean;
onToggleTool: (toolId: string) => void;
onShowPopup: (content: ToolPopupContent) => void;
@@ -385,7 +383,6 @@ interface ExpandableToolRowProps {
const ExpandableToolRow: React.FC<ExpandableToolRowProps> = ({
activity,
isExpanded,
syntaxTheme,
isMobile,
onToggleTool,
onShowPopup,
@@ -402,7 +399,6 @@ const ExpandableToolRow: React.FC<ExpandableToolRowProps> = ({
part={activity.part as ToolPartType}
isExpanded={isExpanded}
onToggle={handleToggle}
syntaxTheme={syntaxTheme}
isMobile={isMobile}
onContentChange={onContentChange}
onShowPopup={onShowPopup}
@@ -425,7 +421,6 @@ const ExpandableToolRow: React.FC<ExpandableToolRowProps> = ({
const MemoExpandableToolRow = React.memo(ExpandableToolRow, (prev, next) => {
return prev.isExpanded === next.isExpanded
&& prev.syntaxTheme === next.syntaxTheme
&& prev.isMobile === next.isMobile
&& prev.onToggleTool === next.onToggleTool
&& prev.onShowPopup === next.onShowPopup
@@ -826,7 +821,6 @@ const ProgressiveGroup: React.FC<ProgressiveGroupProps> = ({
isExpanded,
collapsedPreviewCount = 0,
onToggle,
syntaxTheme,
isMobile,
expandedTools,
onToggleTool,
@@ -915,7 +909,6 @@ const ProgressiveGroup: React.FC<ProgressiveGroupProps> = ({
key={row.activity.id}
activity={row.activity}
isExpanded={expandedTools.has(row.activity.id)}
syntaxTheme={syntaxTheme}
isMobile={isMobile}
onToggleTool={onToggleTool}
onShowPopup={onShowPopup}
@@ -942,7 +935,6 @@ const ProgressiveGroup: React.FC<ProgressiveGroupProps> = ({
key={row.activity.id}
activity={row.activity}
isExpanded={expandedTools.has(row.activity.id)}
syntaxTheme={syntaxTheme}
isMobile={isMobile}
onToggleTool={onToggleTool}
onShowPopup={onShowPopup}
@@ -59,7 +59,6 @@ interface ToolPartProps {
part: ToolPartType;
isExpanded: boolean;
onToggle: (toolId: string) => void;
syntaxTheme: { [key: string]: React.CSSProperties };
isMobile: boolean;
alwaysShowActions?: boolean;
onContentChange?: (reason?: ContentChangeReason) => void;
@@ -843,7 +842,6 @@ const ToolScrollableTextOutput: React.FC<{
part: ToolPartType;
metadata: Record<string, unknown> | undefined;
input: Record<string, unknown> | undefined;
syntaxTheme: { [key: string]: React.CSSProperties };
}> = ({ output, part, metadata, input }) => {
const { t } = useI18n();
const renderedOutput = getToolOutputText(output, part, metadata);
@@ -1594,7 +1592,6 @@ DiffPreview.displayName = 'DiffPreview';
interface ToolExpandedContentProps {
part: ToolPartType;
state: ToolStateUnion;
syntaxTheme: { [key: string]: React.CSSProperties };
currentDirectory: string;
isExpanded: boolean;
onShowPopup?: (content: ToolPopupContent) => void;
@@ -1603,7 +1600,6 @@ interface ToolExpandedContentProps {
const ToolExpandedContent: React.FC<ToolExpandedContentProps> = React.memo(({
part,
state,
syntaxTheme,
currentDirectory,
isExpanded,
onShowPopup,
@@ -1853,7 +1849,6 @@ const ToolExpandedContent: React.FC<ToolExpandedContentProps> = React.memo(({
part={part}
metadata={metadata}
input={input}
syntaxTheme={syntaxTheme}
/>,
{
className: part.tool === 'bash' ? 'p-1 rounded-none' : 'p-1',
@@ -1944,7 +1939,6 @@ const ToolPartContent: React.FC<ToolPartProps> = ({
part,
isExpanded,
onToggle,
syntaxTheme,
isMobile,
onContentChange,
onShowPopup,
@@ -2844,7 +2838,6 @@ const ToolPartContent: React.FC<ToolPartProps> = ({
<ToolExpandedContent
part={part}
state={state}
syntaxTheme={syntaxTheme}
currentDirectory={currentDirectory}
isExpanded={isExpanded}
onShowPopup={onShowPopup}
@@ -2926,7 +2919,6 @@ const ToolPart: React.FC<ToolPartProps> = (props) => {
export default React.memo(ToolPart, (prev, next) => {
return areRenderRelevantPartsEqual([prev.part], [next.part])
&& prev.isExpanded === next.isExpanded
&& prev.syntaxTheme === next.syntaxTheme
&& prev.isMobile === next.isMobile
&& prev.alwaysShowActions === next.alwaysShowActions
&& prev.onContentChange === next.onContentChange
@@ -510,7 +510,7 @@ export const renderTodoOutput = (
}
};
export const renderWebSearchOutput = (output: string, _syntaxTheme: { [key: string]: React.CSSProperties }, options?: { unstyled?: boolean }) => {
export const renderWebSearchOutput = (output: string, options?: { unstyled?: boolean }) => {
try {
return (
<div
@@ -18,7 +18,6 @@ import { buildCodeMirrorCommentWidgets, normalizeLineRange, useInlineCommentCont
import { getLanguageFromExtension } from '@/lib/toolHelpers';
import { useDeviceInfo } from '@/lib/device';
import { useThemeSystem } from '@/contexts/useThemeSystem';
import { generateSyntaxTheme } from '@/lib/theme/syntaxThemeGenerator';
import { createFlexokiCodeMirrorTheme } from '@/lib/codemirror/flexokiTheme';
import { languageByExtension } from '@/lib/codemirror/languageByExtension';
import { useSessionUIStore } from '@/sync/session-ui-store';
@@ -164,7 +163,6 @@ export const PlanView: React.FC<PlanViewProps> = ({ targetPath = null }) => {
const runtimeApis = useRuntimeAPIs();
const { isMobile } = useDeviceInfo();
const { currentTheme } = useThemeSystem();
React.useMemo(() => generateSyntaxTheme(currentTheme), [currentTheme]);
const session = React.useMemo(() => {
if (!currentSessionId) return null;
@@ -1,208 +0,0 @@
import type { Theme } from '@/types/theme';
export function generateSyntaxTheme(theme: Theme) {
const syntax = theme.colors.syntax;
const surface = theme.colors.surface;
return {
'code[class*="language-"]': {
color: syntax.base.foreground,
background: 'transparent',
fontFamily: 'Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace',
fontSize: '1em',
textAlign: 'left' as const,
whiteSpace: 'pre',
wordSpacing: 'normal',
wordBreak: 'normal' as const,
wordWrap: 'normal' as const,
lineHeight: '1.5',
MozTabSize: '4',
OTabSize: '4',
tabSize: '4',
WebkitHyphens: 'none' as const,
MozHyphens: 'none' as const,
msHyphens: 'none' as const,
hyphens: 'none' as const,
},
'pre[class*="language-"]': {
color: syntax.base.foreground,
background: 'transparent',
fontFamily: 'Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace',
fontSize: '1em',
textAlign: 'left' as const,
whiteSpace: 'pre',
wordSpacing: 'normal',
wordBreak: 'normal' as const,
wordWrap: 'normal' as const,
lineHeight: '1.5',
MozTabSize: '4',
OTabSize: '4',
tabSize: '4',
WebkitHyphens: 'none' as const,
MozHyphens: 'none' as const,
msHyphens: 'none' as const,
hyphens: 'none' as const,
padding: '0',
margin: '0',
overflow: 'auto',
},
comment: {
color: syntax.base.comment,
fontStyle: 'italic',
},
prolog: {
color: syntax.base.comment,
},
doctype: {
color: syntax.base.comment,
},
cdata: {
color: syntax.base.comment,
},
punctuation: {
color: syntax.tokens?.punctuation || surface.mutedForeground,
},
property: {
color: syntax.tokens?.variableProperty || syntax.base.variable,
},
tag: {
color: syntax.tokens?.tag || syntax.base.keyword,
},
'attr-name': {
color: syntax.tokens?.tagAttribute || syntax.base.variable,
},
'attr-value': {
color: syntax.tokens?.tagAttributeValue || syntax.base.string,
},
boolean: {
color: syntax.tokens?.boolean || syntax.base.number,
},
number: {
color: syntax.base.number,
},
constant: {
color: syntax.tokens?.constant || syntax.base.number,
},
symbol: {
color: syntax.tokens?.constant || syntax.base.number,
},
string: {
color: syntax.base.string,
},
char: {
color: syntax.base.string,
},
function: {
color: syntax.base.function,
},
builtin: {
color: syntax.tokens?.functionBuiltin || syntax.base.function,
},
'class-name': {
color: syntax.tokens?.className || syntax.base.type,
},
namespace: {
color: syntax.tokens?.namespace || syntax.base.type,
opacity: 0.8,
},
keyword: {
color: syntax.base.keyword,
},
atrule: {
color: syntax.base.keyword,
},
selector: {
color: syntax.base.function,
},
operator: {
color: syntax.base.operator,
},
variable: {
color: syntax.base.variable,
},
regex: {
color: syntax.tokens?.regex || syntax.base.string,
},
url: {
color: syntax.base.function,
textDecoration: 'underline',
},
entity: {
color: syntax.base.function,
cursor: 'help',
},
'.language-css .token.string': {
color: syntax.base.string,
},
'.style .token.string': {
color: syntax.base.string,
},
deleted: {
color: theme.colors.status.error,
backgroundColor: theme.colors.status.errorBackground,
},
inserted: {
color: theme.colors.status.success,
backgroundColor: theme.colors.status.successBackground,
},
title: {
color: theme.colors.primary.base,
fontWeight: 'bold',
},
'code-block': {
color: syntax.base.string,
},
'code-snippet': {
color: syntax.base.string,
},
list: {
color: syntax.base.variable,
},
hr: {
color: surface.mutedForeground,
},
table: {
color: syntax.base.function,
},
blockquote: {
color: surface.mutedForeground,
fontStyle: 'italic',
},
important: {
color: syntax.base.keyword,
fontWeight: 'bold',
},
bold: {
fontWeight: 'bold',
},
italic: {
fontStyle: 'italic',
},
strike: {
textDecoration: 'line-through',
},
decorator: {
color: syntax.tokens?.decorator || syntax.base.function,
},
annotation: {
color: syntax.tokens?.decorator || syntax.base.function,
},
};
}