diff --git a/examples/SampleApp/yarn.lock b/examples/SampleApp/yarn.lock index b95479fc13..2ea2cb15fe 100644 --- a/examples/SampleApp/yarn.lock +++ b/examples/SampleApp/yarn.lock @@ -2041,6 +2041,18 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@khanacademy/perseus-utils@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@khanacademy/perseus-utils/-/perseus-utils-2.1.0.tgz#a405f5e740e73a1345f2b172ff34260b6d87f57e" + integrity sha512-QgN9qW1hnoCGkErSVwCJ5SmWAqlcgXA3TvtMjsyv8LiYbAS4/ZMQOHIuq5P7u0t1i6tp02w7Qjbz/COsxyHg/A== + +"@khanacademy/simple-markdown@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@khanacademy/simple-markdown/-/simple-markdown-2.1.0.tgz#7fcbbcb4d0eda7d0dfb09c2ae35f0b9eb1e07dcf" + integrity sha512-U9yemDLqP3ehAhUdBhzVfZe9h10wWbkrIkfFf2ejSO/cUSFZwjt+KRfZtZK6q2HY+RkNYUjALDpwJu7LkrY1gw== + dependencies: + "@khanacademy/perseus-utils" "2.1.0" + "@napi-rs/wasm-runtime@^0.2.11": version "0.2.11" resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.11.tgz#192c1610e1625048089ab4e35bc0649ce478500e" diff --git a/package/package.json b/package/package.json index a892d446d7..888b46af8e 100644 --- a/package/package.json +++ b/package/package.json @@ -68,6 +68,7 @@ }, "dependencies": { "@gorhom/bottom-sheet": "^5.1.8", + "@khanacademy/simple-markdown": "^2.1.0", "@ungap/structured-clone": "^1.3.0", "dayjs": "1.11.13", "emoji-regex": "^10.4.0", @@ -77,7 +78,6 @@ "lodash-es": "4.17.21", "mime-types": "^2.1.35", "path": "0.12.7", - "react-native-markdown-package": "1.8.2", "react-native-url-polyfill": "^2.0.0", "stream-chat": "^9.23.0", "use-sync-external-store": "^1.5.0" @@ -112,11 +112,11 @@ "@babel/core": "^7.27.4", "@babel/runtime": "^7.27.6", "@op-engineering/op-sqlite": "^14.0.3", - "@shopify/flash-list": "^2.1.0", "@react-native-community/eslint-config": "3.2.0", "@react-native-community/eslint-plugin": "1.3.0", "@react-native-community/netinfo": "^11.4.1", "@react-native/babel-preset": "0.79.3", + "@shopify/flash-list": "^2.1.0", "@testing-library/jest-native": "^5.4.3", "@testing-library/react-native": "13.2.0", "@types/better-sqlite3": "^7.6.13", diff --git a/package/src/components/Message/MessageSimple/utils/markdown/index.tsx b/package/src/components/Message/MessageSimple/utils/markdown/index.tsx new file mode 100644 index 0000000000..e91351fbcb --- /dev/null +++ b/package/src/components/Message/MessageSimple/utils/markdown/index.tsx @@ -0,0 +1,64 @@ +import React, { PropsWithChildren, useMemo } from 'react'; +import { View } from 'react-native'; + +import SimpleMarkdown, { + OutputRules, + ParserRules, + ReactOutputRule, +} from '@khanacademy/simple-markdown'; +import { isArray, isEqual, merge } from 'lodash'; + +import { getLocalRules } from './rules'; +import styles from './styles'; + +import { MarkdownStyle } from '../../../../../contexts'; + +type DefaultRules = typeof SimpleMarkdown.defaultRules; + +export type MarkdownRules = Partial; + +export type MarkdownProps = { + onLink: (url: string) => Promise; + rules: MarkdownRules; + styles: MarkdownStyle; +}; + +export type MarkdownOptions = Partial>; + +const Markdown = (props: PropsWithChildren) => { + const { onLink, rules: rulesProp, styles: stylesProp, children } = props; + + const mergedStyles = useMemo(() => merge({}, styles, stylesProp), [stylesProp]); + const localRules = useMemo( + () => + merge( + {}, + SimpleMarkdown.defaultRules, + getLocalRules(mergedStyles, { onLink }), + rulesProp, + ) as unknown as ParserRules, + [mergedStyles, onLink, rulesProp], + ); + + const parser = useMemo(() => SimpleMarkdown.parserFor(localRules), [localRules]); + const renderer = useMemo( + () => SimpleMarkdown.outputFor(localRules as unknown as OutputRules, 'react'), + [localRules], + ); + + const childText = useMemo(() => (isArray(children) ? children.join('') : children), [children]); + + const toRender = useMemo(() => { + const blockSource = `${childText ?? ''}\n\n`; + return parser(blockSource, { inline: false }); + }, [childText, parser]); + + const tree = useMemo(() => renderer(toRender), [renderer, toRender]); + + return {tree}; +}; + +const areEqual = (prevProps: PropsWithChildren, nextProps: PropsWithChildren) => + isEqual(prevProps.children, nextProps.children); + +export default React.memo(Markdown, areEqual); diff --git a/package/src/components/Message/MessageSimple/utils/markdown/rules.ts b/package/src/components/Message/MessageSimple/utils/markdown/rules.ts new file mode 100644 index 0000000000..012c57df7e --- /dev/null +++ b/package/src/components/Message/MessageSimple/utils/markdown/rules.ts @@ -0,0 +1,571 @@ +import React from 'react'; +import { Text, TextStyle, View, ViewStyle } from 'react-native'; + +import SimpleMarkdown, { + MatchFunction, + Output, + OutputRules, + ParseFunction, + Parser, + ReactOutputRule, + SingleASTNode, + State, +} from '@khanacademy/simple-markdown'; +import { head, includes, map, noop, size, some } from 'lodash'; + +import { MarkdownStyle } from '../../../../../contexts'; + +import { MarkdownOptions } from './index'; + +type MarkdownStyleProp = TextStyle | ViewStyle; + +type MarkdownState = State & { + key: number | string; + inline?: boolean; + withinText?: boolean; + withinQuote?: boolean; + withinHeading?: boolean; + withinLink?: boolean; + withinList?: boolean; + withinParagraphWithImage?: boolean; + style: MarkdownStyleProp; +}; + +type NodeWithContent = SingleASTNode & { content: SingleASTNode[] }; +type NodeWithStringContent = SingleASTNode & { content: string }; +type HeadingNode = SingleASTNode & { level: number; content: SingleASTNode[] }; +type ListNode = SingleASTNode & { + ordered: boolean; + items: SingleASTNode[] | SingleASTNode[][]; +}; +type TableNode = SingleASTNode & { + header: SingleASTNode[]; + cells: SingleASTNode[][]; +}; +type TargetNode = SingleASTNode & { target: string }; + +// Allow dynamic heading style access like styles["heading1"] +type HeadingStyles = Record; + +export const getLocalRules = ( + styles: MarkdownStyle, + opts: MarkdownOptions = {}, +): OutputRules => { + const LINK_INSIDE = '(?:\\[[^\\]]*\\]|[^\\]]|\\](?=[^\\[]*\\]))*'; + const LINK_HREF_AND_TITLE = '\\s*?(?:\\s+[\'"]([\\s\\S]*?)[\'"])?\\s*'; + + const pressHandler = (target: string) => { + if (opts.onLink) { + // user-supplied handler may be async; we keep your behavior + Promise.resolve(opts.onLink(target)).catch((error: unknown) => { + const msg = + error && typeof error === 'object' && 'toString' in error + ? String(error) + : 'Unknown error'; + + console.log('There has been a problem with this action. ' + msg); + throw error; + }); + } + }; + + const parseInline = function ( + parse: Parser, + content: string, + state: MarkdownState, + ): SingleASTNode[] { + const isCurrentlyInline = state.inline || false; + state.inline = true; + const result = parse(content, state); + state.inline = isCurrentlyInline; + return result; + }; + + const parseCaptureInline: ParseFunction = (capture, parse, state) => { + return { + content: parseInline(parse, capture[2], state as MarkdownState), + }; + }; + + return { + autolink: { + react(node: SingleASTNode, output: Output, { ...state }: MarkdownState) { + state.withinText = true; + const n = node as NodeWithContent & TargetNode; + const onPress = () => pressHandler(n.target); + return React.createElement( + Text, + { + key: state.key, + onPress, + style: styles.autolink, + }, + output(n.content, state), + ); + }, + }, + blockQuote: { + react(node: SingleASTNode, output: Output, { ...state }: MarkdownState) { + state.withinQuote = true; + const n = node as NodeWithContent; + + const img = React.createElement(View, { + key: Number(state.key) - Number(state.key), + style: [styles.blockQuoteSectionBar, styles.blockQuoteBar], + }); + + const blockQuote = React.createElement( + Text, + { + key: state.key, + style: styles.blockQuoteText, + }, + output(n.content, state), + ); + + return React.createElement( + View, + { + key: state.key, + style: [styles.blockQuoteSection, styles.blockQuoteText], + }, + [img, blockQuote], + ); + }, + }, + br: { + react(_node: SingleASTNode, _output: Output, { ...state }: MarkdownState) { + return React.createElement( + Text, + { + key: state.key, + style: styles.br, + }, + '\n\n', + ); + }, + }, + codeBlock: { + react(node: SingleASTNode, _output: Output, { ...state }: MarkdownState) { + state.withinText = true; + const n = node as NodeWithStringContent; + return React.createElement( + Text, + { + key: state.key, + style: styles.codeBlock, + }, + n.content, + ); + }, + }, + del: { + react(node: SingleASTNode, output: Output, { ...state }: MarkdownState) { + state.withinText = true; + const n = node as NodeWithContent; + return React.createElement( + Text, + { + key: state.key, + style: styles.del, + }, + output(n.content, state), + ); + }, + }, + em: { + react(node: SingleASTNode, output: Output, { ...state }: MarkdownState) { + state.withinText = true; + state.style = { + ...(state.style || {}), + ...styles.em, + }; + const n = node as NodeWithContent; + return React.createElement( + Text, + { + key: state.key, + style: styles.em, + }, + output(n.content, state), + ); + }, + }, + heading: { + match: SimpleMarkdown.blockRegex(/^ *(#{1,6}) *([^\n]+?) *#* *(?:\n *)+/) as MatchFunction, + react(node: SingleASTNode, output: Output, { ...state }: MarkdownState) { + state.withinText = true; + state.withinHeading = true; + + const n = node as HeadingNode; + const dynHeadingStyle = (styles as unknown as HeadingStyles)[`heading${n.level}`]; + + state.style = { + ...(state.style || {}), + ...dynHeadingStyle, + }; + + const ret = React.createElement( + Text, + { + key: state.key, + style: state.style, + }, + output(n.content, state), + ); + return ret; + }, + }, + hr: { + react(_node: SingleASTNode, _output: Output, { ...state }: MarkdownState) { + return React.createElement(View, { key: state.key, style: styles.hr }); + }, + }, + image: { + // You intentionally disable parsing images; keep the shape + match: (() => null) as unknown as MatchFunction, + }, + inlineCode: { + parse: parseCaptureInline, + react(node: SingleASTNode, output: Output, { ...state }: MarkdownState) { + state.withinText = true; + const n = node as NodeWithContent; + return React.createElement( + Text, + { + key: state.key, + style: styles.inlineCode, + }, + output(n.content, state), + ); + }, + }, + link: { + match: SimpleMarkdown.inlineRegex( + new RegExp('^\\[(' + LINK_INSIDE + ')\\]\\(' + LINK_HREF_AND_TITLE + '\\)'), + ) as MatchFunction, + react(node: SingleASTNode, output: Output, { ...state }: MarkdownState) { + state.withinLink = true; + const n = node as NodeWithContent & TargetNode; + const onPress = () => pressHandler(n.target); + const link = React.createElement( + Text, + { + key: state.key, + onPress, + style: styles.autolink, + }, + output(n.content, state), + ); + state.withinLink = false; + return link; + }, + }, + list: { + react(node: SingleASTNode, output: Output, { ...state }: MarkdownState) { + let numberIndex = 1; + const n = node as ListNode; + + const items = map(n.items as SingleASTNode[][], (item, i) => { + let bullet: React.ReactNode; + state.withinList = false; + + if (n.ordered) { + bullet = React.createElement( + Text, + { key: 0, style: [styles.text, styles.listItemNumber] }, + numberIndex + '. ', + ); + } else { + bullet = React.createElement( + Text, + { key: 0, style: [styles.text, styles.listItemBullet] }, + '\u2022 ', + ); + } + + if ((item as SingleASTNode[]).length> 1) { + if ((item as SingleASTNode[])[1].type === 'list') { + state.withinList = true; + } + } + + const content = output(item as unknown as SingleASTNode[], state); + + let listItem: React.ReactNode; + if ( + includes(['text', 'paragraph', 'strong'], (head(item) || {}).type) && + state.withinList === false + ) { + state.withinList = true; + listItem = React.createElement( + Text, + { + key: 1, + style: [styles.listItemText, { marginBottom: 0 }], + }, + content, + ); + } else { + listItem = React.createElement( + View, + { + key: 1, + style: styles.listItemText, + }, + content, + ); + } + state.withinList = false; + numberIndex++; + + return React.createElement( + View, + { + key: i, + style: styles.listRow, + }, + [bullet, listItem], + ); + }); + + return React.createElement(View, { key: state.key, style: styles.list }, items); + }, + }, + mailto: { + react(node: SingleASTNode, output: Output, { ...state }: MarkdownState) { + state.withinText = true; + const n = node as NodeWithContent; + return React.createElement( + Text, + { + key: state.key, + onPress: noop, + style: styles.autolink, + }, + output(n.content, state), + ); + }, + }, + newline: { + react(_node: SingleASTNode, _output: Output, { ...state }: MarkdownState) { + return React.createElement( + Text, + { + key: state.key, + style: styles.newline, + }, + '\n', + ); + }, + }, + paragraph: { + react(node: SingleASTNode, output: Output, { ...state }: MarkdownState) { + const n = node as NodeWithContent; + let paragraphStyle: TextStyle | (TextStyle | undefined)[] | undefined = styles.paragraph; + + // Allow image to drop in next line within the paragraph + if (some(n.content, { type: 'image' })) { + state.withinParagraphWithImage = true; + const paragraph = React.createElement( + View, + { + key: state.key, + style: styles.paragraphWithImage, + }, + output(n.content, state), + ); + state.withinParagraphWithImage = false; + return paragraph; + } else if (size(n.content) < 3 && some(n.content, { type: 'strong' })) { + // center for Strong-only content + paragraphStyle = styles.paragraphCenter; + } + if (state.withinList) { + paragraphStyle = [paragraphStyle, styles.noMargin]; + } + return React.createElement( + Text, + { + key: state.key, + style: paragraphStyle, + }, + output(n.content, state), + ); + }, + }, + strong: { + react(node: SingleASTNode, output: Output, { ...state }: MarkdownState) { + state.withinText = true; + state.style = { + ...(state.style || {}), + ...styles.strong, + }; + const n = node as NodeWithContent; + return React.createElement( + Text, + { + key: state.key, + style: state.style, + }, + output(n.content, state), + ); + }, + }, + sublist: { + react(node: SingleASTNode, output: Output, { ...state }: MarkdownState) { + const n = node as ListNode; + + const items = map(n.items as SingleASTNode[][], (item, i) => { + let bullet: React.ReactNode; + if (n.ordered) { + bullet = React.createElement( + Text, + { key: 0, style: [styles.text, styles.listItemNumber] }, + i + 1 + '. ', + ); + } else { + bullet = React.createElement( + Text, + { key: 0, style: [styles.text, styles.listItemBullet] }, + '\u2022 ', + ); + } + + const content = output(item as unknown as SingleASTNode[], state); + let listItem: React.ReactNode; + state.withinList = true; + if (includes(['text', 'paragraph', 'strong'], (head(item) || {}).type)) { + listItem = React.createElement( + Text, + { + key: 1, + style: styles.listItemText, + }, + content, + ); + } else { + listItem = React.createElement( + View, + { + key: 1, + style: styles.listItem, + }, + content, + ); + } + state.withinList = false; + return React.createElement( + View, + { + key: i, + style: styles.listRow, + }, + [bullet, listItem], + ); + }); + + return React.createElement(View, { key: state.key, style: styles.sublist }, items); + }, + }, + table: { + react(node: SingleASTNode, output: Output, { ...state }: MarkdownState) { + const n = node as TableNode; + + const headers = map(n.header, (content, i) => + React.createElement( + Text, + { + key: i, + style: styles.tableHeaderCell, + }, + output(content, state), + ), + ); + + const header = React.createElement(View, { key: -1, style: styles.tableHeader }, headers); + + const rows = map(n.cells, (row, r) => { + const cells = map(row, (content, c) => + React.createElement( + View, + { + key: c, + style: styles.tableRowCell, + }, + output(content, state), + ), + ); + const rowStyles: (TextStyle | ViewStyle | undefined)[] = [styles.tableRow]; + if (n.cells.length - 1 === r) { + rowStyles.push(styles.tableRowLast); + } + return React.createElement(View, { key: r, style: rowStyles }, cells); + }); + + return React.createElement(View, { key: state.key, style: styles.table }, [header, rows]); + }, + }, + text: { + react(node: SingleASTNode, _output: Output, { ...state }: MarkdownState) { + const n = node as NodeWithStringContent; + let textStyle: TextStyle | (TextStyle | ViewStyle | undefined)[] = { + ...styles.text, + ...(state.style || {}), + }; + + if (state.withinLink) { + textStyle = [styles.text, styles.autolink]; + } + + if (state.withinQuote) { + textStyle = [styles.text, styles.blockQuoteText]; + } + + return React.createElement( + Text, + { + key: state.key, + style: textStyle, + }, + n.content, + ); + }, + }, + u: { + // u will do the same as strong, to avoid the View nested inside text problem + react(node: SingleASTNode, output: Output, { ...state }: MarkdownState) { + state.withinText = true; + state.style = { + ...(state.style || {}), + ...styles.u, + }; + const n = node as NodeWithContent; + return React.createElement( + Text, + { + key: state.key, + style: styles.strong, + }, + output(n.content, state), + ); + }, + }, + url: { + react(node: SingleASTNode, output: Output, { ...state }: MarkdownState) { + state.withinText = true; + const n = node as NodeWithContent & TargetNode; + const onPress = () => pressHandler(n.target); + return React.createElement( + Text, + { + key: state.key, + onPress, + style: styles.autolink, + }, + output(n.content, state), + ); + }, + }, + } as unknown as OutputRules; +}; diff --git a/package/src/components/Message/MessageSimple/utils/markdown/styles.ts b/package/src/components/Message/MessageSimple/utils/markdown/styles.ts new file mode 100644 index 0000000000..a7da1aa678 --- /dev/null +++ b/package/src/components/Message/MessageSimple/utils/markdown/styles.ts @@ -0,0 +1,177 @@ +import { Dimensions, Platform, StyleSheet } from 'react-native'; + +export default StyleSheet.create({ + autolink: { + color: 'blue', + }, + bgImage: { + bottom: 0, + flex: 1, + left: 0, + position: 'absolute', + right: 0, + top: 0, + }, + bgImageView: { + flex: 1, + overflow: 'hidden', + }, + blockQuoteSection: { + flexDirection: 'row', + }, + blockQuoteSectionBar: { + backgroundColor: '#DDDDDD', + height: null, + marginRight: 15, + width: 3, + }, + blockQuoteText: { + color: 'grey', + }, + codeBlock: { + backgroundColor: '#DDDDDD', + fontFamily: Platform.OS === 'ios' ? 'Courier' : 'Monospace', + fontWeight: '500', + }, + del: { + textDecorationLine: 'line-through', + textDecorationStyle: 'solid', + }, + em: { + fontStyle: 'italic', + }, + heading: { + fontWeight: '200', + }, + heading1: { + fontSize: 32, + }, + heading2: { + fontSize: 24, + }, + heading3: { + fontSize: 18, + }, + heading4: { + fontSize: 16, + }, + heading5: { + fontSize: 13, + }, + heading6: { + fontSize: 11, + }, + hr: { + backgroundColor: '#cccccc', + height: 1, + }, + image: { + alignSelf: 'center', + height: 200, + resizeMode: 'contain', + width: Dimensions.get('window').width - 30, + }, + imageBox: { + flex: 1, + resizeMode: 'cover', + }, + inlineCode: { + backgroundColor: '#eeeeee', + borderColor: '#dddddd', + borderRadius: 3, + borderWidth: 1, + fontFamily: Platform.OS === 'ios' ? 'Courier' : 'Monospace', + fontWeight: 'bold', + }, + list: {}, + listItem: { + flexDirection: 'row', + }, + listItemBullet: { + fontSize: 20, + lineHeight: 20, + }, + listItemNumber: { + fontWeight: 'normal', + }, + listItemText: { + flex: 1, + }, + listRow: { + flexDirection: 'row', + }, + noMargin: { + marginBottom: 0, + marginTop: 0, + }, + paragraph: { + alignItems: 'flex-start', + flexDirection: 'row', + flexWrap: 'wrap', + justifyContent: 'flex-start', + marginBottom: 10, + marginTop: 10, + }, + paragraphCenter: { + alignItems: 'flex-start', + flexDirection: 'row', + flexWrap: 'wrap', + justifyContent: 'center', + marginBottom: 10, + marginTop: 10, + textAlign: 'center', + }, + paragraphWithImage: { + alignItems: 'flex-start', + flex: 1, + justifyContent: 'flex-start', + marginBottom: 10, + marginTop: 10, + }, + strong: { + fontWeight: 'bold', + }, + sublist: { + paddingLeft: 20, + width: Dimensions.get('window').width - 60, + }, + table: { + borderColor: '#222222', + borderRadius: 3, + borderWidth: 1, + }, + tableHeader: { + backgroundColor: '#222222', + flexDirection: 'row', + justifyContent: 'space-around', + }, + tableHeaderCell: { + color: '#ffffff', + fontWeight: 'bold', + padding: 5, + }, + tableRow: { + borderColor: '#222222', + flexDirection: 'row', + justifyContent: 'space-around', + }, + tableRowCell: { + padding: 5, + }, + tableRowLast: { + borderColor: 'transparent', + }, + text: { + color: '#222222', + }, + textRow: { + flexDirection: 'row', + }, + u: { + borderBottomWidth: 1, + borderColor: '#222222', + }, + view: { + alignSelf: 'stretch', + }, +}); diff --git a/package/src/components/Message/MessageSimple/utils/renderText.tsx b/package/src/components/Message/MessageSimple/utils/renderText.tsx index 7ec1599f50..afc7a60221 100644 --- a/package/src/components/Message/MessageSimple/utils/renderText.tsx +++ b/package/src/components/Message/MessageSimple/utils/renderText.tsx @@ -10,25 +10,24 @@ import { } from 'react-native'; import { Gesture, GestureDetector } from 'react-native-gesture-handler'; -// @ts-expect-error -import Markdown from 'react-native-markdown-package'; + import Animated, { clamp, scrollTo, useAnimatedRef, useSharedValue } from 'react-native-reanimated'; -import { - DefaultRules, - defaultRules, +import SimpleMarkdown, { MatchFunction, NodeOutput, Output, ParseFunction, - parseInline, SingleASTNode, State, -} from 'simple-markdown'; +} from '@khanacademy/simple-markdown'; + +const { defaultRules, parseInline } = SimpleMarkdown; import type { LocalMessage, UserResponse } from 'stream-chat'; import { generateMarkdownText } from './generateMarkdownText'; +import Markdown, { MarkdownRules } from './markdown'; import type { MessageContextValue } from '../../../../contexts/messageContext/MessageContext'; import type { Colors, MarkdownStyle } from '../../../../contexts/themeContext/utils/theme'; @@ -155,8 +154,6 @@ const mentionsParseFunction: ParseFunction = (capture, parse, state) => ({ content: parseInline(parse, capture[0], state), }); -export type MarkdownRules = Partial; - export type RenderTextParams = Partial< Pick > & { @@ -450,7 +447,7 @@ export const renderText = (params: RenderTextParams) => { }-${JSON.stringify(colors)}`} onLink={onLink} rules={{ - ...customRules, + ...(customRules as unknown as MarkdownRules), ...markdownRules, }} styles={styles} diff --git a/package/src/contexts/messagesContext/MessagesContext.tsx b/package/src/contexts/messagesContext/MessagesContext.tsx index f919028760..61b89e13ca 100644 --- a/package/src/contexts/messagesContext/MessagesContext.tsx +++ b/package/src/contexts/messagesContext/MessagesContext.tsx @@ -45,7 +45,7 @@ import type { MessageTextProps } from '../../components/Message/MessageSimple/Me import { MessageTimestampProps } from '../../components/Message/MessageSimple/MessageTimestamp'; import { ReactionListBottomProps } from '../../components/Message/MessageSimple/ReactionList/ReactionListBottom'; import type { ReactionListTopProps } from '../../components/Message/MessageSimple/ReactionList/ReactionListTop'; -import type { MarkdownRules } from '../../components/Message/MessageSimple/utils/renderText'; +import type { MarkdownRules } from '../../components/Message/MessageSimple/utils/markdown'; import type { MessageActionsParams } from '../../components/Message/utils/messageActions'; import type { DateHeaderProps } from '../../components/MessageList/DateHeader'; import type { InlineDateSeparatorProps } from '../../components/MessageList/InlineDateSeparator'; diff --git a/package/yarn.lock b/package/yarn.lock index 0e0cfcab86..7916a756cd 100644 --- a/package/yarn.lock +++ b/package/yarn.lock @@ -1889,6 +1889,18 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@khanacademy/perseus-utils@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@khanacademy/perseus-utils/-/perseus-utils-2.1.0.tgz#a405f5e740e73a1345f2b172ff34260b6d87f57e" + integrity sha512-QgN9qW1hnoCGkErSVwCJ5SmWAqlcgXA3TvtMjsyv8LiYbAS4/ZMQOHIuq5P7u0t1i6tp02w7Qjbz/COsxyHg/A== + +"@khanacademy/simple-markdown@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@khanacademy/simple-markdown/-/simple-markdown-2.1.0.tgz#7fcbbcb4d0eda7d0dfb09c2ae35f0b9eb1e07dcf" + integrity sha512-U9yemDLqP3ehAhUdBhzVfZe9h10wWbkrIkfFf2ejSO/cUSFZwjt+KRfZtZK6q2HY+RkNYUjALDpwJu7LkrY1gw== + dependencies: + "@khanacademy/perseus-utils" "2.1.0" + "@napi-rs/wasm-runtime@^0.2.11": version "0.2.11" resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.11.tgz#192c1610e1625048089ab4e35bc0649ce478500e" @@ -2340,7 +2352,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@>=16.0.0", "@types/react@^19.1.0": +"@types/react@*", "@types/react@^19.1.0": version "19.1.8" resolved "https://registry.yarnpkg.com/@types/react/-/react-19.1.8.tgz#ff8395f2afb764597265ced15f8dddb0720ae1c3" integrity sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g== @@ -6414,7 +6426,7 @@ lodash.throttle@^4.1.1: resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" integrity sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ== -lodash@^4.17.15, lodash@^4.17.21: +lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -7568,7 +7580,7 @@ prompts@^2.4.2: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.5.10, prop-types@^15.8.1: +prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -7702,22 +7714,6 @@ react-native-is-edge-to-edge@1.1.7: resolved "https://registry.yarnpkg.com/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.1.7.tgz#28947688f9fafd584e73a4f935ea9603bd9b1939" integrity sha512-EH6i7E8epJGIcu7KpfXYXiV2JFIYITtq+rVS8uEb+92naMRBdxhTuS8Wn2Q7j9sqyO0B+Xbaaf9VdipIAmGW4w== -react-native-lightbox@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/react-native-lightbox/-/react-native-lightbox-0.7.0.tgz#e52b4d7fcc141f59d7b23f0180de535e35b20ec9" - integrity sha512-HS3T4WlCd0Gb3us2d6Jse5m6KjNhngnKm35Wapq30WtQa9s+/VMmtuktbGPGaWtswcDyOj6qByeJBw9W80iPCA== - dependencies: - prop-types "^15.5.10" - -react-native-markdown-package@1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/react-native-markdown-package/-/react-native-markdown-package-1.8.2.tgz#19db1047e174077f9b9f80303938c775b1c223c0" - integrity sha512-F3z/p0XfY6Nu9NlXQx1pYcPdz7Y37NRcAKTN+yb9nwRi8BW75mdc3uaBrM13PDVUlL0hbfTL7FuoAdSbsyB5vg== - dependencies: - lodash "^4.17.15" - react-native-lightbox "^0.7.0" - simple-markdown "^0.7.1" - react-native-monorepo-config@^0.1.8: version "0.1.9" resolved "https://registry.yarnpkg.com/react-native-monorepo-config/-/react-native-monorepo-config-0.1.9.tgz#1caacc259a2b4ccf5162c14c6f5f457ae9adec40" @@ -8258,13 +8254,6 @@ simple-get@^4.0.0: once "^1.3.1" simple-concat "^1.0.0" -simple-markdown@^0.7.1: - version "0.7.3" - resolved "https://registry.yarnpkg.com/simple-markdown/-/simple-markdown-0.7.3.tgz#e32150b2ec6f8287197d09869fd928747a9c5640" - integrity sha512-uGXIc13NGpqfPeFJIt/7SHHxd6HekEJYtsdoCM06mEBPL9fQH/pSD7LRM6PZ7CKchpSvxKL4tvwMamqAaNDAyg== - dependencies: - "@types/react" ">=16.0.0" - sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"

AltStyle によって変換されたページ (->オリジナル) /