import { Platform, processColor, StyleSheet } from 'react-native' import { type MarkdownStyle } from './types' const styleCache = new WeakMap() const colors = { background: 'rgb(242, 240, 229)', border: 'rgb(183, 181, 172)', link: 'rgb(160, 47, 111)', user: 'rgb(102, 128, 11)', channel: 'rgb(32, 94, 166)', command: 'rgb(94, 64, 157)', } const fonts = { mono: Platform.select({ ios: 'Menlo', android: 'monospace', }), } const defaultStyles: MarkdownStyle = { heading1: { fontSize: 24, fontWeight: '600', }, heading2: { fontSize: 20, fontWeight: '600', }, heading3: { fontSize: 18, fontWeight: '600', }, code: { fontFamily: fonts.mono, fontSize: 14, backgroundColor: colors.background, }, codeBlock: { fontFamily: fonts.mono, fontSize: 14, backgroundColor: colors.background, borderRadius: 6, padding: 12, }, link: { color: colors.link, }, image: { borderRadius: 6, }, blockquote: { backgroundColor: colors.background, padding: 6, borderRadius: 6, borderLeftWidth: 6, borderColor: colors.border, }, listBullet: { color: colors.border, }, spoiler: { backgroundColor: colors.link, borderRadius: 4, }, mentionUser: { color: colors.user, fontWeight: '600', }, mentionChannel: { color: colors.channel, fontWeight: '600', }, mentionCommand: { color: colors.command, fontWeight: '600', fontFamily: fonts.mono, }, tableHeaderRow: { backgroundColor: colors.background, }, tableHeaderCell: { fontWeight: '600', }, tableCell: { borderWidth: StyleSheet.hairlineWidth, borderColor: colors.border, padding: 6, }, thematicBreak: { backgroundColor: colors.border, height: StyleSheet.hairlineWidth, marginVertical: 12, }, } function normalizeColorValues( style: Record, ): Record { const result: Record = {} for (const [key, value] of Object.entries(style)) { if (key.toLowerCase().includes('color')) { if (!value) { continue } result[key] = processColor(value as Parameters[0]) continue } if (typeof value === 'object' && value !== null && !Array.isArray(value)) { result[key] = normalizeColorValues(value as Record) } else { result[key] = value } } return result } function mergeStyles( defaults: Record, overrides: Record, ): Record { const result: Record = { ...defaults, } for (const [key, value] of Object.entries(overrides)) { if ( typeof value === 'object' && value !== null && !Array.isArray(value) && typeof result[key] === 'object' && result[key] !== null ) { result[key] = mergeStyles( result[key] as Record, value as Record, ) } else { result[key] = value } } return result } export function normalizeMarkdownStyle(userStyle?: MarkdownStyle): string { if (!userStyle) { const cached = styleCache.get(defaultStyles) if (cached) { return cached } const serialized = JSON.stringify( normalizeColorValues(defaultStyles as unknown as Record), ) styleCache.set(defaultStyles, serialized) return serialized } const cached = styleCache.get(userStyle) if (cached) { return cached } const merged = mergeStyles( defaultStyles as unknown as Record, userStyle as unknown as Record, ) const normalized = normalizeColorValues(merged) const serialized = JSON.stringify(normalized) styleCache.set(userStyle, serialized) return serialized }