diff --git a/examples/nextjs-ai-chatbot/app/api/chats/[id]/route.ts b/examples/nextjs-ai-chatbot/app/api/chats/[id]/route.ts index d673159..4790466 100644 --- a/examples/nextjs-ai-chatbot/app/api/chats/[id]/route.ts +++ b/examples/nextjs-ai-chatbot/app/api/chats/[id]/route.ts @@ -4,7 +4,6 @@ import { createConfigFromEnv } from '@stream-io/ai-sdk-storage/dist/utils'; const storage = createStreamStorageClient(createConfigFromEnv()); - export async function GET(_req: Request, { params }: any) { const { id } = await params; try { diff --git a/examples/nextjs-ai-chatbot/app/layout.tsx b/examples/nextjs-ai-chatbot/app/layout.tsx index dee279b..28575c7 100644 --- a/examples/nextjs-ai-chatbot/app/layout.tsx +++ b/examples/nextjs-ai-chatbot/app/layout.tsx @@ -24,20 +24,20 @@ export const metadata: Metadata = { export default function RootLayout({ children }: LayoutProps) { return ( - + -
-
+
+
-
-
-
{children}
-
-
+
+
+
{children}
+
+
diff --git a/examples/nextjs-ai-chatbot/components/markdown.tsx b/examples/nextjs-ai-chatbot/components/markdown.tsx index 9cf949c..c7486db 100644 --- a/examples/nextjs-ai-chatbot/components/markdown.tsx +++ b/examples/nextjs-ai-chatbot/components/markdown.tsx @@ -10,7 +10,7 @@ import Weather from './tools/weather'; export const NonMemoizedMarkdown = ({ children }: { children: string }) => { const components = { p: ({ children }: { children: string }) => { - return
{children}
; + return
{children}
; }, pre: ({ children, ...props }: any) => { const codeElement = React.Children.only(children); @@ -33,21 +33,21 @@ export const NonMemoizedMarkdown = ({ children }: { children: string }) => { }, ol: ({ node, children, ...props }: any) => { return ( -
    +
      {children}
    ); }, li: ({ node, children, ...props }: any) => { return ( -
  1. +
  2. {children}
  3. ); }, ul: ({ node, children, ...props }: any) => { return ( -
      +
        {children}
      ); diff --git a/examples/nextjs-ai-chatbot/components/messages.tsx b/examples/nextjs-ai-chatbot/components/messages.tsx index f54de67..422a0f2 100644 --- a/examples/nextjs-ai-chatbot/components/messages.tsx +++ b/examples/nextjs-ai-chatbot/components/messages.tsx @@ -23,19 +23,19 @@ export default function Messages() { if (isLoadingMessages) { return ( -
      - +
      +
      ); } if (!id) { return ( -
      -

      - Welcome to AI Assistant +
      +

      + Welcome to AI Assistant

      -

      +

      Ready to help you with any questions or tasks. How can I assist you today?

      @@ -44,7 +44,7 @@ export default function Messages() { } return ( -
      +
      {messages.map((m: UIMessage) => (
      -
      +
      {m.parts.map( (part, index) => part.type === 'file' && @@ -60,13 +60,13 @@ export default function Messages() { {part.filename @@ -91,9 +91,9 @@ export default function Messages() {
      ))} {status === 'submitted' && ( -
      - - Thinking +
      + + Thinking
      )}
      diff --git a/examples/nextjs-ai-chatbot/components/sidebar.tsx b/examples/nextjs-ai-chatbot/components/sidebar.tsx index ee88852..e7ea660 100644 --- a/examples/nextjs-ai-chatbot/components/sidebar.tsx +++ b/examples/nextjs-ai-chatbot/components/sidebar.tsx @@ -11,10 +11,10 @@ export default function Sidebar({ title = 'Chats' }) { const { id } = useParams(); return ( <> -

      - {title} +

      + {title}

      -
      +
      {chats?.length > 0 ? ( chats.map((chat: ChatType) => ( -
      - - {chat.name} +
      + + {chat.name}
      )) ) : ( -
      +
      No chats found
      )}
      - + New Chat diff --git a/examples/nextjs-ai-chatbot/components/tools/weather.tsx b/examples/nextjs-ai-chatbot/components/tools/weather.tsx index e57dd2d..6a7402e 100644 --- a/examples/nextjs-ai-chatbot/components/tools/weather.tsx +++ b/examples/nextjs-ai-chatbot/components/tools/weather.tsx @@ -4,17 +4,17 @@ import { CloudSunIcon, MapPinIcon } from 'lucide-react'; export default function Weather({ data }: { data: string }) { const weather: WeatherToolResponse = JSON.parse(data); return ( -
      -
      - +
      +
      + {weather.location?.name || 'Unknown'}
      -
      +
      {weather.current?.condition?.text} @@ -24,15 +24,15 @@ export default function Weather({ data }: { data: string }) {
      Feels like: {weather.current?.feelslike_c}°C
      -
      +
      Condition: {weather.current?.condition?.text}
      Wind: {weather.current?.wind_kph} km/h
      Humidity: {weather.current?.humidity}%
      Pressure: {weather.current?.pressure_mb} mb
      -
      - - weather tool +
      + + weather tool
      ); diff --git a/examples/nextjs-ai-chatbot/hooks/usetranscribe.ts b/examples/nextjs-ai-chatbot/hooks/usetranscribe.ts index f918578..39cf438 100644 --- a/examples/nextjs-ai-chatbot/hooks/usetranscribe.ts +++ b/examples/nextjs-ai-chatbot/hooks/usetranscribe.ts @@ -112,7 +112,6 @@ export function useTranscriber(options: Options = {}) { mr.onstop = handleStop; mr.start(250); } - } catch (e: any) { setError(e?.message || 'Something went wrong'); console.error(e); @@ -185,7 +184,6 @@ export function useTranscriber(options: Options = {}) { }); streamRef.current = stream; const ctx = new (window.AudioContext || - (window as any).webkitAudioContext)(); audioCtxRef.current = ctx; @@ -216,7 +214,6 @@ export function useTranscriber(options: Options = {}) { recordingStartedAtRef.current = performance.now(); rafRef.current = requestAnimationFrame(checkSilence); - } catch (e: any) { setError(e?.message || 'Mic access failed'); console.error(e); diff --git a/examples/react-example/src/App.tsx b/examples/react-example/src/App.tsx index 310a759..d0aac25 100644 --- a/examples/react-example/src/App.tsx +++ b/examples/react-example/src/App.tsx @@ -9,15 +9,15 @@ function App() { return ( <>

      Vite + React

      -
      +
      @@ -25,7 +25,7 @@ function App() { Edit src/App.tsx and save to test HMR

      -

      +

      Click on the Vite and React logos to learn more

      diff --git a/packages/react-native-sdk/package.json b/packages/react-native-sdk/package.json index fbd98bd..ec1c077 100644 --- a/packages/react-native-sdk/package.json +++ b/packages/react-native-sdk/package.json @@ -45,13 +45,13 @@ "@khanacademy/simple-markdown": "^2.1.0", "linkifyjs": "^4.3.2", "lodash": "4.17.21", - "react-native-syntax-highlighter": "^2.1.0", "react-syntax-highlighter": "15.5.0" }, "devDependencies": { "@types/lodash": "4.17.20", "@types/node": "^24", "@types/react": "19.2.2", + "@types/react-syntax-highlighter": "^15.5.13", "concurrently": "catalog:", "react": "19.2.0", "react-native": "^0.82.1", @@ -68,7 +68,8 @@ "react-native": ">=0.73.0", "react-native-gesture-handler": ">=2.18.0", "react-native-reanimated": ">=3.16.0", - "react-native-svg": ">=15.8.0" + "react-native-svg": ">=15.8.0", + "react-syntax-highlighter": ">=15.0.0" }, "peerDependenciesMeta": { "expo": { diff --git a/packages/react-native-sdk/src/index.ts b/packages/react-native-sdk/src/index.ts index f666465..8566a6a 100644 --- a/packages/react-native-sdk/src/index.ts +++ b/packages/react-native-sdk/src/index.ts @@ -1,2 +1,3 @@ export * from './markdown'; export * from './MarkdownRichText'; +export * from './syntax-highlighting'; diff --git a/packages/react-native-sdk/src/markdown/components/CodeBlock.tsx b/packages/react-native-sdk/src/markdown/components/CodeBlock.tsx index d5a9efc..693f0e0 100644 --- a/packages/react-native-sdk/src/markdown/components/CodeBlock.tsx +++ b/packages/react-native-sdk/src/markdown/components/CodeBlock.tsx @@ -1,8 +1,7 @@ import { Pressable, type PressableProps, Text, View } from 'react-native'; import type { MarkdownComponentProps, RuleRenderFunction } from '../types.ts'; import { MarkdownReactiveScrollView } from '../../components'; -// @ts-expect-error need to check what's up with the lib -import SyntaxHighlighter from 'react-native-syntax-highlighter'; +import SyntaxHighlighter from '../../syntax-highlighting/SyntaxHighlighter.tsx'; import { type PropsWithChildren, useCallback, useMemo } from 'react'; export const CodeBlockCopyButton = ({ diff --git a/packages/react-native-sdk/src/markdown/components/List.tsx b/packages/react-native-sdk/src/markdown/components/List.tsx index 84e0047..d6a4c64 100644 --- a/packages/react-native-sdk/src/markdown/components/List.tsx +++ b/packages/react-native-sdk/src/markdown/components/List.tsx @@ -23,7 +23,7 @@ export const List = ({ if (item === null) { return ( - + + { + stylesheet = Array.isArray(stylesheet) ? stylesheet[0] : stylesheet; + + const transformedStyle = Object.entries(stylesheet ?? {}).reduce( + (newStylesheet, [className, style]) => { + const rn = cssToRNTextStyle(style); + + newStylesheet[className] = rn as RNStyle; + return newStylesheet; + }, + {}, + ); + + const topLevel = ( + highlighter === 'prism' + ? transformedStyle['pre[class*="language-"]'] + : transformedStyle.hljs + ) as RNStyle; + + const defaultColor = (topLevel && topLevel.color) || '#000'; + + return { transformedStyle, defaultColor }; +}; + +const createChildren = ({ + stylesheet, + fontSize, + fontFamily, +}: { + stylesheet: SyntaxHighlighterStylesheet; + fontSize?: number; + fontFamily?: string; +}) => { + let childrenCount = 0; + return (children: rendererNode['children'], defaultColor: string) => { + childrenCount += 1; + return (children ?? []).map((child, i) => + createNativeElement({ + node: child, + stylesheet, + key: `code-segment-${childrenCount}-${i}`, + defaultColor, + fontSize, + fontFamily, + }), + ); + }; +}; + +const createNativeElement = ({ + node, + stylesheet, + key, + defaultColor, + fontFamily, + fontSize = DEFAULT_FONT_SIZE, +}: { + node: rendererNode; + stylesheet: SyntaxHighlighterStylesheet; + key: string; + defaultColor: string; + fontFamily?: string; + fontSize?: number; +}) => { + const { properties, type, tagName: TagName, value } = node; + const startingStyle = { fontFamily, fontSize, height: fontSize + 5 }; + if (type === 'text') { + return ( + + {value} + + ); + } else if (TagName) { + const childrenFactory = createChildren({ + stylesheet, + fontSize, + fontFamily, + }); + const style = properties + ? createStyleObject( + properties.className, + Object.assign( + { color: defaultColor }, + properties.style, + startingStyle, + ), + stylesheet, + ) + : {}; + const children = childrenFactory( + node.children, + style.color || defaultColor, + ); + return ( + + {children} + + ); + } +}; + +const nativeRenderer = ({ + defaultColor, + fontFamily, + fontSize, +}: { + defaultColor: string; + fontFamily?: string; + fontSize?: number; +}): SyntaxHighlighterProps['renderer'] => { + return ({ rows, stylesheet }) => + rows.map((node, i) => + createNativeElement({ + node, + stylesheet, + key: `code-segment-${i}`, + defaultColor, + fontFamily, + fontSize, + }), + ); +}; + +const NativeSyntaxHighlighter = ({ + fontFamily = Platform.OS === 'ios' ? 'Courier' : 'Monospace', + fontSize = DEFAULT_FONT_SIZE, + children, + highlighter = 'highlightjs', + style = highlighter === 'prism' ? prismDefaultStyle : defaultStyle, + PreTag = MarkdownReactiveScrollView, + CodeTag = MarkdownReactiveScrollView, + ...rest +}: PropsWithChildren) => { + const { transformedStyle, defaultColor } = useMemo( + () => + generateNewStylesheet({ + stylesheet: style, + highlighter, + }), + [highlighter, style], + ); + const renderer = useMemo( + () => + nativeRenderer({ + defaultColor: defaultColor as string, + fontFamily, + fontSize, + }), + [defaultColor, fontFamily, fontSize], + ); + + const Highlighter = + highlighter === 'prism' ? SyntaxHighlighterPrism : SyntaxHighlighter; + + return ( + + {children} + + ); +}; + +export default NativeSyntaxHighlighter; diff --git a/packages/react-native-sdk/src/syntax-highlighting/converter.ts b/packages/react-native-sdk/src/syntax-highlighting/converter.ts new file mode 100644 index 0000000..5aadacb --- /dev/null +++ b/packages/react-native-sdk/src/syntax-highlighting/converter.ts @@ -0,0 +1,242 @@ +import type { TextStyle } from 'react-native'; +import type { ConvertOpts, CSSP } from './types.ts'; + +const EM_DEFAULT = 16; + +/** Parse CSS lengths like 14, "14", "14px", "1.25em", "0.875rem" */ +const len = ( + v: unknown, + { baseFontSize, rootFontSize }: ConvertOpts, +): number | undefined => { + if (v == null) return undefined; + if (typeof v === 'number') return v; + if (typeof v !== 'string') return undefined; + + const s = v.trim().toLowerCase(); + if (s.endsWith('px')) { + const n = Number(s.slice(0, -2)); + return Number.isFinite(n) ? n : undefined; + } + if (s.endsWith('em')) { + const n = Number(s.slice(0, -2)); + return Number.isFinite(n) ? n * (baseFontSize ?? EM_DEFAULT) : undefined; + } + if (s.endsWith('rem')) { + const n = Number(s.slice(0, -3)); + const root = rootFontSize ?? baseFontSize ?? EM_DEFAULT; + return Number.isFinite(n) ? n * root : undefined; + } + // plain number-like string + const n = Number(s); + return Number.isFinite(n) ? n : undefined; +}; + +/** Font shorthand: e.g. "italic small-caps 700 14px/20px Menlo, monospace" */ +const parseFontShorthand = (v: string, opts: ConvertOpts) => { + const out: Partial = {}; + const parts = v.split(/\s+/); + // Very light parser: pick out style, weight, size[/lineHeight], and family + // Strategy: + // - style: "normal|italic" + // - weight: "normal|bold|100..900" + // - when we hit a token like "14px" or "14px/20px" → size & optional lineHeight + // - everything after size token is font family (may contain commas/spaces) + let i = 0; + + // style + if (parts[i] === 'italic' || parts[i] === 'normal') { + if (parts[i] !== 'normal') out.fontStyle = 'italic'; + i++; + } + + // (skip small-caps if present; RN uses fontVariant which is separate) + if (parts[i] === 'small-caps') { + out.fontVariant = ['small-caps']; + i++; + } + + // weight + if (/^(normal|bold|[1-9]00)$/.test(parts[i] ?? '')) { + const token = parts[i]!; + out.fontWeight = ( + token === 'normal' ? undefined : token + ) as TextStyle['fontWeight']; + i++; + } + + // size[/lineHeight] + const sizeToken = parts[i]; + if ( + sizeToken && + (/.+(px|em|rem)$/.test(sizeToken) || + /^[\d.]+(\/[\d.]+)?(px|em|rem)?$/.test(sizeToken)) + ) { + const [fs, lh] = sizeToken.split('/'); + const fontSize = len(fs, opts); + if (fontSize != null) out.fontSize = fontSize; + if (lh) { + const lineHeight = len(lh, opts); + if (lineHeight != null) out.lineHeight = lineHeight; + } + i++; + } + + // family + const family = parts.slice(i).join(' ').replace(/^,|,$/g, '').trim(); + if (family) { + // basic cleanup: take first family; strip quotes + const first = family + .split(',')[0] + ?.trim() + .replace(/^["']|["']$/g, ''); + if (first) out.fontFamily = first; + } + + return out; +}; + +/** text-decoration shorthand → RN textDecorationLine/Color/Style */ +const applyTextDecoration = (v: unknown, acc: Partial) => { + if (typeof v !== 'string') return; + const tokens = v.toLowerCase().split(/\s+/); + let line: TextStyle['textDecorationLine'] | undefined = undefined; + let style: TextStyle['textDecorationStyle'] | undefined; + let color: TextStyle['textDecorationColor'] | undefined; + + for (const t of tokens) { + if (t === 'none') line = 'none'; + else if (t === 'underline') + line = line === 'line-through' ? 'underline line-through' : 'underline'; + else if (t === 'line-through') + line = line === 'underline' ? 'underline line-through' : 'line-through'; + else if ( + t === 'solid' || + t === 'double' || + t === 'dotted' || + t === 'dashed' + ) + style = t; + // we take a naive approach and treat this as a color token; RN accepts CSS color strings + else if (t) color = t; + } + + if (line) acc.textDecorationLine = line; + if (style) acc.textDecorationStyle = style; + if (color) acc.textDecorationColor = color; +}; + +/** text-shadow CSS → RN textShadowColor/Offset/Radius + * for example: "1px 1px #000", "1px 1px 2px rgba(0,0,0,0.4)" + */ +const applyTextShadow = ( + v: unknown, + acc: Partial, + opts: ConvertOpts, +) => { + if (typeof v !== 'string') return; + const parts = v.trim().split(/\s+/); + if (parts.length < 2) return; + + const ox = len(parts[0], opts); + const oy = len(parts[1], opts); + + let radius: number | undefined; + let color: string | undefined; + + if (parts[2]) { + const r = len(parts[2], opts); + if (Number.isFinite(r!)) { + radius = r!; + color = parts.slice(3).join(' '); + } else { + color = parts.slice(2).join(' '); + } + } + + if (color) acc.textShadowColor = color; + acc.textShadowOffset = { width: ox ?? 0, height: oy ?? 0 }; + if (radius != null) acc.textShadowRadius = radius; +}; + +/** map CSS (web) to RN TextStyle */ +export const cssToRNTextStyle = ( + css: Partial, + options: ConvertOpts = {}, +): TextStyle => { + const opts: ConvertOpts = { + baseFontSize: options.baseFontSize ?? EM_DEFAULT, + rootFontSize: options.rootFontSize ?? options.baseFontSize ?? EM_DEFAULT, + }; + + const out: Partial = {}; + + // Simple 1:1 or unit-converted mappings + if (css.color) out.color = String(css.color); + + if (css.background || css.backgroundColor) { + // RN ignores complex backgrounds; pass solid color if present + if (typeof css.backgroundColor === 'string') + out.backgroundColor = css.backgroundColor; + else if (typeof css.background === 'string') + out.backgroundColor = css.background; + } + + if (css.fontFamily) out.fontFamily = String(css.fontFamily); + if (css.fontStyle) out.fontStyle = css.fontStyle as TextStyle['fontStyle']; + if (css.fontWeight) + out.fontWeight = String(css.fontWeight) as TextStyle['fontWeight']; + + if (css.fontSize != null) { + const n = len(css.fontSize, opts); + if (n != null) out.fontSize = n; + } + + if (css.lineHeight != null) { + const n = len(css.lineHeight, opts); + if (n != null) out.lineHeight = n; + } + + if (css.letterSpacing != null) { + const n = len(css.letterSpacing, opts); + if (n != null) out.letterSpacing = n; + } + + if (css.textAlign) out.textAlign = css.textAlign as TextStyle['textAlign']; + if (css.textTransform) + out.textTransform = css.textTransform as TextStyle['textTransform']; + + if (css.font && typeof css.font === 'string') { + Object.assign(out, parseFontShorthand(css.font, opts)); + } + + if (css.textDecoration) { + applyTextDecoration(css.textDecoration, out); + } + if (css.textDecorationLine) { + out.textDecorationLine = + css.textDecorationLine as TextStyle['textDecorationLine']; + } + if (css.textDecorationColor) { + out.textDecorationColor = String(css.textDecorationColor); + } + if (css.textDecorationStyle) { + out.textDecorationStyle = + css.textDecorationStyle as TextStyle['textDecorationStyle']; + } + + if (css.textShadow) applyTextShadow(css.textShadow, out, opts); + + // Simulating the closest we can get to a CSS overflow-like functionality + const overflowLike = + (css as any).overflow ?? (css as any).overflowX ?? (css as any).overflowY; + if (typeof overflowLike === 'string') { + out.overflow = + overflowLike === 'hidden' + ? 'hidden' + : overflowLike === 'scroll' || overflowLike === 'auto' + ? 'scroll' + : 'visible'; + } + + return out as TextStyle; +}; diff --git a/packages/react-native-sdk/src/syntax-highlighting/index.ts b/packages/react-native-sdk/src/syntax-highlighting/index.ts new file mode 100644 index 0000000..3e11996 --- /dev/null +++ b/packages/react-native-sdk/src/syntax-highlighting/index.ts @@ -0,0 +1,11 @@ +/** + * This sub-library is a port/took inspiration from https://github.com/conorhastings/react-native-syntax-highlighter. + * We create our own since we don't want the version of https://github.com/react-syntax-highlighter/react-syntax-highlighter + * to be pinned to version 6 but rather use whatever we see fit. + */ + +export * from './SyntaxHighlighter'; +export * from './prism-config'; +export * from './converter'; + +export * from './types'; diff --git a/packages/react-native-sdk/src/syntax-highlighting/prism-config.ts b/packages/react-native-sdk/src/syntax-highlighting/prism-config.ts new file mode 100644 index 0000000..c83c126 --- /dev/null +++ b/packages/react-native-sdk/src/syntax-highlighting/prism-config.ts @@ -0,0 +1,2 @@ +// @ts-expect-error This negates the global variable set by react-syntax-highlighter +global.Prism = { disableWorkerMessageHandler: true }; diff --git a/packages/react-native-sdk/src/syntax-highlighting/types.ts b/packages/react-native-sdk/src/syntax-highlighting/types.ts new file mode 100644 index 0000000..5ad2f04 --- /dev/null +++ b/packages/react-native-sdk/src/syntax-highlighting/types.ts @@ -0,0 +1,29 @@ +import type { TextStyle } from 'react-native'; +import type { SyntaxHighlighterProps } from 'react-syntax-highlighter'; +import type React from 'react'; + +export type RNStyle = TextStyle; +export type RNSheet = Record; + +export type SyntaxHighlighterStylesheet = NonNullable< + NativeSyntaxHighlighterProps['style'] +>; + +export type ExtraSyntaxHighlighterProps = { + fontFamily?: string; + fontSize?: number; + highlighter?: 'highlightjs' | 'prism'; +}; + +export type NativeSyntaxHighlighterProps = SyntaxHighlighterProps & + ExtraSyntaxHighlighterProps; + +export type CSSP = React.CSSProperties; + +/** Options for unit conversion */ +export type ConvertOpts = { + /** base for `em` (defaults to 16) */ + baseFontSize?: number; + /** root base for `rem` (defaults to baseFontSize) */ + rootFontSize?: number; +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 93c9860..01efffc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -221,9 +221,6 @@ importers: react-native-svg: specifier: '>=15.8.0' version: 15.14.0(react-native@0.82.1(@babel/core@7.28.4)(@types/react@19.2.2)(react@19.2.0))(react@19.2.0) - react-native-syntax-highlighter: - specifier: ^2.1.0 - version: 2.1.0(react-syntax-highlighter@15.5.0(react@19.2.0)) react-syntax-highlighter: specifier: 15.5.0 version: 15.5.0(react@19.2.0) @@ -237,6 +234,9 @@ importers: '@types/react': specifier: 19.2.2 version: 19.2.2 + '@types/react-syntax-highlighter': + specifier: ^15.5.13 + version: 15.5.13 concurrently: specifier: 'catalog:' version: 9.2.1 @@ -5122,11 +5122,6 @@ packages: react: '*' react-native: '*' - react-native-syntax-highlighter@2.1.0: - resolution: {integrity: sha512-upu8gpKT2ZeslXn2d763KwtzzhM9OUHGgJjIKKIUw1JnFAzVwQmKCaFGoI6PkQa7T1LVggBW5k5VoaLFhZDb+g==} - peerDependencies: - react-syntax-highlighter: ^6.0.4 - react-native-worklets@0.5.2: resolution: {integrity: sha512-lCzmuIPAK/UaOJYEPgYpVqrsxby1I54f7PyyZUMEO04nwc00CDrCvv9lCTY1daLHYTF8lS3f9zlzErfVsIKqkA==} peerDependencies: @@ -12103,10 +12098,6 @@ snapshots: react-native: 0.82.1(@babel/core@7.28.4)(@types/react@19.2.2)(react@19.2.0) warn-once: 0.1.1 - react-native-syntax-highlighter@2.1.0(react-syntax-highlighter@15.5.0(react@19.2.0)): - dependencies: - react-syntax-highlighter: 15.5.0(react@19.2.0) - react-native-worklets@0.5.2(@babel/core@7.28.4)(react-native@0.82.1(@babel/core@7.28.4)(@types/react@19.2.2)(react@19.2.0))(react@19.2.0): dependencies: '@babel/core': 7.28.4