Improve markdown styling and enable text selection on mobile
- Fix agent chat bottom scroll padding by adding overflow hidden to inputAreaContainer
- Create shared markdown-styles.ts with comprehensive styling for tables, headings, code blocks, blockquotes, and horizontal rules
- Enable text selection in AssistantMessage and AgentThoughtMessage by overriding markdown render rules with selectable={true}
- Path chips remain non-selectable to preserve onPress navigation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
adc56da129
commit
38a4c70aec
@@ -1139,6 +1139,7 @@ const styles = StyleSheet.create(((theme: any) => ({
|
|||||||
minHeight: FOOTER_HEIGHT,
|
minHeight: FOOTER_HEIGHT,
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
|
overflow: "hidden",
|
||||||
},
|
},
|
||||||
inputAreaContent: {
|
inputAreaContent: {
|
||||||
width: "100%",
|
width: "100%",
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import {
|
|||||||
extractReadEntries,
|
extractReadEntries,
|
||||||
} from "@/utils/tool-call-parsers";
|
} from "@/utils/tool-call-parsers";
|
||||||
import { ToolCallSheetProvider } from "./tool-call-sheet";
|
import { ToolCallSheetProvider } from "./tool-call-sheet";
|
||||||
|
import { createMarkdownStyles } from "@/styles/markdown-styles";
|
||||||
|
|
||||||
const MAX_CHAT_WIDTH = 960;
|
const MAX_CHAT_WIDTH = 960;
|
||||||
type PermissionResolvedMessage = Extract<
|
type PermissionResolvedMessage = Extract<
|
||||||
@@ -594,62 +595,7 @@ function PermissionRequestCard({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const markdownStyles = useMemo(
|
const markdownStyles = useMemo(
|
||||||
() => ({
|
() => createMarkdownStyles(theme),
|
||||||
body: {
|
|
||||||
color: theme.colors.foreground,
|
|
||||||
fontSize: theme.fontSize.base,
|
|
||||||
lineHeight: 22,
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
color: theme.colors.foreground,
|
|
||||||
fontSize: theme.fontSize.base,
|
|
||||||
lineHeight: 22,
|
|
||||||
},
|
|
||||||
paragraph: {
|
|
||||||
marginBottom: theme.spacing[1],
|
|
||||||
},
|
|
||||||
strong: {
|
|
||||||
fontWeight: theme.fontWeight.semibold,
|
|
||||||
},
|
|
||||||
bullet_list: {
|
|
||||||
marginBottom: theme.spacing[1],
|
|
||||||
},
|
|
||||||
ordered_list: {
|
|
||||||
marginBottom: theme.spacing[1],
|
|
||||||
},
|
|
||||||
list_item: {
|
|
||||||
flexDirection: "row" as const,
|
|
||||||
marginBottom: theme.spacing[1],
|
|
||||||
},
|
|
||||||
code_inline: {
|
|
||||||
fontFamily: "monospace",
|
|
||||||
backgroundColor: theme.colors.card,
|
|
||||||
paddingHorizontal: theme.spacing[1],
|
|
||||||
paddingVertical: theme.spacing[1],
|
|
||||||
borderRadius: theme.borderRadius.sm,
|
|
||||||
},
|
|
||||||
code_block: {
|
|
||||||
fontFamily: "monospace",
|
|
||||||
backgroundColor: theme.colors.card,
|
|
||||||
padding: theme.spacing[2],
|
|
||||||
borderRadius: theme.borderRadius.md,
|
|
||||||
},
|
|
||||||
fence: {
|
|
||||||
fontFamily: "monospace",
|
|
||||||
backgroundColor: theme.colors.card,
|
|
||||||
padding: theme.spacing[2],
|
|
||||||
borderRadius: theme.borderRadius.md,
|
|
||||||
},
|
|
||||||
blockquote: {
|
|
||||||
borderLeftWidth: theme.borderWidth[1],
|
|
||||||
borderLeftColor: theme.colors.border,
|
|
||||||
paddingLeft: theme.spacing[3],
|
|
||||||
marginBottom: theme.spacing[2],
|
|
||||||
},
|
|
||||||
blockquote_text: {
|
|
||||||
color: theme.colors.mutedForeground,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
[theme]
|
[theme]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
} from "lucide-react-native";
|
} from "lucide-react-native";
|
||||||
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
||||||
import { baseColors, theme } from "@/styles/theme";
|
import { baseColors, theme } from "@/styles/theme";
|
||||||
|
import { createMarkdownStyles, createCompactMarkdownStyles } from "@/styles/markdown-styles";
|
||||||
import { Colors } from "@/constants/theme";
|
import { Colors } from "@/constants/theme";
|
||||||
import * as Clipboard from "expo-clipboard";
|
import * as Clipboard from "expo-clipboard";
|
||||||
import type { TodoEntry, ThoughtStatus } from "@/types/stream";
|
import type { TodoEntry, ThoughtStatus } from "@/types/stream";
|
||||||
@@ -161,27 +162,7 @@ export const assistantMessageStylesheet = StyleSheet.create((theme) => ({
|
|||||||
fontSize: theme.fontSize.xs,
|
fontSize: theme.fontSize.xs,
|
||||||
fontWeight: theme.fontWeight.bold,
|
fontWeight: theme.fontWeight.bold,
|
||||||
},
|
},
|
||||||
// Markdown styles
|
// Used in custom markdownRules for inline code styling
|
||||||
markdownBody: {
|
|
||||||
color: theme.colors.foreground,
|
|
||||||
fontSize: theme.fontSize.base,
|
|
||||||
lineHeight: 24,
|
|
||||||
},
|
|
||||||
markdownText: {
|
|
||||||
color: theme.colors.foreground,
|
|
||||||
fontSize: theme.fontSize.base,
|
|
||||||
lineHeight: 22,
|
|
||||||
},
|
|
||||||
markdownParagraph: {
|
|
||||||
marginTop: 0,
|
|
||||||
marginBottom: theme.spacing[2],
|
|
||||||
},
|
|
||||||
markdownStrong: {
|
|
||||||
fontWeight: theme.fontWeight.bold,
|
|
||||||
},
|
|
||||||
markdownEm: {
|
|
||||||
fontStyle: "italic" as const,
|
|
||||||
},
|
|
||||||
markdownCodeInline: {
|
markdownCodeInline: {
|
||||||
backgroundColor: theme.colors.secondary,
|
backgroundColor: theme.colors.secondary,
|
||||||
color: theme.colors.secondaryForeground,
|
color: theme.colors.secondaryForeground,
|
||||||
@@ -191,47 +172,7 @@ export const assistantMessageStylesheet = StyleSheet.create((theme) => ({
|
|||||||
fontFamily: "monospace",
|
fontFamily: "monospace",
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
},
|
},
|
||||||
markdownCodeBlock: {
|
// Used in custom markdownRules for path chip styling
|
||||||
backgroundColor: theme.colors.secondary,
|
|
||||||
color: theme.colors.secondaryForeground,
|
|
||||||
padding: theme.spacing[3],
|
|
||||||
borderRadius: theme.borderRadius.md,
|
|
||||||
fontFamily: "monospace",
|
|
||||||
fontSize: 13,
|
|
||||||
},
|
|
||||||
markdownFence: {
|
|
||||||
backgroundColor: theme.colors.secondary,
|
|
||||||
borderColor: theme.colors.border,
|
|
||||||
color: theme.colors.secondaryForeground,
|
|
||||||
padding: theme.spacing[3],
|
|
||||||
borderRadius: theme.borderRadius.md,
|
|
||||||
marginVertical: theme.spacing[2],
|
|
||||||
fontFamily: "monospace",
|
|
||||||
fontSize: 13,
|
|
||||||
},
|
|
||||||
markdownLink: {
|
|
||||||
color: theme.colors.primary,
|
|
||||||
textDecorationLine: "underline" as const,
|
|
||||||
},
|
|
||||||
markdownList: {
|
|
||||||
marginBottom: theme.spacing[2],
|
|
||||||
},
|
|
||||||
markdownListItem: {
|
|
||||||
marginBottom: theme.spacing[1],
|
|
||||||
},
|
|
||||||
markdownBlockquote: {
|
|
||||||
backgroundColor: theme.colors.secondary,
|
|
||||||
borderLeftWidth: 4,
|
|
||||||
borderLeftColor: theme.colors.primary,
|
|
||||||
paddingHorizontal: theme.spacing[3],
|
|
||||||
paddingVertical: theme.spacing[2],
|
|
||||||
marginVertical: theme.spacing[2],
|
|
||||||
borderRadius: theme.borderRadius.sm,
|
|
||||||
},
|
|
||||||
markdownBlockquoteText: {
|
|
||||||
color: theme.colors.foreground,
|
|
||||||
fontStyle: "italic" as const,
|
|
||||||
},
|
|
||||||
pathChip: {
|
pathChip: {
|
||||||
backgroundColor: theme.colors.secondary,
|
backgroundColor: theme.colors.secondary,
|
||||||
borderRadius: theme.borderRadius.full,
|
borderRadius: theme.borderRadius.full,
|
||||||
@@ -391,95 +332,75 @@ export const AssistantMessage = memo(function AssistantMessage({
|
|||||||
const lastPathRef = useRef<string | null>(null);
|
const lastPathRef = useRef<string | null>(null);
|
||||||
|
|
||||||
const markdownStyles = useMemo(
|
const markdownStyles = useMemo(
|
||||||
() => ({
|
() => createMarkdownStyles(theme),
|
||||||
body: {
|
|
||||||
color: theme.colors.foreground,
|
|
||||||
fontSize: theme.fontSize.base,
|
|
||||||
lineHeight: 24,
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
color: theme.colors.foreground,
|
|
||||||
},
|
|
||||||
paragraph: {
|
|
||||||
marginTop: 0,
|
|
||||||
marginBottom: theme.spacing[2],
|
|
||||||
},
|
|
||||||
strong: {
|
|
||||||
fontWeight: theme.fontWeight.bold,
|
|
||||||
},
|
|
||||||
em: {
|
|
||||||
fontStyle: "italic" as const,
|
|
||||||
},
|
|
||||||
code_inline: {
|
|
||||||
backgroundColor: theme.colors.secondary,
|
|
||||||
color: theme.colors.secondaryForeground,
|
|
||||||
paddingHorizontal: theme.spacing[2],
|
|
||||||
paddingVertical: 2,
|
|
||||||
borderRadius: theme.borderRadius.sm,
|
|
||||||
fontFamily: "monospace",
|
|
||||||
fontSize: 13,
|
|
||||||
},
|
|
||||||
code_block: {
|
|
||||||
backgroundColor: theme.colors.secondary,
|
|
||||||
color: theme.colors.secondaryForeground,
|
|
||||||
padding: theme.spacing[3],
|
|
||||||
borderRadius: theme.borderRadius.md,
|
|
||||||
fontFamily: "monospace",
|
|
||||||
fontSize: 13,
|
|
||||||
},
|
|
||||||
fence: {
|
|
||||||
backgroundColor: theme.colors.secondary,
|
|
||||||
borderColor: theme.colors.border,
|
|
||||||
color: theme.colors.secondaryForeground,
|
|
||||||
padding: theme.spacing[3],
|
|
||||||
borderRadius: theme.borderRadius.md,
|
|
||||||
marginVertical: theme.spacing[2],
|
|
||||||
fontFamily: "monospace",
|
|
||||||
fontSize: 13,
|
|
||||||
},
|
|
||||||
link: {
|
|
||||||
color: theme.colors.primary,
|
|
||||||
textDecorationLine: "underline" as const,
|
|
||||||
},
|
|
||||||
bullet_list: {
|
|
||||||
marginBottom: theme.spacing[2],
|
|
||||||
},
|
|
||||||
ordered_list: {
|
|
||||||
marginBottom: theme.spacing[2],
|
|
||||||
},
|
|
||||||
list_item: {
|
|
||||||
marginBottom: theme.spacing[1],
|
|
||||||
},
|
|
||||||
blockquote: {
|
|
||||||
backgroundColor: theme.colors.secondary,
|
|
||||||
borderLeftWidth: 4,
|
|
||||||
borderLeftColor: theme.colors.primary,
|
|
||||||
paddingHorizontal: theme.spacing[3],
|
|
||||||
paddingVertical: theme.spacing[2],
|
|
||||||
marginVertical: theme.spacing[2],
|
|
||||||
borderRadius: theme.borderRadius.sm,
|
|
||||||
},
|
|
||||||
blockquote_text: {
|
|
||||||
color: theme.colors.foreground,
|
|
||||||
fontStyle: "italic" as const,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
[theme]
|
[theme]
|
||||||
);
|
);
|
||||||
|
|
||||||
const markdownRules = useMemo(() => {
|
const markdownRules = useMemo(() => {
|
||||||
if (!onInlinePathPress) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
code_inline: (node: any) => {
|
text: (
|
||||||
|
node: any,
|
||||||
|
_children: ReactNode[],
|
||||||
|
_parent: any,
|
||||||
|
styles: any,
|
||||||
|
inheritedStyles: any = {}
|
||||||
|
) => (
|
||||||
|
<Text key={node.key} style={[inheritedStyles, styles.text]} selectable>
|
||||||
|
{node.content}
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
textgroup: (
|
||||||
|
node: any,
|
||||||
|
children: ReactNode[],
|
||||||
|
_parent: any,
|
||||||
|
styles: any,
|
||||||
|
inheritedStyles: any = {}
|
||||||
|
) => (
|
||||||
|
<Text key={node.key} style={[inheritedStyles, styles.textgroup]} selectable>
|
||||||
|
{children}
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
code_block: (
|
||||||
|
node: any,
|
||||||
|
_children: ReactNode[],
|
||||||
|
_parent: any,
|
||||||
|
styles: any,
|
||||||
|
inheritedStyles: any = {}
|
||||||
|
) => (
|
||||||
|
<Text key={node.key} style={[inheritedStyles, styles.code_block]} selectable>
|
||||||
|
{node.content}
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
fence: (
|
||||||
|
node: any,
|
||||||
|
_children: ReactNode[],
|
||||||
|
_parent: any,
|
||||||
|
styles: any,
|
||||||
|
inheritedStyles: any = {}
|
||||||
|
) => (
|
||||||
|
<Text key={node.key} style={[inheritedStyles, styles.fence]} selectable>
|
||||||
|
{node.content}
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
code_inline: (
|
||||||
|
node: any,
|
||||||
|
_children: ReactNode[],
|
||||||
|
_parent: any,
|
||||||
|
_styles: any,
|
||||||
|
inheritedStyles: any = {}
|
||||||
|
) => {
|
||||||
const content = node.content ?? "";
|
const content = node.content ?? "";
|
||||||
const parsed = parseInlinePathToken(content, lastPathRef);
|
const parsed = onInlinePathPress
|
||||||
|
? parseInlinePathToken(content, lastPathRef)
|
||||||
|
: null;
|
||||||
|
|
||||||
if (!parsed) {
|
if (!parsed) {
|
||||||
return (
|
return (
|
||||||
<Text key={node.key} style={assistantMessageStylesheet.markdownCodeInline}>
|
<Text
|
||||||
|
key={node.key}
|
||||||
|
style={[inheritedStyles, assistantMessageStylesheet.markdownCodeInline]}
|
||||||
|
selectable
|
||||||
|
>
|
||||||
{content}
|
{content}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
@@ -489,6 +410,7 @@ export const AssistantMessage = memo(function AssistantMessage({
|
|||||||
<Text
|
<Text
|
||||||
key={node.key}
|
key={node.key}
|
||||||
onPress={() => parsed && onInlinePathPress?.(parsed)}
|
onPress={() => parsed && onInlinePathPress?.(parsed)}
|
||||||
|
selectable={false}
|
||||||
style={[assistantMessageStylesheet.pathChip, assistantMessageStylesheet.pathChipText]}
|
style={[assistantMessageStylesheet.pathChip, assistantMessageStylesheet.pathChipText]}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
@@ -1020,42 +942,7 @@ export function AgentThoughtMessage({ message, status = "ready" }: AgentThoughtM
|
|||||||
const [isExpanded, setIsExpanded] = useState(false);
|
const [isExpanded, setIsExpanded] = useState(false);
|
||||||
const markdownContent = useMemo(() => message?.trim() ?? "", [message]);
|
const markdownContent = useMemo(() => message?.trim() ?? "", [message]);
|
||||||
const markdownStyles = useMemo(
|
const markdownStyles = useMemo(
|
||||||
() => ({
|
() => createCompactMarkdownStyles(theme),
|
||||||
body: {
|
|
||||||
color: theme.colors.foreground,
|
|
||||||
fontSize: theme.fontSize.sm,
|
|
||||||
lineHeight: 20,
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
color: theme.colors.foreground,
|
|
||||||
},
|
|
||||||
paragraph: {
|
|
||||||
marginBottom: theme.spacing[2],
|
|
||||||
},
|
|
||||||
strong: {
|
|
||||||
fontWeight: theme.fontWeight.semibold,
|
|
||||||
},
|
|
||||||
em: {
|
|
||||||
fontStyle: "italic" as const,
|
|
||||||
},
|
|
||||||
code_inline: {
|
|
||||||
backgroundColor: theme.colors.secondary,
|
|
||||||
color: theme.colors.secondaryForeground,
|
|
||||||
paddingHorizontal: theme.spacing[1],
|
|
||||||
paddingVertical: 2,
|
|
||||||
borderRadius: theme.borderRadius.sm,
|
|
||||||
fontFamily: "monospace",
|
|
||||||
fontSize: theme.fontSize.xs,
|
|
||||||
},
|
|
||||||
code_block: {
|
|
||||||
backgroundColor: theme.colors.secondary,
|
|
||||||
color: theme.colors.secondaryForeground,
|
|
||||||
padding: theme.spacing[3],
|
|
||||||
borderRadius: theme.borderRadius.md,
|
|
||||||
fontFamily: "monospace",
|
|
||||||
fontSize: theme.fontSize.sm,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
[theme]
|
[theme]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1063,12 +950,76 @@ export function AgentThoughtMessage({ message, status = "ready" }: AgentThoughtM
|
|||||||
setIsExpanded((prev) => !prev);
|
setIsExpanded((prev) => !prev);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const markdownRules = useMemo(() => {
|
||||||
|
return {
|
||||||
|
text: (
|
||||||
|
node: any,
|
||||||
|
_children: ReactNode[],
|
||||||
|
_parent: any,
|
||||||
|
styles: any,
|
||||||
|
inheritedStyles: any = {}
|
||||||
|
) => (
|
||||||
|
<Text key={node.key} style={[inheritedStyles, styles.text]} selectable>
|
||||||
|
{node.content}
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
textgroup: (
|
||||||
|
node: any,
|
||||||
|
children: ReactNode[],
|
||||||
|
_parent: any,
|
||||||
|
styles: any,
|
||||||
|
inheritedStyles: any = {}
|
||||||
|
) => (
|
||||||
|
<Text key={node.key} style={[inheritedStyles, styles.textgroup]} selectable>
|
||||||
|
{children}
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
code_block: (
|
||||||
|
node: any,
|
||||||
|
_children: ReactNode[],
|
||||||
|
_parent: any,
|
||||||
|
styles: any,
|
||||||
|
inheritedStyles: any = {}
|
||||||
|
) => (
|
||||||
|
<Text key={node.key} style={[inheritedStyles, styles.code_block]} selectable>
|
||||||
|
{node.content}
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
fence: (
|
||||||
|
node: any,
|
||||||
|
_children: ReactNode[],
|
||||||
|
_parent: any,
|
||||||
|
styles: any,
|
||||||
|
inheritedStyles: any = {}
|
||||||
|
) => (
|
||||||
|
<Text key={node.key} style={[inheritedStyles, styles.fence]} selectable>
|
||||||
|
{node.content}
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
code_inline: (
|
||||||
|
node: any,
|
||||||
|
_children: ReactNode[],
|
||||||
|
_parent: any,
|
||||||
|
styles: any,
|
||||||
|
inheritedStyles: any = {}
|
||||||
|
) => (
|
||||||
|
<Text key={node.key} style={[inheritedStyles, styles.code_inline]} selectable>
|
||||||
|
{node.content}
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const renderDetails = useCallback(() => {
|
const renderDetails = useCallback(() => {
|
||||||
if (!markdownContent) {
|
if (!markdownContent) {
|
||||||
return <Text style={agentThoughtStylesheet.emptyText}>No captured thinking</Text>;
|
return <Text style={agentThoughtStylesheet.emptyText}>No captured thinking</Text>;
|
||||||
}
|
}
|
||||||
return <Markdown style={markdownStyles}>{markdownContent}</Markdown>;
|
return (
|
||||||
}, [markdownContent, markdownStyles]);
|
<Markdown style={markdownStyles} rules={markdownRules}>
|
||||||
|
{markdownContent}
|
||||||
|
</Markdown>
|
||||||
|
);
|
||||||
|
}, [markdownContent, markdownRules, markdownStyles]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExpandableBadge
|
<ExpandableBadge
|
||||||
|
|||||||
@@ -0,0 +1,365 @@
|
|||||||
|
import type { Theme } from "./theme";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates comprehensive markdown styles for react-native-markdown-display.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* const markdownStyles = useMemo(() => createMarkdownStyles(theme), [theme]);
|
||||||
|
* <Markdown style={markdownStyles}>{content}</Markdown>
|
||||||
|
*/
|
||||||
|
export function createMarkdownStyles(theme: Theme) {
|
||||||
|
return {
|
||||||
|
// =========================================================================
|
||||||
|
// BASE STYLES
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
body: {
|
||||||
|
color: theme.colors.foreground,
|
||||||
|
fontSize: theme.fontSize.base,
|
||||||
|
lineHeight: 22,
|
||||||
|
},
|
||||||
|
|
||||||
|
text: {
|
||||||
|
color: theme.colors.foreground,
|
||||||
|
},
|
||||||
|
|
||||||
|
paragraph: {
|
||||||
|
marginTop: 0,
|
||||||
|
marginBottom: theme.spacing[3],
|
||||||
|
flexWrap: "wrap" as const,
|
||||||
|
flexDirection: "row" as const,
|
||||||
|
alignItems: "flex-start" as const,
|
||||||
|
justifyContent: "flex-start" as const,
|
||||||
|
},
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// HEADINGS
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
heading1: {
|
||||||
|
fontSize: theme.fontSize["3xl"],
|
||||||
|
fontWeight: theme.fontWeight.bold,
|
||||||
|
color: theme.colors.foreground,
|
||||||
|
marginTop: theme.spacing[6],
|
||||||
|
marginBottom: theme.spacing[3],
|
||||||
|
lineHeight: 32,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: theme.colors.border,
|
||||||
|
paddingBottom: theme.spacing[2],
|
||||||
|
},
|
||||||
|
|
||||||
|
heading2: {
|
||||||
|
fontSize: theme.fontSize["2xl"],
|
||||||
|
fontWeight: theme.fontWeight.bold,
|
||||||
|
color: theme.colors.foreground,
|
||||||
|
marginTop: theme.spacing[6],
|
||||||
|
marginBottom: theme.spacing[3],
|
||||||
|
lineHeight: 28,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: theme.colors.border,
|
||||||
|
paddingBottom: theme.spacing[2],
|
||||||
|
},
|
||||||
|
|
||||||
|
heading3: {
|
||||||
|
fontSize: theme.fontSize.xl,
|
||||||
|
fontWeight: theme.fontWeight.semibold,
|
||||||
|
color: theme.colors.foreground,
|
||||||
|
marginTop: theme.spacing[4],
|
||||||
|
marginBottom: theme.spacing[2],
|
||||||
|
lineHeight: 26,
|
||||||
|
},
|
||||||
|
|
||||||
|
heading4: {
|
||||||
|
fontSize: theme.fontSize.lg,
|
||||||
|
fontWeight: theme.fontWeight.semibold,
|
||||||
|
color: theme.colors.foreground,
|
||||||
|
marginTop: theme.spacing[4],
|
||||||
|
marginBottom: theme.spacing[2],
|
||||||
|
lineHeight: 24,
|
||||||
|
},
|
||||||
|
|
||||||
|
heading5: {
|
||||||
|
fontSize: theme.fontSize.base,
|
||||||
|
fontWeight: theme.fontWeight.semibold,
|
||||||
|
color: theme.colors.foreground,
|
||||||
|
marginTop: theme.spacing[3],
|
||||||
|
marginBottom: theme.spacing[1],
|
||||||
|
lineHeight: 22,
|
||||||
|
},
|
||||||
|
|
||||||
|
heading6: {
|
||||||
|
fontSize: theme.fontSize.base,
|
||||||
|
fontWeight: theme.fontWeight.semibold,
|
||||||
|
color: theme.colors.mutedForeground,
|
||||||
|
marginTop: theme.spacing[3],
|
||||||
|
marginBottom: theme.spacing[1],
|
||||||
|
lineHeight: 20,
|
||||||
|
textTransform: "uppercase" as const,
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
},
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// TEXT FORMATTING
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
strong: {
|
||||||
|
fontWeight: theme.fontWeight.bold,
|
||||||
|
},
|
||||||
|
|
||||||
|
em: {
|
||||||
|
fontStyle: "italic" as const,
|
||||||
|
},
|
||||||
|
|
||||||
|
s: {
|
||||||
|
textDecorationLine: "line-through" as const,
|
||||||
|
color: theme.colors.mutedForeground,
|
||||||
|
},
|
||||||
|
|
||||||
|
link: {
|
||||||
|
color: theme.colors.primary,
|
||||||
|
textDecorationLine: "underline" as const,
|
||||||
|
},
|
||||||
|
|
||||||
|
blocklink: {
|
||||||
|
color: theme.colors.primary,
|
||||||
|
textDecorationLine: "underline" as const,
|
||||||
|
},
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// CODE
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
code_inline: {
|
||||||
|
backgroundColor: theme.colors.secondary,
|
||||||
|
color: theme.colors.secondaryForeground,
|
||||||
|
paddingHorizontal: theme.spacing[1],
|
||||||
|
paddingVertical: 2,
|
||||||
|
borderRadius: theme.borderRadius.sm,
|
||||||
|
fontFamily: "monospace",
|
||||||
|
fontSize: theme.fontSize.sm,
|
||||||
|
},
|
||||||
|
|
||||||
|
code_block: {
|
||||||
|
backgroundColor: theme.colors.secondary,
|
||||||
|
color: theme.colors.secondaryForeground,
|
||||||
|
padding: theme.spacing[3],
|
||||||
|
borderRadius: theme.borderRadius.md,
|
||||||
|
fontFamily: "monospace",
|
||||||
|
fontSize: theme.fontSize.sm,
|
||||||
|
marginVertical: theme.spacing[2],
|
||||||
|
},
|
||||||
|
|
||||||
|
fence: {
|
||||||
|
backgroundColor: theme.colors.secondary,
|
||||||
|
color: theme.colors.secondaryForeground,
|
||||||
|
padding: theme.spacing[3],
|
||||||
|
borderRadius: theme.borderRadius.md,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: theme.colors.border,
|
||||||
|
fontFamily: "monospace",
|
||||||
|
fontSize: theme.fontSize.sm,
|
||||||
|
marginVertical: theme.spacing[3],
|
||||||
|
},
|
||||||
|
|
||||||
|
pre: {
|
||||||
|
marginVertical: theme.spacing[2],
|
||||||
|
},
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// TABLES
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
table: {
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: theme.colors.border,
|
||||||
|
borderRadius: theme.borderRadius.md,
|
||||||
|
marginVertical: theme.spacing[3],
|
||||||
|
},
|
||||||
|
|
||||||
|
thead: {
|
||||||
|
backgroundColor: theme.colors.secondary,
|
||||||
|
},
|
||||||
|
|
||||||
|
tbody: {},
|
||||||
|
|
||||||
|
th: {
|
||||||
|
padding: theme.spacing[2],
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderRightWidth: 1,
|
||||||
|
borderColor: theme.colors.border,
|
||||||
|
backgroundColor: theme.colors.secondary,
|
||||||
|
fontWeight: theme.fontWeight.semibold,
|
||||||
|
color: theme.colors.foreground,
|
||||||
|
fontSize: theme.fontSize.sm,
|
||||||
|
textAlign: "left" as const,
|
||||||
|
},
|
||||||
|
|
||||||
|
tr: {
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderColor: theme.colors.border,
|
||||||
|
flexDirection: "row" as const,
|
||||||
|
},
|
||||||
|
|
||||||
|
td: {
|
||||||
|
padding: theme.spacing[2],
|
||||||
|
borderRightWidth: 1,
|
||||||
|
borderColor: theme.colors.border,
|
||||||
|
color: theme.colors.foreground,
|
||||||
|
fontSize: theme.fontSize.sm,
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// LISTS
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
bullet_list: {
|
||||||
|
marginVertical: theme.spacing[2],
|
||||||
|
paddingLeft: theme.spacing[1],
|
||||||
|
},
|
||||||
|
|
||||||
|
ordered_list: {
|
||||||
|
marginVertical: theme.spacing[2],
|
||||||
|
paddingLeft: theme.spacing[1],
|
||||||
|
},
|
||||||
|
|
||||||
|
list_item: {
|
||||||
|
marginBottom: theme.spacing[1],
|
||||||
|
flexDirection: "row" as const,
|
||||||
|
alignItems: "flex-start" as const,
|
||||||
|
},
|
||||||
|
|
||||||
|
bullet_list_content: {
|
||||||
|
flex: 1,
|
||||||
|
flexWrap: "wrap" as const,
|
||||||
|
},
|
||||||
|
|
||||||
|
ordered_list_content: {
|
||||||
|
flex: 1,
|
||||||
|
flexWrap: "wrap" as const,
|
||||||
|
},
|
||||||
|
|
||||||
|
bullet_list_icon: {
|
||||||
|
color: theme.colors.mutedForeground,
|
||||||
|
marginRight: theme.spacing[2],
|
||||||
|
fontSize: theme.fontSize.base,
|
||||||
|
lineHeight: 22,
|
||||||
|
},
|
||||||
|
|
||||||
|
ordered_list_icon: {
|
||||||
|
color: theme.colors.mutedForeground,
|
||||||
|
marginRight: theme.spacing[2],
|
||||||
|
fontSize: theme.fontSize.base,
|
||||||
|
fontWeight: theme.fontWeight.semibold,
|
||||||
|
lineHeight: 22,
|
||||||
|
minWidth: 20,
|
||||||
|
},
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// BLOCKQUOTE
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
blockquote: {
|
||||||
|
backgroundColor: theme.colors.muted,
|
||||||
|
borderLeftWidth: 4,
|
||||||
|
borderLeftColor: theme.colors.primary,
|
||||||
|
paddingHorizontal: theme.spacing[4],
|
||||||
|
paddingVertical: theme.spacing[3],
|
||||||
|
marginVertical: theme.spacing[3],
|
||||||
|
borderRadius: theme.borderRadius.md,
|
||||||
|
},
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// HORIZONTAL RULE
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
hr: {
|
||||||
|
backgroundColor: theme.colors.border,
|
||||||
|
height: 1,
|
||||||
|
marginVertical: theme.spacing[6],
|
||||||
|
},
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// IMAGES
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
image: {
|
||||||
|
borderRadius: theme.borderRadius.md,
|
||||||
|
marginVertical: theme.spacing[2],
|
||||||
|
},
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// BREAKS
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
hardbreak: {
|
||||||
|
height: theme.spacing[2],
|
||||||
|
},
|
||||||
|
|
||||||
|
softbreak: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a smaller variant of markdown styles for compact UI elements
|
||||||
|
* like thought bubbles, tooltips, or side panels.
|
||||||
|
*/
|
||||||
|
export function createCompactMarkdownStyles(theme: Theme) {
|
||||||
|
const baseStyles = createMarkdownStyles(theme);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...baseStyles,
|
||||||
|
|
||||||
|
body: {
|
||||||
|
...baseStyles.body,
|
||||||
|
fontSize: theme.fontSize.sm,
|
||||||
|
lineHeight: 20,
|
||||||
|
},
|
||||||
|
|
||||||
|
heading1: {
|
||||||
|
...baseStyles.heading1,
|
||||||
|
fontSize: theme.fontSize.xl,
|
||||||
|
marginTop: theme.spacing[4],
|
||||||
|
marginBottom: theme.spacing[2],
|
||||||
|
lineHeight: 26,
|
||||||
|
},
|
||||||
|
|
||||||
|
heading2: {
|
||||||
|
...baseStyles.heading2,
|
||||||
|
fontSize: theme.fontSize.lg,
|
||||||
|
marginTop: theme.spacing[3],
|
||||||
|
marginBottom: theme.spacing[2],
|
||||||
|
lineHeight: 24,
|
||||||
|
},
|
||||||
|
|
||||||
|
heading3: {
|
||||||
|
...baseStyles.heading3,
|
||||||
|
fontSize: theme.fontSize.base,
|
||||||
|
marginTop: theme.spacing[3],
|
||||||
|
marginBottom: theme.spacing[1],
|
||||||
|
lineHeight: 22,
|
||||||
|
},
|
||||||
|
|
||||||
|
paragraph: {
|
||||||
|
...baseStyles.paragraph,
|
||||||
|
marginBottom: theme.spacing[2],
|
||||||
|
},
|
||||||
|
|
||||||
|
code_inline: {
|
||||||
|
...baseStyles.code_inline,
|
||||||
|
fontSize: theme.fontSize.xs,
|
||||||
|
},
|
||||||
|
|
||||||
|
code_block: {
|
||||||
|
...baseStyles.code_block,
|
||||||
|
fontSize: theme.fontSize.xs,
|
||||||
|
padding: theme.spacing[2],
|
||||||
|
},
|
||||||
|
|
||||||
|
fence: {
|
||||||
|
...baseStyles.fence,
|
||||||
|
fontSize: theme.fontSize.xs,
|
||||||
|
padding: theme.spacing[2],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user