rn-shiki 0.0.37-24 → 0.0.37-26

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.37-24",
4
+ "version": "0.0.37-26",
5
5
  "description": "Shiki syntax highlighter for React Native.",
6
6
  "author": "Ryan Skinner <hello@ryanskinner.com>",
7
7
  "license": "MIT",
@@ -1,6 +1,5 @@
1
- import type { ColorValue, StyleProp, TextStyle, ViewStyle } from 'react-native'
1
+ import type { ColorValue, StyleProp, TextStyle } from 'react-native'
2
2
  import type { SyntaxHighlighterProps } from 'src/types/shiki'
3
- import type { ReactStyle } from '../../utils/style-transformer'
4
3
  import React, { useMemo } from 'react'
5
4
  import { Platform, ScrollView, StyleSheet, Text, View } from 'react-native'
6
5
  import { useSyntaxHighlighter } from '../../hooks/useSyntaxHighlighter'
@@ -12,14 +11,30 @@ const monospaceFont = Platform.select({
12
11
  default: 'monospace',
13
12
  })
14
13
 
14
+ interface TokenStyle extends Omit<TextStyle, 'color' | 'fontWeight'> {
15
+ color?: ColorValue
16
+ fontStyle?: 'normal' | 'italic'
17
+ fontWeight?: FontWeight
18
+ fontFamily?: string
19
+ }
20
+
15
21
  interface Styles {
16
- scrollView: ViewStyle
17
- lineContainer: ViewStyle
18
- token: TextStyle
19
- container: ViewStyle
22
+ [key: string]: TokenStyle
23
+ }
24
+
25
+ type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'
26
+
27
+ interface ThemeColors {
28
+ 'editor.background'?: string
29
+ 'editor.foreground'?: string
30
+ }
31
+
32
+ interface Theme {
33
+ colors?: ThemeColors
34
+ tokenColors?: any[]
20
35
  }
21
36
 
22
- const baseStyles = StyleSheet.create<Styles>({
37
+ const baseStyles = StyleSheet.create({
23
38
  scrollView: {
24
39
  flex: 1,
25
40
  minHeight: 20,
@@ -27,29 +42,39 @@ const baseStyles = StyleSheet.create<Styles>({
27
42
  lineContainer: {
28
43
  flexDirection: 'row',
29
44
  flexWrap: 'wrap',
45
+ paddingHorizontal: 16,
46
+ paddingVertical: 8,
30
47
  },
31
48
  token: {
32
49
  fontFamily: monospaceFont,
50
+ fontSize: 14,
33
51
  },
34
52
  container: {
35
53
  flex: 1,
36
54
  },
37
55
  })
38
56
 
39
- type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'
40
-
41
- interface TokenStyle extends Omit<TextStyle, 'color' | 'fontWeight'> {
42
- color?: ColorValue
43
- fontStyle?: 'normal' | 'italic'
44
- fontWeight?: FontWeight
45
- fontFamily?: string
46
- }
47
-
48
- const SyntaxHighlighter: React.FC<SyntaxHighlighterProps> = ({ text, language, languageId, theme }) => {
57
+ const SyntaxHighlighter: React.FC<SyntaxHighlighterProps> = ({
58
+ text,
59
+ language,
60
+ languageId,
61
+ theme,
62
+ fontSize = 14,
63
+ }) => {
49
64
  const stylesheet = useMemo(() => {
50
- const styles = getRNStylesFromShikiStyle(theme as ReactStyle)
51
- return StyleSheet.create(styles)
52
- }, [theme])
65
+ const styles = getRNStylesFromShikiStyle(theme as Theme)
66
+ return StyleSheet.create<Styles>({
67
+ ...styles,
68
+ editor: {
69
+ backgroundColor: (theme as Theme).colors?.['editor.background'] || '#1e1e1e',
70
+ },
71
+ token: {
72
+ ...baseStyles.token,
73
+ fontSize,
74
+ color: (theme as Theme).colors?.['editor.foreground'] || '#d4d4d4',
75
+ },
76
+ })
77
+ }, [theme, fontSize])
53
78
 
54
79
  const { tokens } = useSyntaxHighlighter({
55
80
  text,
@@ -59,19 +84,17 @@ const SyntaxHighlighter: React.FC<SyntaxHighlighterProps> = ({ text, language, l
59
84
  })
60
85
 
61
86
  const renderToken = (token: any, index: number, lineIndex: number) => {
62
- const styles: StyleProp<TokenStyle>[] = [baseStyles.token]
63
-
64
- if (stylesheet.base) {
65
- styles.push(stylesheet.base as unknown as TokenStyle)
66
- }
87
+ const styles: StyleProp<TokenStyle>[] = [stylesheet.token]
67
88
 
89
+ // Add token-specific styles from stylesheet based on scope
68
90
  const tokenScopes = Array.isArray(token.scope) ? token.scope : [token.scope]
69
91
  tokenScopes.forEach((scope: string) => {
70
92
  if (stylesheet[scope]) {
71
- styles.push(stylesheet[scope] as unknown as TokenStyle)
93
+ styles.push(stylesheet[scope])
72
94
  }
73
95
  })
74
96
 
97
+ // Add token-specific styles from the token itself
75
98
  const tokenStyles: TokenStyle = {}
76
99
  if (token.color) {
77
100
  tokenStyles.color = token.color
@@ -87,10 +110,10 @@ const SyntaxHighlighter: React.FC<SyntaxHighlighterProps> = ({ text, language, l
87
110
  styles.push(tokenStyles)
88
111
  }
89
112
 
90
- const combinedStyles = StyleSheet.flatten(styles)
113
+ const finalStyle = StyleSheet.flatten(styles)
91
114
 
92
115
  return (
93
- <Text key={`${lineIndex}-${index}`} style={combinedStyles}>
116
+ <Text key={`${lineIndex}-${index}`} style={finalStyle}>
94
117
  {token.content.replace(/ /g, '\u00A0')}
95
118
  </Text>
96
119
  )
@@ -103,10 +126,13 @@ const SyntaxHighlighter: React.FC<SyntaxHighlighterProps> = ({ text, language, l
103
126
  )
104
127
 
105
128
  return (
106
- <ScrollView horizontal showsHorizontalScrollIndicator={Platform.OS !== 'web'} style={[baseStyles.scrollView, stylesheet.base as unknown as TokenStyle]}>
107
- <View style={baseStyles.container} onStartShouldSetResponder={() => true}>
108
- {tokens.map((line, index) => renderLine(line, index))}
109
- </View>
129
+ <ScrollView
130
+ horizontal
131
+ showsHorizontalScrollIndicator={Platform.OS !== 'web'}
132
+ style={[baseStyles.scrollView, stylesheet.editor]}
133
+ contentContainerStyle={baseStyles.container}
134
+ >
135
+ {tokens.map((line, index) => renderLine(line, index))}
110
136
  </ScrollView>
111
137
  )
112
138
  }
@@ -1,5 +1,4 @@
1
1
  import type { TextStyle } from 'react-native'
2
- import transform from 'css-to-react-native'
3
2
 
4
3
  export interface HighlighterStyleSheet {
5
4
  [key: string]: TextStyle
@@ -9,7 +8,15 @@ export type ReactStyle = Record<string, TextStyle>
9
8
 
10
9
  // Helper function to convert hex color to RGB
11
10
  function hexToRgb(hex: string): { r: number, g: number, b: number } | null {
12
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
11
+ // Remove # if present
12
+ hex = hex.replace(/^#/, '')
13
+
14
+ // Handle both 3 and 6 digit hex
15
+ if (hex.length === 3) {
16
+ hex = `${hex[0]}${hex[0]}${hex[1]}${hex[1]}${hex[2]}${hex[2]}`
17
+ }
18
+
19
+ const result = /^([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
13
20
  return result
14
21
  ? {
15
22
  r: Number.parseInt(result[1]!, 16),
@@ -19,18 +26,25 @@ function hexToRgb(hex: string): { r: number, g: number, b: number } | null {
19
26
  : null
20
27
  }
21
28
 
22
- // Helper function to normalize color values
23
29
  function normalizeColor(color: string | undefined): string {
24
- if (!color)
30
+ if (!color) {
25
31
  return 'transparent'
32
+ }
26
33
 
27
- // Type guard to ensure color is string before we call string methods
28
- if (typeof color === 'string' && color.startsWith('#')) {
34
+ // Handle hex colors
35
+ if (color.startsWith('#')) {
29
36
  const rgb = hexToRgb(color)
30
37
  if (rgb) {
31
38
  return `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`
32
39
  }
33
40
  }
41
+
42
+ // Handle rgb/rgba
43
+ if (color.startsWith('rgb')) {
44
+ return color
45
+ }
46
+
47
+ // Handle named colors
34
48
  return color
35
49
  }
36
50
 
@@ -44,64 +58,86 @@ export function convertTokenColorsToReactStyle(tokenColors: any[]): ReactStyle {
44
58
  }
45
59
 
46
60
  tokenColors.forEach((token) => {
47
- if (!token.settings || !token.scope)
61
+ if (!token.settings)
48
62
  return
49
63
 
64
+ // Handle global theme settings
65
+ if (!token.scope) {
66
+ if (token.settings.foreground) {
67
+ styleMap.default!.color = normalizeColor(token.settings.foreground)
68
+ }
69
+ if (token.settings.background) {
70
+ styleMap.default!.backgroundColor = normalizeColor(token.settings.background)
71
+ }
72
+ return
73
+ }
74
+
50
75
  const scopes = Array.isArray(token.scope) ? token.scope : [token.scope]
51
- const styleEntries: Array<[string, string]> = []
76
+ const style: TextStyle = {}
52
77
 
53
- // Only add styles if they exist
54
78
  if (token.settings.foreground) {
55
- styleEntries.push(['color', normalizeColor(token.settings.foreground)])
79
+ style.color = normalizeColor(token.settings.foreground)
56
80
  }
57
81
  if (token.settings.background) {
58
- styleEntries.push(['backgroundColor', normalizeColor(token.settings.background)])
82
+ style.backgroundColor = normalizeColor(token.settings.background)
59
83
  }
60
84
  if (token.settings.fontStyle) {
61
85
  if (token.settings.fontStyle.includes('italic')) {
62
- styleEntries.push(['fontStyle', 'italic'])
86
+ style.fontStyle = 'italic'
63
87
  }
64
88
  if (token.settings.fontStyle.includes('bold')) {
65
- styleEntries.push(['fontWeight', 'bold'])
89
+ style.fontWeight = 'bold'
66
90
  }
67
91
  }
68
92
 
69
- if (styleEntries.length > 0) {
70
- const transformedStyle = transform(styleEntries)
71
-
72
- scopes.forEach((scope: string) => {
73
- if (typeof scope === 'string') {
74
- styleMap[scope] = { ...(styleMap[scope] || {}), ...transformedStyle }
75
- }
76
- })
77
- }
93
+ scopes.forEach((scope: string) => {
94
+ styleMap[scope] = { ...(styleMap[scope] || {}), ...style }
95
+ })
78
96
  })
79
97
 
80
98
  return styleMap
81
99
  }
82
100
 
83
- export function getRNStylesFromShikiStyle(theme: any): HighlighterStyleSheet {
84
- if (!theme || !theme.tokenColors) {
85
- console.error('Provided theme does not contain \'tokenColors\'.')
101
+ interface ThemeColors {
102
+ 'editor.background'?: string
103
+ 'editor.foreground'?: string
104
+ [key: string]: string | undefined
105
+ }
106
+
107
+ interface Theme {
108
+ colors?: ThemeColors
109
+ tokenColors?: any[]
110
+ }
111
+
112
+ export function getRNStylesFromShikiStyle(theme: Theme): HighlighterStyleSheet {
113
+ if (!theme) {
86
114
  return {}
87
115
  }
88
116
 
89
117
  try {
90
- // Convert theme colors to React Native styles
91
- const styles = convertTokenColorsToReactStyle(theme.tokenColors)
118
+ const styles: HighlighterStyleSheet = {}
119
+
120
+ // Convert token colors
121
+ if (theme.tokenColors) {
122
+ Object.assign(styles, convertTokenColorsToReactStyle(theme.tokenColors))
123
+ }
92
124
 
93
- // Add theme-specific base styles if they exist
125
+ // Add theme-specific editor styles
94
126
  if (theme.colors) {
127
+ styles.editor = {
128
+ backgroundColor: normalizeColor(theme.colors['editor.background']),
129
+ }
95
130
  styles.base = {
96
131
  color: normalizeColor(theme.colors['editor.foreground']),
97
- backgroundColor: normalizeColor(theme.colors['editor.background']),
98
132
  }
99
133
  }
100
134
 
101
135
  return styles
102
136
  }
103
137
  catch {
104
- console.error('Error converting theme styles')
105
- return {}
138
+ return {
139
+ editor: { backgroundColor: '#1e1e1e' },
140
+ base: { color: '#d4d4d4' },
141
+ }
106
142
  }
107
143
  }