Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README-zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

🍒 Cherry Studio App —— Cherry Studio 的官方移动版本,将强大的 LLMs(AI 大语言模型) 交互带到您的 iOS 和 Android 设备。

🌟 **支持项目:** [赞助](https://github.com/CherryHQ/cherry-studio/blob/main/docs/sponsor.md) | 给仓库点个 Star!
🌟 **支持项目:** [赞助](https://github.com/CherryHQ/cherry-studio/blob/main/docs/zh/guides/sponsor.md) | 给仓库点个 Star!

## ✨ 主要特性

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ English | [中文](./README-zh.md)

🍒 Cherry Studio App —— The official mobile version of Cherry Studio, bringing powerful LLMs (Large Language Models) interaction to your iOS and Android devices.

🌟 **Support the Project:** [Sponsor](https://github.com/CherryHQ/cherry-studio/blob/main/docs/sponsor.md) | Give the repo a Star!
🌟 **Support the Project:** [Sponsor](https://github.com/CherryHQ/cherry-studio/blob/main/docs/zh/guides/sponsor.md) | Give the repo a Star!

## ✨ Key Features

Expand Down
155 changes: 89 additions & 66 deletions patches/react-native-code-highlighter.patch
Original file line number Diff line number Diff line change
@@ -1,63 +1,5 @@
diff --git a/dist/commonjs/lib/CodeHighlighter.js b/dist/commonjs/lib/CodeHighlighter.js
index 0dfea90d15fb0dba4e9cfba1747205e48bb41dad..c3939f59522148cf78b28eb1f527d11b0901973e 100644
--- a/dist/commonjs/lib/CodeHighlighter.js
+++ b/dist/commonjs/lib/CodeHighlighter.js
@@ -17,6 +17,7 @@ const CodeHighlighter = ({
textStyle,
hljsStyle,
scrollViewProps,
+ horizontal = true,
containerStyle,
...rest
}) => {
@@ -47,7 +48,7 @@ const CodeHighlighter = ({
} = props;
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
...scrollViewProps,
- horizontal: true,
+ horizontal: horizontal,
contentContainerStyle: [stylesheet.hljs, scrollViewProps?.contentContainerStyle, containerStyle],
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
onStartShouldSetResponder: () => true,
diff --git a/dist/module/lib/CodeHighlighter.js b/dist/module/lib/CodeHighlighter.js
index 8eb424c842caf1229f068b4c723834e63148cf80..44ea78416c489edb966c21b267c9429e4ace2737 100644
--- a/dist/module/lib/CodeHighlighter.js
+++ b/dist/module/lib/CodeHighlighter.js
@@ -11,6 +11,7 @@ export const CodeHighlighter = ({
textStyle,
hljsStyle,
scrollViewProps,
+ horizontal = true,
containerStyle,
...rest
}) => {
@@ -41,7 +42,7 @@ export const CodeHighlighter = ({
} = props;
return /*#__PURE__*/_jsx(ScrollView, {
...scrollViewProps,
- horizontal: true,
+ horizontal: horizontal,
contentContainerStyle: [stylesheet.hljs, scrollViewProps?.contentContainerStyle, containerStyle],
children: /*#__PURE__*/_jsx(View, {
onStartShouldSetResponder: () => true,
diff --git a/dist/typescript/lib/CodeHighlighter.d.ts b/dist/typescript/lib/CodeHighlighter.d.ts
index 9f0116e726f603f7dbdbf2dfd743ca6877e2a5e0..e30a2f9f4caf7651cdcbad07720441bc018ee4e0 100644
--- a/dist/typescript/lib/CodeHighlighter.d.ts
+++ b/dist/typescript/lib/CodeHighlighter.d.ts
@@ -6,6 +6,11 @@ export interface CodeHighlighterProps extends SyntaxHighlighterProps {
hljsStyle: ReactStyle;
textStyle?: StyleProp<TextStyle>;
scrollViewProps?: ScrollViewProps;
+ /**
+ * Enable horizontal scrolling for long lines. Set to false to wrap text.
+ * @default true
+ */
+ horizontal?: boolean;
/**
* @deprecated Use scrollViewProps.contentContainerStyle instead
*/
diff --git a/src/lib/CodeHighlighter.tsx b/src/lib/CodeHighlighter.tsx
index c5fb4175f84136636dcfa8a45b3ee881b33beabc..63987cf4e95b412d3ef8fa752ded2cd5f3aa119f 100644
index c5fb4175f84136636dcfa8a45b3ee881b33beabc..e7fd6ee74f01028715a920e7d261700bb20f2066 100644
--- a/src/lib/CodeHighlighter.tsx
+++ b/src/lib/CodeHighlighter.tsx
@@ -23,6 +23,11 @@ export interface CodeHighlighterProps extends SyntaxHighlighterProps {
Expand All @@ -80,12 +22,93 @@ index c5fb4175f84136636dcfa8a45b3ee881b33beabc..63987cf4e95b412d3ef8fa752ded2cd5
containerStyle,
...rest
}) => {
@@ -77,7 +83,7 @@ export const CodeHighlighter: FunctionComponent<CodeHighlighterProps> = ({
@@ -73,21 +79,68 @@ export const CodeHighlighter: FunctionComponent<CodeHighlighterProps> = ({
}, []);

const renderer = (props: rendererProps) => {
- const { rows } = props;
+ const { rows } = props;
+ const lineNumbers = rest.lineNumbers;
+ const codeLines = rows.map((row, rowIndex) => {
return (
<ScrollView
{...scrollViewProps}
- <ScrollView
- {...scrollViewProps}
- horizontal
+ horizontal={horizontal}
contentContainerStyle={[
stylesheet.hljs,
scrollViewProps?.contentContainerStyle,
- contentContainerStyle={[
- stylesheet.hljs,
- scrollViewProps?.contentContainerStyle,
- containerStyle,
- ]}
- >
- <View onStartShouldSetResponder={() => true}>{renderNode(rows)}</View>
- </ScrollView>
+ <View key={`line-container-${rowIndex}`} style={{ flexDirection: 'row' }}>
+ {/* 行号 - 仅当 showLineNumbers 为 true 时显示 */}
+ {lineNumbers && (
+ <View style={{
+
+ paddingRight: 10,
+ backgroundColor: '#f5f5f5',
+ borderRightWidth: 1,
+ borderRightColor: '#ddd',
+ width: 40,
+ alignItems: 'center',
+ justifyContent: 'center',
+ }}>
+ <Text style={{
+ color: '#666',
+ fontSize: 14,
+ fontWeight: '400',
+ includeFontPadding: false,
+ }}>
+ {rowIndex + 1}
+ </Text>
+ </View>
+ )}
+
+ {/* 代码内容 */}
+ <View style={{ flex: 1, paddingLeft: lineNumbers ? 10 : 0 }}>
+ <Text style={[
+ textStyle,
+ {
+ color: stylesheet.hljs?.color,
+ lineHeight: 20,
+ includeFontPadding: false,
+ }
+ ]}>
+ {row.value ? trimNewlines(String(row.value)) : ''}
+ {row.children && renderNode(row.children, `row_${rowIndex}`)}
+ </Text>
+ </View>
+ </View>
);
- };
+ });
+
+ return (
+ <ScrollView
+ {...scrollViewProps}
+ horizontal={horizontal}
+ contentContainerStyle={[
+ stylesheet.hljs,
+ scrollViewProps?.contentContainerStyle,
+ containerStyle,
+ ]}
+ >
+ <View>
+ {codeLines}
+ </View>
+ </ScrollView>
+ );
+};

return (
<SyntaxHighlighter
@@ -97,6 +150,7 @@ export const CodeHighlighter: FunctionComponent<CodeHighlighterProps> = ({
PreTag={View}
style={{}}
testID="react-native-code-highlighter"
+
>
{children}
</SyntaxHighlighter>
6 changes: 3 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { useDrizzleStudio } from 'expo-drizzle-studio-plugin'
import { useFonts } from 'expo-font'
import * as SplashScreen from 'expo-splash-screen'
import { HeroUINativeProvider } from 'heroui-native'
import React, { Suspense, useEffect } from 'react'
import React, { useEffect } from 'react'
import { ActivityIndicator } from 'react-native'
import { SystemBars } from 'react-native-edge-to-edge'
import { GestureHandlerRootView } from 'react-native-gesture-handler'
Expand Down
13 changes: 13 additions & 0 deletions src/componentsV2/features/ChatScreen/Header/CodeSettingsButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react'

import { IconButton } from '@/componentsV2/base/IconButton'
import { Settings2 } from '@/componentsV2/icons/LucideIcon'

import { presentCodeSettingsSheet } from './CodeSettingsSheet'

export const CodeSettingButton = () => {
const handleSettingsPress = () => {
presentCodeSettingsSheet()
}
return <IconButton onPress={handleSettingsPress} icon={<Settings2 size={24} />} />
}
114 changes: 114 additions & 0 deletions src/componentsV2/features/ChatScreen/Header/CodeSettingsSheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { Switch } from 'heroui-native'
import React, { useEffect, useState } from 'react'
import { Pressable, StyleSheet, View } from 'react-native'
import Animated, {
Easing,
Extrapolation,
interpolate,
useAnimatedStyle,
useSharedValue,
withTiming
} from 'react-native-reanimated'

import { Group, GroupTitle } from '@/componentsV2'
import Text from '@/componentsV2/base/Text'
import XStack from '@/componentsV2/layout/XStack'
import YStack from '@/componentsV2/layout/YStack'
import { usePreference } from '@/hooks/usePreference'
import { useTheme } from '@/hooks/useTheme'
import { Width } from '@/utils/device'

const DRAWER_WIDTH = Width * 0.75

let setIsVisibleCallback: ((isVisible: boolean) => void) | null = null

export const presentCodeSettingsSheet = () => {
setIsVisibleCallback?.(true)
}

export const dismissCodeSettingsSheet = () => {
setIsVisibleCallback?.(false)
}

const CodeSettingsSheet: React.FC = () => {
const { isDark } = useTheme()
const [isVisible, setIsVisible] = useState(false)
const [showLineNumbers, setShowLineNumbers] = usePreference('code.show_line_numbers')
const translateX = useSharedValue(DRAWER_WIDTH)

useEffect(() => {
setIsVisibleCallback = setIsVisible
return () => {
setIsVisibleCallback = null
}
}, [])

useEffect(() => {
translateX.value = withTiming(isVisible ? 0 : DRAWER_WIDTH, {
duration: 250,
easing: Easing.bezier(0.4, 0, 0.2, 1)
})
}, [isVisible, translateX])

const rBackdropStyle = useAnimatedStyle(() => {
'worklet'
const opacity = interpolate(translateX.value, [0, DRAWER_WIDTH], [0.5, 0], Extrapolation.CLAMP)
return {
opacity,
pointerEvents: opacity < 0.01 ? 'none' : 'auto'
}
})

const handleClose = () => {
setIsVisible(false)
}

const rContentStyle = useAnimatedStyle(() => {
'worklet'
return {
transform: [{ translateX: translateX.value }]
}
})

const dynamicDrawerStyle = {
backgroundColor: isDark ? '#000000' : '#f8f8f8'
}

return (
<View style={StyleSheet.absoluteFill} pointerEvents="box-none">
<Animated.View style={[StyleSheet.absoluteFill, rBackdropStyle, { backgroundColor: 'black' }]}>
<Pressable style={StyleSheet.absoluteFill} onPress={handleClose} />
</Animated.View>
<Animated.View style={[rContentStyle, styles.drawer, dynamicDrawerStyle]}>
<YStack className="gap-4 p-4">
<GroupTitle>Code Settings</GroupTitle>
<Group>
<XStack className="items-center justify-between p-4">
<Text className="text-lg">Show Line Number</Text>
<Switch isSelected={showLineNumbers} onSelectedChange={setShowLineNumbers} />
</XStack>
</Group>
</YStack>
</Animated.View>
</View>
)
}

const styles = StyleSheet.create({
drawer: {
position: 'absolute',
top: 0,
bottom: 0,
right: 0,
width: DRAWER_WIDTH,
shadowColor: '#000',
shadowOffset: { width: -2, height: 0 },
shadowOpacity: 0.25,
shadowRadius: 3.84,
elevation: 5
}
})

CodeSettingsSheet.displayName = 'CodeSettingsSheet'

export default CodeSettingsSheet
2 changes: 2 additions & 0 deletions src/componentsV2/features/ChatScreen/Header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useAssistant } from '@/hooks/useAssistant'
import type { Topic } from '@/types/assistant'

import { AssistantSelection } from './AssistantSelection'
import { CodeSettingButton } from './CodeSettingsButton'
import { HistoryTopicButton } from './HistoryTopicButton'
import { NewTopicButton } from './NewTopicButton'

Expand Down Expand Up @@ -37,6 +38,7 @@ export const ChatScreenHeader = ({ topic }: HeaderBarProps) => {
<XStack className="min-w-10 items-center justify-end gap-4">
<NewTopicButton assistant={assistant} />
<HistoryTopicButton assistant={assistant} />
<CodeSettingButton />
</XStack>
<XStack className="absolute inset-y-0 left-3.5 right-3.5 items-center justify-center">
<AssistantSelection assistant={assistant} topic={topic} />
Expand Down
2 changes: 2 additions & 0 deletions src/screens/home/ChatScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'

import { SafeAreaContainer, YStack } from '@/componentsV2'
import { ChatScreenHeader } from '@/componentsV2/features/ChatScreen/Header'
import CodeSettingsSheet from '@/componentsV2/features/ChatScreen/Header/CodeSettingsSheet'
import { MessageInputContainer } from '@/componentsV2/features/ChatScreen/MessageInput/MessageInputContainer'
import { CitationSheet } from '@/componentsV2/features/Sheet/CitationSheet'
import { useAssistant } from '@/hooks/useAssistant'
Expand Down Expand Up @@ -100,6 +101,7 @@ const ChatScreen = () => {
</YStack>
</KeyboardAvoidingView>
</PanGestureHandler>
<CodeSettingsSheet />
<CitationSheet />
</SafeAreaContainer>
)
Expand Down
Loading