@@ -2,6 +2,11 @@ import { Button } from "../../components/ui/Button";
22import { QuestionAttachments } from "./QuestionAttachments" ;
33import type { EnvironmentId , RuntimeRequestId } from "@t3tools/contracts" ;
44import type { ThreadUserInputQuestion } from "@t3tools/client-runtime/state/thread-requests" ;
5+ import {
6+ markdownProseDirection ,
7+ textDirection ,
8+ type TextDirection ,
9+ } from "@t3tools/shared/textDirection" ;
510import { useRef } from "react" ;
611import { Platform , Pressable , ScrollView , View , type LayoutChangeEvent } from "react-native" ;
712import Animated , {
@@ -61,14 +66,31 @@ export interface PendingUserInputCardProps {
6166/** Android cannot hit-test overlays beyond their parent's bounds; render its expanded card in-flow. */
6267const EXPANDED_CARD_IS_OVERLAY = Platform . OS === "ios" ;
6368
64- function QuestionText ( props : { readonly markdown : string ; readonly environmentId : EnvironmentId } ) {
69+ function proseDirectionStyle ( text : string , setDirection : TextDirection | undefined ) {
70+ const direction = textDirection ( text ) ;
71+ return {
72+ ...( direction ? { direction, writingDirection : direction } : null ) ,
73+ textAlign : ( direction === "rtl" ) === ( setDirection === "rtl" ) ? "left" : "right" ,
74+ } as const ;
75+ }
76+
77+ function QuestionText ( props : {
78+ readonly markdown : string ;
79+ readonly environmentId : EnvironmentId ;
80+ readonly setDirection : TextDirection | undefined ;
81+ } ) {
6582 const styles = useMarkdownPreviewStyles ( ) ;
6683 const markdown = useLiveThreadLinkLabels ( props . markdown , props . environmentId ) ;
6784 const openLink = useOpenPullRequest ( ) ;
6885 return (
6986 < SelectableMarkdownText
7087 markdown = { markdown }
7188 textStyle = { styles . nativeTextStyle }
89+ writingDirection = { markdownProseDirection ( markdown ) }
90+ // Android's renderer aligns relative to each paragraph's direction.
91+ textAlign = {
92+ Platform . OS === "android" ? undefined : props . setDirection === "rtl" ? "right" : "left"
93+ }
7294 onLinkPress = { ( href ) => void openLink ( href ) }
7395 />
7496 ) ;
@@ -79,6 +101,15 @@ export function PendingUserInputCard(props: PendingUserInputCardProps) {
79101 const canRespond = props . pendingUserInput . responseCapability !== "not_resumable" ;
80102 const isResponding = props . respondingUserInputId === props . pendingUserInput . requestId ;
81103 const responseDisabled = ! canRespond || isResponding ;
104+ const setDirection = markdownProseDirection (
105+ props . pendingUserInput . questions
106+ . flatMap ( ( question ) => [
107+ question . header ,
108+ question . question ,
109+ ...question . options . flatMap ( ( option ) => [ option . label , option . description ?? "" ] ) ,
110+ ] )
111+ . join ( " " ) ,
112+ ) ;
82113
83114 const cardCoverage = props . cardCoverage ;
84115 const barHeightRef = useRef ( 0 ) ;
@@ -217,10 +248,17 @@ export function PendingUserInputCard(props: PendingUserInputCardProps) {
217248 const draft = props . drafts [ question . id ] ;
218249 return (
219250 < View key = { question . id } className = "gap-2 pt-1" >
220- < Text className = "font-t3-bold text-xs uppercase tracking-[1px] text-foreground-muted" >
251+ < Text
252+ className = "font-t3-bold text-xs uppercase tracking-[1px] text-foreground-muted"
253+ style = { proseDirectionStyle ( question . header , setDirection ) }
254+ >
221255 { question . header }
222256 </ Text >
223- < QuestionText markdown = { question . question } environmentId = { props . environmentId } />
257+ < QuestionText
258+ markdown = { question . question }
259+ environmentId = { props . environmentId }
260+ setDirection = { setDirection }
261+ />
224262 < View className = "gap-2" >
225263 { question . options . map ( ( option ) => {
226264 const optionValue = option . value ?? option . label . trim ( ) ;
@@ -251,11 +289,15 @@ export function PendingUserInputCard(props: PendingUserInputCardProps) {
251289 "font-t3-bold text-sm" ,
252290 selected ? "text-foreground" : "text-foreground-secondary" ,
253291 ) }
292+ style = { proseDirectionStyle ( option . label , setDirection ) }
254293 >
255294 { option . label }
256295 </ Text >
257296 { description ? (
258- < Text className = "font-sans text-sm leading-5 text-foreground-muted" >
297+ < Text
298+ className = "font-sans text-sm leading-5 text-foreground-muted"
299+ style = { proseDirectionStyle ( description , setDirection ) }
300+ >
259301 { description }
260302 </ Text >
261303 ) : null }
0 commit comments