diff --git a/packages/ui/src/components/instance/instance-shell2.tsx b/packages/ui/src/components/instance/instance-shell2.tsx index 84e55578..0263d343 100644 --- a/packages/ui/src/components/instance/instance-shell2.tsx +++ b/packages/ui/src/components/instance/instance-shell2.tsx @@ -81,7 +81,8 @@ interface InstanceShellProps { } const InstanceShell2: Component = (props) => { - const { t } = useI18n() + const { t, locale } = useI18n() + const isRTL = () => locale() === "he" const [sessionSidebarWidth, setSessionSidebarWidth] = createSignal(DEFAULT_SESSION_SIDEBAR_WIDTH) const [rightDrawerWidth, setRightDrawerWidth] = createSignal( @@ -371,7 +372,7 @@ const InstanceShell2: Component = (props) => { sx={{ width: `${sessionSidebarWidth()}px`, flexShrink: 0, - borderRight: "1px solid var(--border-base)", + borderInlineEnd: "1px solid var(--border-base)", backgroundColor: "var(--surface-secondary)", height: "100%", minHeight: 0, @@ -413,7 +414,7 @@ const InstanceShell2: Component = (props) => { const modalProps = container ? { container: container as Element } : undefined return ( = (props) => { "& .MuiDrawer-paper": { width: isPhoneLayout() ? "100vw" : `${sessionSidebarWidth()}px`, boxSizing: "border-box", - borderRight: isPhoneLayout() ? "none" : "1px solid var(--border-base)", + borderInlineEnd: isPhoneLayout() ? "none" : "1px solid var(--border-base)", backgroundColor: "var(--surface-secondary)", backgroundImage: "none", color: "var(--text-primary)", @@ -480,7 +481,7 @@ const InstanceShell2: Component = (props) => { sx={{ width: `${rightDrawerWidth()}px`, flexShrink: 0, - borderLeft: "1px solid var(--border-base)", + borderInlineStart: "1px solid var(--border-base)", backgroundColor: "var(--surface-secondary)", height: "100%", minHeight: 0, @@ -523,7 +524,7 @@ const InstanceShell2: Component = (props) => { const modalProps = container ? { container: container as Element } : undefined return ( = (props) => { "& .MuiDrawer-paper": { width: isPhoneLayout() ? "100vw" : `${rightDrawerWidth()}px`, boxSizing: "border-box", - borderLeft: isPhoneLayout() ? "none" : "1px solid var(--border-base)", + borderInlineStart: isPhoneLayout() ? "none" : "1px solid var(--border-base)", backgroundColor: "var(--surface-secondary)", backgroundImage: "none", color: "var(--text-primary)", diff --git a/packages/ui/src/lib/i18n/index.tsx b/packages/ui/src/lib/i18n/index.tsx index c65e5bc3..4632d096 100644 --- a/packages/ui/src/lib/i18n/index.tsx +++ b/packages/ui/src/lib/i18n/index.tsx @@ -210,6 +210,11 @@ export const I18nProvider: ParentComponent = (props) => { document.documentElement.lang = activeLocale }) + const RTL_LOCALES = new Set(["he"]) + createEffect(() => { + document.documentElement.dir = RTL_LOCALES.has(locale()) ? "rtl" : "ltr" + }) + onCleanup(() => { globalMessages = previousGlobalMessages globalLocale = previousGlobalLocale diff --git a/packages/ui/src/styles/components/directory-browser.css b/packages/ui/src/styles/components/directory-browser.css index bdbccbc0..cc727afa 100644 --- a/packages/ui/src/styles/components/directory-browser.css +++ b/packages/ui/src/styles/components/directory-browser.css @@ -124,7 +124,7 @@ display: flex; align-items: center; gap: var(--space-md); - text-align: left; + text-align: start; background: transparent; border: none; color: var(--text-primary); diff --git a/packages/ui/src/styles/components/settings-screen.css b/packages/ui/src/styles/components/settings-screen.css index 7f6abe56..31805bb9 100644 --- a/packages/ui/src/styles/components/settings-screen.css +++ b/packages/ui/src/styles/components/settings-screen.css @@ -43,7 +43,7 @@ padding: 1.25rem; background: linear-gradient(180deg, color-mix(in oklab, var(--surface-secondary) 92%, var(--accent-primary) 8%), var(--surface-secondary)); - border-right: 1px solid var(--border-base); + border-inline-end: 1px solid var(--border-base); } .settings-screen-nav-header { @@ -121,6 +121,9 @@ color: var(--text-primary); transform: translateX(2px); } +[dir="rtl"] .settings-nav-button[data-selected="true"] { + transform: translateX(-2px); +} .settings-nav-button-icon { width: 1rem; @@ -360,7 +363,7 @@ border: 1px solid var(--border-base); background: var(--surface-base); color: var(--text-primary); - text-align: left; + text-align: start; transition: border-color 140ms ease, background-color 140ms ease, box-shadow 140ms ease, transform 140ms ease; outline: none; cursor: pointer; @@ -418,7 +421,7 @@ } .settings-choice-check { - margin-left: auto; + margin-inline-start: auto; color: var(--accent-primary); opacity: 0; } @@ -488,7 +491,7 @@ .settings-screen-nav { gap: 0.75rem; padding: 1rem; - border-right: none; + border-inline-end: none; border-bottom: 1px solid var(--border-base); } diff --git a/packages/ui/src/styles/messaging/message-base.css b/packages/ui/src/styles/messaging/message-base.css index dc048055..fa9614dd 100644 --- a/packages/ui/src/styles/messaging/message-base.css +++ b/packages/ui/src/styles/messaging/message-base.css @@ -141,13 +141,13 @@ .message-step-start { background-color: var(--message-assistant-bg); - border-left: 4px solid var(--message-assistant-border); + border-inline-start: 4px solid var(--message-assistant-border); margin-top: 0; } .message-step-finish { background-color: var(--message-assistant-bg); - border-left: 4px solid var(--message-assistant-border); + border-inline-start: 4px solid var(--message-assistant-border); margin: 0; } @@ -172,7 +172,7 @@ font-size: 9px; color: var(--text-muted); font-weight: var(--font-weight-medium); - margin-right: 0.35rem; + margin-inline-end: 0.35rem; } .message-step-heading { @@ -335,12 +335,12 @@ .message-step-start { background-color: var(--message-assistant-bg); - border-left: 4px solid var(--message-assistant-border); + border-inline-start: 4px solid var(--message-assistant-border); } .message-step-finish { background-color: var(--message-assistant-bg); - border-left: 4px solid var(--message-assistant-border); + border-inline-start: 4px solid var(--message-assistant-border); } .message-step-heading { @@ -390,7 +390,7 @@ .message-reasoning-card { --reasoning-border-color: var(--border-strong, var(--border-base)); background-color: var(--message-assistant-bg); - border-left: 4px solid var(--message-assistant-border); + border-inline-start: 4px solid var(--message-assistant-border); margin-top: 0; margin-bottom: 0; padding: 0; diff --git a/packages/ui/src/styles/messaging/message-section.css b/packages/ui/src/styles/messaging/message-section.css index 15ded9e5..7e6f2475 100644 --- a/packages/ui/src/styles/messaging/message-section.css +++ b/packages/ui/src/styles/messaging/message-section.css @@ -207,7 +207,7 @@ .message-scroll-button-wrapper { position: absolute; - right: 1rem; + inset-inline-end: 1rem; bottom: 1rem; display: flex; flex-direction: column; @@ -274,7 +274,7 @@ } .message-quote-button + .message-quote-button { - border-left: 1px solid var(--list-item-highlight-border); + border-inline-start: 1px solid var(--list-item-highlight-border); } .message-quote-button:hover { diff --git a/packages/ui/src/styles/messaging/message-selection.css b/packages/ui/src/styles/messaging/message-selection.css index b6e7eb01..28e9df97 100644 --- a/packages/ui/src/styles/messaging/message-selection.css +++ b/packages/ui/src/styles/messaging/message-selection.css @@ -3,7 +3,7 @@ .message-select-checkbox { width: 14px; height: 14px; - margin-right: 0.5rem; + margin-inline-end: 0.5rem; cursor: pointer; accent-color: var(--status-error); flex: 0 0 auto; @@ -134,7 +134,7 @@ } .message-delete-mode-menu { - right: 0; + inset-inline-end: 0; bottom: calc(100% + 6px); min-width: 150px; width: max-content; diff --git a/packages/ui/src/styles/messaging/message-timeline.css b/packages/ui/src/styles/messaging/message-timeline.css index 613b2220..78aeb927 100644 --- a/packages/ui/src/styles/messaging/message-timeline.css +++ b/packages/ui/src/styles/messaging/message-timeline.css @@ -20,7 +20,7 @@ position: absolute; top: 0; bottom: 0; - right: 64px; + inset-inline-end: 64px; width: 1px; background-color: var(--border-muted); pointer-events: none; @@ -32,7 +32,7 @@ } .message-layout--with-timeline::after { - right: 40px; + inset-inline-end: 40px; } } @@ -311,12 +311,12 @@ /* Tool segments that are part of a group get a left accent border. */ .message-timeline-group-child { - border-left: 3px solid color-mix(in oklab, var(--accent-primary) 35%, transparent); + border-inline-start: 3px solid color-mix(in oklab, var(--accent-primary) 35%, transparent); } /* The assistant "parent" at the bottom of a tool group gets the same border. */ .message-timeline-group-parent { - border-left: 3px solid color-mix(in oklab, var(--accent-primary) 35%, transparent); + border-inline-start: 3px solid color-mix(in oklab, var(--accent-primary) 35%, transparent); } /* Extra spacing before the first tool in a group to separate from the @@ -346,7 +346,7 @@ /* Extend the overlay box into the stream so ribs are not relying on overflow-visible behavior (which is brittle around scroll containers). */ --xray-overhang: calc(var(--max-rib-width, 50vw) + 84px); - left: calc(-1 * var(--xray-overhang)); + inset-inline-start: calc(-1 * var(--xray-overhang)); width: calc(100% + var(--xray-overhang)); overflow: hidden; padding: 0.25rem; @@ -374,10 +374,10 @@ .message-timeline-xray-token-label { position: absolute; - right: 100%; + inset-inline-end: 100%; top: 50%; transform: translateY(-50%); - margin-right: 4px; + margin-inline-end: 4px; height: 1.5rem; display: flex; align-items: center; @@ -403,16 +403,25 @@ var(--status-success) calc(100% - var(--segment-weight) * 100%), var(--status-error) calc(var(--segment-weight) * 100%) ); - border-radius: 3px 0 0 3px; + border-start-start-radius: 3px; + border-end-start-radius: 3px; + border-start-end-radius: 0; + border-end-end-radius: 0; transition: width 0.3s ease, background-color 0.3s ease; box-shadow: -2px 0 4px rgba(0, 0, 0, 0.25); } +[dir="rtl"] .message-timeline-relative-bar { + box-shadow: 2px 0 4px rgba(0, 0, 0, 0.25); +} .message-timeline-absolute-bar { height: 3px; width: calc(var(--segment-weight) * var(--max-rib-width, 50vw)); background-color: var(--text-muted); - border-radius: 2px 0 0 2px; + border-start-start-radius: 2px; + border-end-start-radius: 2px; + border-start-end-radius: 0; + border-end-end-radius: 0; transition: width 0.3s ease; opacity: 0.5; position: relative; @@ -425,7 +434,7 @@ .message-timeline-absolute-bar-overflow::before { content: ""; position: absolute; - left: -1px; + inset-inline-start: -1px; top: -3px; bottom: -3px; width: 3px; diff --git a/packages/ui/src/styles/messaging/prompt-input.css b/packages/ui/src/styles/messaging/prompt-input.css index 8cbfb796..bd8df62c 100644 --- a/packages/ui/src/styles/messaging/prompt-input.css +++ b/packages/ui/src/styles/messaging/prompt-input.css @@ -65,8 +65,8 @@ .prompt-input-overlay { position: absolute; bottom: 1rem; - left: 0.75rem; - right: 0.75rem; + inset-inline-start: 0.75rem; + inset-inline-end: 0.75rem; display: flex; flex-wrap: wrap; gap: 0.5rem; @@ -85,7 +85,7 @@ .prompt-nav-buttons { position: absolute; top: 0.25rem; - right: 0.25rem; + inset-inline-end: 0.25rem; bottom: 0.25rem; display: flex; flex-direction: column; diff --git a/packages/ui/src/styles/messaging/tool-call.css b/packages/ui/src/styles/messaging/tool-call.css index 4c504b36..7c54fa36 100644 --- a/packages/ui/src/styles/messaging/tool-call.css +++ b/packages/ui/src/styles/messaging/tool-call.css @@ -5,7 +5,7 @@ .tool-call-message { @apply flex flex-col gap-2 p-3 w-full; background-color: var(--message-tool-bg); - border-left: 4px solid var(--message-tool-border); + border-inline-start: 4px solid var(--message-tool-border); color: inherit; } @@ -105,7 +105,7 @@ .tool-call-header-toggle::before { content: "▶"; font-size: 11px; - margin-right: 0.35rem; + margin-inline-end: 0.35rem; color: var(--text-secondary); } @@ -168,26 +168,26 @@ } .tool-call-summary[data-tool-icon=""]::before { - margin-right: 0; + margin-inline-end: 0; content: ""; } .tool-call-summary[data-tool-icon]:not([data-tool-icon=""])::before { - margin-right: 0.35rem; + margin-inline-end: 0.35rem; } /* ToolState uses status="completed"; keep "success" as a legacy alias. */ .tool-call-status-completed, .tool-call-status-success { - border-left: 3px solid var(--status-success); + border-inline-start: 3px solid var(--status-success); } .tool-call-status-error { - border-left: 3px solid var(--status-error); + border-inline-start: 3px solid var(--status-error); } .tool-call-status-running { - border-left: 3px solid var(--status-warning); + border-inline-start: 3px solid var(--status-warning); } .tool-call-status-running .tool-call-status { @@ -195,7 +195,7 @@ } .tool-call-status-pending { - border-left: 3px solid var(--accent-primary); + border-inline-start: 3px solid var(--accent-primary); } .tool-call-status-pending .tool-call-summary { @@ -267,7 +267,7 @@ .tool-call-io-toggle::before { content: "▶"; font-size: 11px; - margin-right: 0.35rem; + margin-inline-end: 0.35rem; color: var(--text-secondary); } @@ -393,7 +393,7 @@ } .tool-call-awaiting-permission { - border-left-color: var(--status-warning); + border-inline-start-color: var(--status-warning); } .tool-call-permission { @@ -484,7 +484,7 @@ } .tool-call-permission-shortcuts .kbd { - margin-right: 0.25rem; + margin-inline-end: 0.25rem; } .tool-call-permission-queued-text { @@ -693,8 +693,8 @@ gap: var(--space-xs); max-height: calc(4 * var(--tool-call-line-unit, 1.4em)); overflow-y: scroll; - padding-right: 0; - margin-right: 0; + padding-inline-end: 0; + margin-inline-end: 0; scrollbar-gutter: stable both-edges; scrollbar-width: thin; } @@ -843,7 +843,7 @@ .tool-call-error-content { background-color: var(--message-error-bg); - border-left: 3px solid var(--status-error); + border-inline-start: 3px solid var(--status-error); padding: 12px; margin: 8px 0; border-radius: 4px; diff --git a/packages/ui/src/styles/messaging/tool-call/task.css b/packages/ui/src/styles/messaging/tool-call/task.css index 83e435ac..d628d6fc 100644 --- a/packages/ui/src/styles/messaging/tool-call/task.css +++ b/packages/ui/src/styles/messaging/tool-call/task.css @@ -86,7 +86,7 @@ .tool-call-task-summary .tool-call::before { content: ""; position: absolute; - left: 0; + inset-inline-start: 0; top: 0; bottom: 0; width: 3px; @@ -117,7 +117,7 @@ align-items: center; gap: 0.4rem; padding: 0.35rem 0.5rem 0.35rem 0.75rem; - border-left: 2px solid var(--tool-call-border-color, var(--border-base)); + border-inline-start: 2px solid var(--tool-call-border-color, var(--border-base)); font-size: var(--font-size-sm); font-family: var(--font-family-mono); line-height: 1.35; @@ -134,19 +134,19 @@ } .tool-call-task-item[data-task-status="completed"] { - border-left-color: var(--status-success); + border-inline-start-color: var(--status-success); } .tool-call-task-item[data-task-status="running"] { - border-left-color: var(--status-warning); + border-inline-start-color: var(--status-warning); } .tool-call-task-item[data-task-status="pending"] { - border-left-color: var(--accent-primary); + border-inline-start-color: var(--accent-primary); } .tool-call-task-item[data-task-status="error"] { - border-left-color: var(--status-error); + border-inline-start-color: var(--status-error); } .tool-call-task-icon { diff --git a/packages/ui/src/styles/panels/right-panel.css b/packages/ui/src/styles/panels/right-panel.css index 6b9d2fcd..d2c08929 100644 --- a/packages/ui/src/styles/panels/right-panel.css +++ b/packages/ui/src/styles/panels/right-panel.css @@ -11,8 +11,8 @@ content: ''; position: absolute; bottom: -1px; - left: 0; - right: 0; + inset-inline-start: 0; + inset-inline-end: 0; height: 1px; background-color: var(--border-base); z-index: 0; @@ -42,7 +42,7 @@ border: 1px solid transparent; border-bottom: none; border-radius: 8px 8px 0 0; - margin-right: 2px; + margin-inline-end: 2px; z-index: 1; } @@ -90,8 +90,8 @@ .file-split-handle { cursor: col-resize; background-color: transparent; - border-left: 1px solid var(--border-base); - border-right: 1px solid var(--border-base); + border-inline-start: 1px solid var(--border-base); + border-inline-end: 1px solid var(--border-base); user-select: none; touch-action: none; } @@ -148,7 +148,7 @@ display: flex; flex-direction: column; background-color: var(--surface-secondary); - border-left: 1px solid var(--border-base); + border-inline-start: 1px solid var(--border-base); /* Monaco uses layered positioned elements; keep overlay well above it. */ z-index: 200; } @@ -459,7 +459,7 @@ } .section-label { - margin-left: 2px; + margin-inline-start: 2px; } .section-info-icon { diff --git a/packages/ui/src/styles/panels/session-layout.css b/packages/ui/src/styles/panels/session-layout.css index f03cf6a7..9b0395ec 100644 --- a/packages/ui/src/styles/panels/session-layout.css +++ b/packages/ui/src/styles/panels/session-layout.css @@ -26,10 +26,10 @@ position: absolute; top: 0; bottom: 0; - left: 0; + inset-inline-start: 0; width: min(90vw, 360px); max-width: 360px; - border-right: 1px solid var(--border-base); + border-inline-end: 1px solid var(--border-base); box-shadow: var(--folder-card-shadow); transform: translateX(0); transition: transform 0.25s ease, opacity 0.2s ease; @@ -41,6 +41,9 @@ opacity: 0; pointer-events: none; } +[dir="rtl"] .session-sidebar-collapsed { + transform: translateX(100%); +} .session-sidebar-backdrop { @apply absolute inset-0; @@ -54,7 +57,7 @@ .session-sidebar-menu-button--floating { position: absolute; top: 1rem; - left: 1rem; + inset-inline-start: 1rem; z-index: 20; } @@ -128,7 +131,7 @@ session-sidebar-controls .selector-trigger-primary { .mobile-fullscreen-exit-wrapper { position: fixed; top: calc(env(safe-area-inset-top, 0px) + 12px); - right: calc(env(safe-area-inset-right, 0px) + 12px); + inset-inline-end: calc(env(safe-area-inset-right, 0px) + 12px); z-index: 1250; pointer-events: none; } @@ -143,11 +146,11 @@ session-sidebar-controls .selector-trigger-primary { } .session-resize-handle--left { - right: 0; + inset-inline-end: 0; } .session-resize-handle--right { - left: 0; + inset-inline-start: 0; } .session-resize-handle:hover { @@ -160,14 +163,20 @@ session-sidebar-controls .selector-trigger-primary { } .session-resize-handle--left::before { - right: 0; + inset-inline-end: 0; transform: translateX(50%); } +[dir="rtl"] .session-resize-handle--left::before { + transform: translateX(-50%); +} .session-resize-handle--right::before { - left: 0; + inset-inline-start: 0; transform: translateX(-50%); } +[dir="rtl"] .session-resize-handle--right::before { + transform: translateX(50%); +} .session-list-header { @apply border-b relative; @@ -197,7 +206,7 @@ session-sidebar-controls .selector-trigger-primary { .session-item-base.session-item-child { - padding-left: 2.25rem; + padding-inline-start: 2.25rem; position: relative; } @@ -206,7 +215,7 @@ session-sidebar-controls .selector-trigger-primary { position: absolute; top: 0; bottom: 0; - left: 1.125rem; + inset-inline-start: 1.125rem; width: 1px; background-color: var(--text-secondary); opacity: 0.95; @@ -221,7 +230,7 @@ session-sidebar-controls .selector-trigger-primary { content: ""; position: absolute; top: 50%; - left: 1.125rem; + inset-inline-start: 1.125rem; width: 0.875rem; height: 1px; background-color: var(--text-secondary); @@ -231,11 +240,11 @@ session-sidebar-controls .selector-trigger-primary { } .session-item-base.session-item-border-user { - border-left: 4px solid var(--message-user-border); + border-inline-start: 4px solid var(--message-user-border); } .session-item-base.session-item-border-assistant { - border-left: 4px solid var(--message-assistant-border); + border-inline-start: 4px solid var(--message-assistant-border); } .session-item-expander {