fix(app): support bidi text input (#42413)

Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com>
This commit is contained in:
opencode-agent[bot]
2026-08-14 07:48:58 +10:00
committed by GitHub
parent d9700c4cd5
commit 8f3e86cf4e
7 changed files with 23 additions and 2 deletions
+6 -1
View File
@@ -1518,6 +1518,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
role="textbox"
aria-multiline="true"
aria-label={placeholder()}
dir={store.mode === "normal" ? "auto" : "ltr"}
contenteditable="true"
autocapitalize={store.mode === "normal" ? "sentences" : "off"}
autocorrect={store.mode === "normal" ? "on" : "off"}
@@ -1539,7 +1540,11 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
"[&_[data-type=agent]]:text-syntax-type": true,
"font-mono!": store.mode === "shell",
}}
style={{ "padding-bottom": space }}
style={{
"padding-bottom": space,
"unicode-bidi": store.mode === "normal" ? "plaintext" : undefined,
"text-align": "start",
}}
/>
<div
class="absolute top-0 inset-x-0 pl-3 pr-2 pt-2 text-14-regular text-text-weak pointer-events-none whitespace-nowrap truncate"
@@ -251,6 +251,7 @@ export function TabNavItem(props: {
}}
data-slot="tab-title"
data-titlebar-tab-title
dir="auto"
class="min-w-0 flex-1 outline-none leading-4"
classList={{
"overflow-hidden text-clip whitespace-nowrap": !editing(),
@@ -601,7 +601,9 @@ export const SessionQuestionDock: Component<{ request: FormInfo; onSubmit: () =>
<Mark multi={multi()} picked={on()} onClick={toggleCustomMark} />
<span data-slot="question-option-main">
<span data-slot="option-label">{customLabel()}</span>
<span data-slot="option-description">{input() || customPlaceholder()}</span>
<span data-slot="option-description" dir="auto">
{input() || customPlaceholder()}
</span>
</span>
</button>
}
@@ -632,10 +634,12 @@ export const SessionQuestionDock: Component<{ request: FormInfo; onSubmit: () =>
<textarea
ref={focusCustom}
data-slot="question-custom-input"
dir="auto"
placeholder={customPlaceholder()}
value={input()}
rows={1}
disabled={sending()}
style={{ "unicode-bidi": "plaintext", "text-align": "start" }}
onKeyDown={(e) => {
if (e.key === "Escape") {
e.preventDefault()
@@ -1159,6 +1159,7 @@ function MessageTimelineView(
titleRef = el
}}
data-slot="session-title-child"
dir="auto"
value={title.draft}
disabled={props.pending.rename()}
classList={{
@@ -1170,6 +1171,7 @@ function MessageTimelineView(
"--inline-input-shadow": props.data.newLayoutDesigns()
? "none"
: "var(--shadow-xs-border-select)",
"text-align": "start",
}}
onInput={(event) => setTitle("draft", event.currentTarget.value)}
onKeyDown={(event) => {
@@ -296,9 +296,11 @@ export const LineCommentEditor = (props: LineCommentEditorProps) => {
refs.textarea = el
}}
data-slot="line-comment-textarea"
dir="auto"
rows={split.rows ?? 3}
placeholder={split.placeholder ?? i18n.t("ui.lineComment.placeholder")}
value={split.value}
style={{ "unicode-bidi": "plaintext", "text-align": "start" }}
on:input={(e) => {
const value = (e.currentTarget as HTMLTextAreaElement).value
split.onInput(value)
@@ -154,6 +154,7 @@ export function PromptInputV2(props: PromptInputV2Props) {
role="textbox"
aria-multiline="true"
aria-label={i18n.t("ui.promptInput.label")}
dir={state.mode === "normal" ? "auto" : "ltr"}
contenteditable={!props.disabled && !props.readOnly}
autocapitalize={state.mode === "normal" ? "sentences" : "off"}
autocorrect={state.mode === "normal" ? "on" : "off"}
@@ -162,6 +163,10 @@ export function PromptInputV2(props: PromptInputV2Props) {
autocomplete="off"
class="relative z-10 block min-h-[60px] max-h-[180px] w-full overflow-y-auto whitespace-pre-wrap bg-transparent px-4 pt-4 pb-2 text-[13px] font-[440] leading-5 text-v2-text-text-base focus:outline-none empty:before:content-['\200B'] [&_[data-mention=file]]:text-syntax-property [&_[data-mention=agent]]:text-syntax-type [&_[data-mention=reference]]:text-syntax-keyword"
classList={{ "font-mono!": state.mode === "shell", "opacity-50": props.disabled }}
style={{
"unicode-bidi": state.mode === "normal" ? "plaintext" : undefined,
"text-align": "start",
}}
onInput={(event) => {
const cursor = promptInputV2Cursor(event.currentTarget)
const prompt = parsePromptInputV2Editor(event.currentTarget)
@@ -212,9 +212,11 @@ export function LineCommentEditorV2(props: LineCommentEditorV2Props) {
textareaRef = el
}}
data-slot="line-comment-v2-textarea"
dir="auto"
rows={local.rows ?? 3}
placeholder={local.placeholder ?? i18n.t("ui.lineComment.contextPlaceholder")}
value={local.value}
style={{ "unicode-bidi": "plaintext", "text-align": "start" }}
onInput={(e) => {
local.onInput(e.currentTarget.value)
syncMention()