feat(app): add minimal RTL support (#40372)

Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com>
This commit is contained in:
opencode-agent[bot]
2026-08-04 05:08:51 +00:00
committed by GitHub
parent 5535f7d6d1
commit 5f29a82190
16 changed files with 80 additions and 36 deletions
@@ -215,6 +215,7 @@ export default function FileTreeV2(props: {
<div
ref={setRoot}
data-component="file-tree-v2"
dir="ltr"
data-total-rows={live() ? rows().length : model()!.total}
class="group/file-tree-v2"
style={{ position: "relative", height: `${virtualizer.getTotalSize()}px` }}
+1
View File
@@ -742,6 +742,7 @@ export const Terminal = (props: TerminalProps) => {
<div
ref={container}
data-component="terminal"
dir="ltr"
data-prevent-autofocus
tabIndex={-1}
style={{ "background-color": terminalColors().background }}
+6 -2
View File
@@ -36,6 +36,8 @@ export type Locale =
| "fi"
| "sv"
const RTL_LOCALES: ReadonlySet<Locale> = new Set(["ar", "ur", "pa"])
type RawDictionary = typeof en & typeof uiEn
type Dictionary = i18n.Flatten<RawDictionary>
type PluralKey =
@@ -315,8 +317,10 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
createEffect(() => {
if (typeof document !== "object") return
document.documentElement.lang = locale()
document.cookie = cookie(locale())
const value = locale()
document.documentElement.lang = value
document.documentElement.dir = RTL_LOCALES.has(value) ? "rtl" : "ltr"
document.cookie = cookie(value)
})
return {
+15 -14
View File
@@ -2278,7 +2278,7 @@ export default function LegacyLayout(props: ParentProps) {
data-component="sidebar-nav-desktop"
classList={{
"hidden xl:block": true,
"absolute inset-y-0 left-0": true,
"absolute inset-y-0 start-0": true,
"z-10": true,
}}
style={{ width: `${side()}px` }}
@@ -2301,7 +2301,7 @@ export default function LegacyLayout(props: ParentProps) {
<Show when={layout.sidebar.opened()}>
<div
class="hidden xl:block absolute inset-y-0 z-30 w-0 overflow-visible"
style={{ left: `${side()}px` }}
style={{ "inset-inline-start": `${side()}px` }}
onPointerDown={() => setState("sizing", true)}
>
<ResizeHandle
@@ -2320,8 +2320,8 @@ export default function LegacyLayout(props: ParentProps) {
</Show>
<div
class="hidden xl:block pointer-events-none absolute top-0 right-0 z-0 border-t border-border-weaker-base"
style={{ left: "calc(4rem + 12px)" }}
class="hidden xl:block pointer-events-none absolute top-0 end-0 z-0 border-t border-border-weaker-base"
style={{ "inset-inline-start": "calc(4rem + 12px)" }}
/>
<div class="xl:hidden">
@@ -2339,9 +2339,9 @@ export default function LegacyLayout(props: ParentProps) {
aria-label={language.t("sidebar.nav.projectsAndSessions")}
data-component="sidebar-nav-mobile"
classList={{
"@container fixed top-10 bottom-0 left-0 z-50 w-full max-w-[400px] overflow-hidden border-r border-border-weaker-base bg-background-base transition-transform duration-200 ease-out": true,
"@container fixed top-10 bottom-0 start-0 z-50 w-full max-w-[400px] overflow-hidden border-e border-border-weaker-base bg-background-base transition-transform duration-200 ease-out": true,
"translate-x-0": layout.mobileSidebar.opened(),
"-translate-x-full": !layout.mobileSidebar.opened(),
"ltr:-translate-x-full rtl:translate-x-full": !layout.mobileSidebar.opened(),
}}
onClick={(e) => e.stopPropagation()}
>
@@ -2352,9 +2352,9 @@ export default function LegacyLayout(props: ParentProps) {
<div
classList={{
"absolute inset-0": true,
"xl:inset-y-0 xl:right-0 xl:left-[var(--main-left)]": true,
"xl:inset-y-0 xl:end-0 xl:start-[var(--main-left)]": true,
"z-20": true,
"transition-[left] duration-200 ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[left] motion-reduce:transition-none":
"transition-[inset-inline-start] duration-200 ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[inset-inline-start] motion-reduce:transition-none":
!state.sizing,
}}
style={{
@@ -2363,7 +2363,7 @@ export default function LegacyLayout(props: ParentProps) {
>
<main
classList={{
"size-full overflow-x-hidden flex flex-col items-start contain-strict border-t border-border-weak-base bg-background-base xl:border-l xl:rounded-tl-[12px]": true,
"size-full overflow-x-hidden flex flex-col items-start contain-strict border-t border-border-weak-base bg-background-base xl:border-s xl:rounded-ss-[12px]": true,
}}
>
<Show when={!autoselecting.loading} fallback={<div class="size-full" />}>
@@ -2374,9 +2374,10 @@ export default function LegacyLayout(props: ParentProps) {
<div
classList={{
"hidden xl:flex absolute inset-y-0 left-16 z-30": true,
"hidden xl:flex absolute inset-y-0 start-16 z-30": true,
"opacity-100 translate-x-0 pointer-events-auto": state.peeked && !layout.sidebar.opened(),
"opacity-0 -translate-x-2 pointer-events-none": !state.peeked || layout.sidebar.opened(),
"opacity-0 ltr:-translate-x-2 rtl:translate-x-2 pointer-events-none":
!state.peeked || layout.sidebar.opened(),
"transition-[opacity,transform] motion-reduce:transition-none": true,
"duration-180 ease-out": state.peeked && !layout.sidebar.opened(),
"duration-120 ease-in": !state.peeked || layout.sidebar.opened(),
@@ -2398,14 +2399,14 @@ export default function LegacyLayout(props: ParentProps) {
<div
classList={{
"hidden xl:block pointer-events-none absolute inset-y-0 right-0 z-25 overflow-hidden": true,
"hidden xl:block pointer-events-none absolute inset-y-0 end-0 z-25 overflow-hidden": true,
"opacity-100 translate-x-0": state.peeked && !layout.sidebar.opened(),
"opacity-0 -translate-x-2": !state.peeked || layout.sidebar.opened(),
"opacity-0 ltr:-translate-x-2 rtl:translate-x-2": !state.peeked || layout.sidebar.opened(),
"transition-[opacity,transform] motion-reduce:transition-none": true,
"duration-180 ease-out": state.peeked && !layout.sidebar.opened(),
"duration-120 ease-in": !state.peeked || layout.sidebar.opened(),
}}
style={{ left: `calc(4rem + ${panel()}px)` }}
style={{ "inset-inline-start": `calc(4rem + ${panel()}px)` }}
>
<div class="h-full w-px" style={{ "box-shadow": "var(--shadow-sidebar-overlay)" }} />
</div>
+1 -1
View File
@@ -2282,7 +2282,7 @@ export default function Page() {
<div onPointerDown={() => size.start()}>
<ResizeHandle
classList={{
"-right-1": settings.general.newLayoutDesigns(),
"-end-1": settings.general.newLayoutDesigns(),
}}
direction="horizontal"
size={sessionPanelResizedWidth()}
@@ -1103,7 +1103,7 @@ export function MessageTimeline(props: {
return (
<TimelineRowFrame row={commentStripRow}>
<div class="w-full px-4 md:px-5 pb-2">
<div class="ml-auto max-w-[82%] overflow-x-auto no-scrollbar">
<div class="ms-auto max-w-[82%] overflow-x-auto no-scrollbar">
<div class="flex w-max min-w-full justify-end gap-2">
<Index each={comments()}>
{(comment) => (