rn-shiki 0.0.36 → 0.0.37-0

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rn-shiki",
3
3
  "type": "module",
4
- "version": "0.0.36",
4
+ "version": "0.0.37-0",
5
5
  "description": "Shiki syntax highlighter for React Native.",
6
6
  "author": "Ryan Skinner <hello@ryanskinner.com>",
7
7
  "license": "MIT",
@@ -1,6 +1,7 @@
1
1
  import type { SyntaxHighlighterProps } from 'src/types/shiki'
2
2
  import React from 'react'
3
3
  import { Platform, ScrollView, StyleSheet, Text, View } from 'react-native'
4
+ import { convertShikiTheme } from 'src/utils/style-transformer'
4
5
  import { useSyntaxHighlighter } from '../../hooks/useSyntaxHighlighter'
5
6
  import SyntaxLine from './SyntaxLine'
6
7
 
@@ -32,7 +33,9 @@ const styles = StyleSheet.create({
32
33
  },
33
34
  })
34
35
 
35
- function SyntaxHighlighter({ text, language, languageId, theme, fontSize = 14 }: SyntaxHighlighterProps & { fontSize?: number }) {
36
+ function SyntaxHighlighter({ text, language, languageId, theme, fontSize = 14 }: SyntaxHighlighterProps) {
37
+ // Convert theme on mount
38
+ const themeStyles = React.useMemo(() => convertShikiTheme(theme ?? {}), [theme])
36
39
  const { tokens, error, isLoading } = useSyntaxHighlighter({
37
40
  text,
38
41
  language,
@@ -75,13 +78,15 @@ function SyntaxHighlighter({ text, language, languageId, theme, fontSize = 14 }:
75
78
  }
76
79
 
77
80
  return (
78
- <ScrollView horizontal showsHorizontalScrollIndicator={Platform.OS !== 'web'} style={styles.container} contentContainerStyle={styles.scrollContent}>
79
- <View style={styles.contentContainer} onStartShouldSetResponder={() => true}>
80
- {tokens.map((line, lineIndex) => (
81
- <SyntaxLine key={`line-${lineIndex}`} line={line} fontSize={fontSize} />
82
- ))}
83
- </View>
84
- </ScrollView>
81
+ <View style={[styles.container, { backgroundColor: themeStyles.backgroundColor }]}>
82
+ <ScrollView horizontal showsHorizontalScrollIndicator={Platform.OS !== 'web'} contentContainerStyle={{ flexGrow: 1 }}>
83
+ <View style={styles.contentContainer}>
84
+ {tokens.map((line, lineIndex) => (
85
+ <SyntaxLine key={`line-${lineIndex}`} line={line} fontSize={fontSize} isLastLine={lineIndex === tokens.length - 1} themeStyles={themeStyles} />
86
+ ))}
87
+ </View>
88
+ </ScrollView>
89
+ </View>
85
90
  )
86
91
  }
87
92
 
@@ -1,3 +1,4 @@
1
+ import type { RNTokenStyle } from 'src/utils/style-transformer'
1
2
  import type { TokenType } from '../../types'
2
3
  import React from 'react'
3
4
  import { Platform, StyleSheet, Text, View } from 'react-native'
@@ -21,10 +22,16 @@ const styles = StyleSheet.create({
21
22
  interface SyntaxLineProps {
22
23
  line: TokenType[]
23
24
  fontSize?: number
25
+ isLastLine?: boolean
26
+ themeStyles: {
27
+ backgroundColor: string
28
+ defaultColor: string
29
+ styles: Record<string, RNTokenStyle>
30
+ }
24
31
  }
25
32
 
26
- function SyntaxLine({ line, fontSize = 14 }: SyntaxLineProps) {
27
- const lineHeight = fontSize * 1.5
33
+ function SyntaxLine({ line, fontSize = 14, isLastLine = false, themeStyles }: SyntaxLineProps) {
34
+ const lineHeight = Math.floor(fontSize * 1.5)
28
35
 
29
36
  return (
30
37
  <View
@@ -32,26 +39,32 @@ function SyntaxLine({ line, fontSize = 14 }: SyntaxLineProps) {
32
39
  styles.lineContainer,
33
40
  {
34
41
  minHeight: lineHeight,
42
+ paddingBottom: !isLastLine ? 4 : 0,
35
43
  },
36
44
  ]}
37
45
  >
38
46
  {line.map((token, tokenIndex) => {
39
47
  const content = token.content.replace(/ /g, '\u00A0')
48
+ // Use converted theme styles
49
+ const tokenStyle = token.color ? { color: token.color } : { color: themeStyles.defaultColor }
50
+
40
51
  return (
41
52
  <Text
42
53
  key={`${tokenIndex}-${content.slice(0, 8)}`}
43
54
  style={[
44
55
  styles.token,
45
56
  {
46
- color: token.color || '#FFFFFF',
57
+ ...tokenStyle,
47
58
  fontFamily: monospaceFont,
48
59
  fontSize,
49
60
  fontStyle: token.fontStyle as 'normal' | 'italic',
50
61
  fontWeight: token.fontWeight as 'normal' | 'bold',
51
62
  lineHeight,
63
+ height: lineHeight,
64
+ textAlignVertical: 'center',
52
65
  ...(Platform.OS === 'ios'
53
66
  ? {
54
- paddingTop: 1,
67
+ paddingTop: 2,
55
68
  }
56
69
  : {}),
57
70
  },
@@ -0,0 +1,71 @@
1
+ import type { TextStyle } from 'react-native'
2
+ import type { ThemeRegistrationAny } from 'shiki'
3
+
4
+ export interface RNTokenStyle {
5
+ color?: string
6
+ backgroundColor?: string
7
+ fontWeight?: TextStyle['fontWeight']
8
+ fontStyle?: TextStyle['fontStyle']
9
+ }
10
+
11
+ const ALLOWED_STYLE_PROPERTIES = {
12
+ 'color': true,
13
+ 'background': true,
14
+ 'background-color': true,
15
+ 'font-weight': true,
16
+ 'font-style': true,
17
+ } as const
18
+
19
+ function transformValue(key: string, value: string): any {
20
+ switch (key) {
21
+ case 'background':
22
+ case 'background-color':
23
+ return ['backgroundColor', value]
24
+ case 'font-weight':
25
+ return ['fontWeight', value === 'bold' ? 'bold' : 'normal']
26
+ case 'font-style':
27
+ return ['fontStyle', value === 'italic' ? 'italic' : 'normal']
28
+ default:
29
+ return [key, value]
30
+ }
31
+ }
32
+
33
+ export function convertShikiTheme(theme: ThemeRegistrationAny): {
34
+ backgroundColor: string
35
+ defaultColor: string
36
+ styles: Record<string, RNTokenStyle>
37
+ } {
38
+ // Extract theme colors
39
+ const backgroundColor = theme.bg || '#1e1e1e'
40
+ const defaultColor = theme.fg || '#d4d4d4'
41
+
42
+ // Convert token colors
43
+ const styles: Record<string, RNTokenStyle> = {}
44
+
45
+ theme.settings?.forEach((setting) => {
46
+ if (!setting.scope || !setting.settings)
47
+ return
48
+
49
+ const scopes = Array.isArray(setting.scope) ? setting.scope : [setting.scope]
50
+ const style: RNTokenStyle = {}
51
+
52
+ // Convert CSS-style properties to RN style properties
53
+ Object.entries(setting.settings).forEach(([key, value]) => {
54
+ if (ALLOWED_STYLE_PROPERTIES[key as keyof typeof ALLOWED_STYLE_PROPERTIES]) {
55
+ const [rnKey, rnValue] = transformValue(key, value as string)
56
+ style[rnKey as keyof RNTokenStyle] = rnValue
57
+ }
58
+ })
59
+
60
+ // Apply style to each scope
61
+ scopes.forEach((scope) => {
62
+ styles[scope] = style
63
+ })
64
+ })
65
+
66
+ return {
67
+ backgroundColor,
68
+ defaultColor,
69
+ styles,
70
+ }
71
+ }