From c3e2df09e7449dad7d4c01a9cc26ab18bb1c05e0 Mon Sep 17 00:00:00 2001 From: Majid Asgari Date: Tue, 7 Jul 2026 03:49:28 +0330 Subject: [PATCH] feat(desktop): support RTL direction and alignment for non-LTR text in markdown and editor Implement dynamic Right-to-Left (RTL) text direction and right-alignment specifically for the desktop client when rendering or typing RTL scripts (Persian, Arabic, Hebrew, etc.). Why: - In the desktop application, messages and query inputs containing RTL characters currently default to LTR alignment and direction, which highly degrades readability. What: - Guarded all RTL logic behind a `window.api` check (exposed by the Electron preload script) to ensure it only applies within the desktop client wrapper. - Extended the Markdown renderer's `decorate` function in `session-ui` to dynamically apply `direction: rtl` and `text-align: right` on paragraphs (`p`), table cells (`td`, `th`), list items (`li`), and headings (`h1`-`h6`) containing RTL script characters. - Structured Q&A tool component rendering elements in `message-part.tsx` are also properly styled using conditional direction and alignment attributes. - Added a reactive `isRtl` memo inside the composer `PromptInput` component to automatically toggle the input's direction/alignment as soon as the user starts typing in an RTL language. Impact: - Zero side-effects on the web application or browser builds, keeping the web client's behavior and performance untouched. --- packages/app/src/components/prompt-input.tsx | 13 ++++++++++- .../session-ui/src/components/markdown.tsx | 12 ++++++++++ .../src/components/message-part.tsx | 22 +++++++++++++++++-- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index 75ec3b7be52b..04dc096db1fb 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -224,6 +224,16 @@ export const PromptInput: Component = (props) => { const inset = 56 const space = `${inset}px` + const isRtl = createMemo(() => { + if (typeof window === "undefined" || !(window as any).api) return false + const text = prompt + .current() + .map((part) => ("content" in part ? part.content : "")) + .join("") + const rtlRegex = /[\u0590-\u05FF\u0600-\u06FF\u0700-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]/ + return rtlRegex.test(text) + }) + const scrollCursorIntoView = () => { const container = scrollRef const selection = window.getSelection() @@ -1608,6 +1618,7 @@ export const PromptInput: Component = (props) => { "[&_[data-type=agent]]:text-syntax-type": true, "font-mono!": store.mode === "shell", }} + style={isRtl() ? { direction: "rtl", "text-align": "right" } : undefined} />
= (props) => { "[&_[data-type=agent]]:text-syntax-type": true, "font-mono!": store.mode === "shell", }} - style={{ "padding-bottom": space }} + style={isRtl() ? { direction: "rtl", "text-align": "right", "padding-bottom": space } : { "padding-bottom": space }} />
answers()[i()] ?? [] return (
-
{q.question}
-
{answer().join(", ") || i18n.t("ui.question.answer.none")}
+
+ {q.question} +
+
+ {answer().join(", ") || i18n.t("ui.question.answer.none")} +
) }}