Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
- **fix(kiro): route enterprise IAM Identity Center accounts to their regional endpoint** ([#3631] — thanks @artickc): Kiro/CodeWhisperer access tokens and Q Developer profile ARNs are region-bound, so enterprise IAM Identity Center accounts outside `us-east-1` (e.g. `eu-central-1`) were rejected by the default host. Adds `resolveKiroRegion` (stored region → profileArn region → `us-east-1`) and `kiroRuntimeHost` (regional `q.{region}.amazonaws.com`, legacy `codewhisperer.us-east-1` for the default), routes chat + usage to the regional endpoint, and discovers the region-matched `profileArn` via `ListAvailableProfiles` in a best-effort `postExchange` hook. 9 tests.
- **fix(combo): skip same-provider/connection targets on connection-level errors** ([#3637] — thanks @herjarsa): on connection-level upstream errors (408/500/502/503/504/524), remaining same-`provider:connection` targets in a combo request are now skipped to avoid hammering a known-bad connection, in both the priority and round-robin paths. Adjusted in review to **exclude OmniRoute circuit-breaker-open responses** (503 + `X-OmniRoute-Provider-Breaker` / `provider_circuit_open`) from this skip, preserving the invariant that a breaker-open is an ordinary target failure (the next same-provider target is still tried). Co-authored with @herjarsa.
- **/v1/responses**: detect stream readiness for tool-call-only and `object`-less chunks so Codex-shaped (reasoning + tools) requests no longer fail with "Stream ended before producing useful content" (#3612)
- **RTL locales (ar/fa/he/ur)**: use logical CSS direction utilities for the sidebar and key overlays so the layout mirrors correctly under `dir=rtl` (#3541, partial — core layout)
- **Kiro/AWS auto-import**: set a descriptive account name and dedupe by `profileArn` so imports no longer create nameless duplicate "OAuth Account" rows (#3615)

---
Expand Down
4 changes: 2 additions & 2 deletions src/shared/components/LanguageSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export default function LanguageSelector() {

{/* Dropdown */}
{open && (
<div className="absolute right-0 top-full mt-1 w-56 max-h-80 rounded-xl border border-border bg-bg shadow-xl z-50 overflow-y-auto animate-in fade-in slide-in-from-top-1 duration-150">
<div className="absolute end-0 top-full mt-1 w-56 max-h-80 rounded-xl border border-border bg-bg shadow-xl z-50 overflow-y-auto animate-in fade-in slide-in-from-top-1 duration-150">
{LANGUAGES.map((lang) => (
<button
key={lang.code}
Expand All @@ -77,7 +77,7 @@ export default function LanguageSelector() {
}`}
>
<span className="text-base leading-none">{lang.flag}</span>
<span className="flex-1 text-left">{lang.name}</span>
<span className="flex-1 text-start">{lang.name}</span>
{lang.code === locale && (
<span className="material-symbols-outlined text-[16px] text-primary">check</span>
)}
Expand Down
4 changes: 2 additions & 2 deletions src/shared/components/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export default function Select({
aria-invalid={error ? true : undefined}
aria-describedby={describedBy}
className={cn(
"w-full py-2 px-3 pr-10 text-sm text-text-main",
"w-full py-2 px-3 pe-10 text-sm text-text-main",
"bg-surface border border-black/10 dark:border-white/10 rounded-md appearance-none",
"focus:ring-1 focus:ring-primary/30 focus:border-primary/50 focus:outline-none",
"transition-all disabled:opacity-50 disabled:cursor-not-allowed",
Expand All @@ -86,7 +86,7 @@ export default function Select({
{children}
</select>
<div
className="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none text-text-muted"
className="absolute inset-y-0 end-0 flex items-center pe-3 pointer-events-none text-text-muted"
aria-hidden="true"
>
<span className="material-symbols-outlined text-[20px]">expand_more</span>
Expand Down
2 changes: 1 addition & 1 deletion src/shared/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ export default function Sidebar({
className={cn(
"rounded-md p-1 text-text-muted/50 transition-colors hover:bg-black/5 hover:text-text-muted dark:hover:bg-white/5",
collapsed && !isMacElectron && "mt-2",
isMacElectron && "ml-auto"
isMacElectron && "ms-auto"
)}
>
<span className="material-symbols-outlined text-[16px]" aria-hidden="true">
Expand Down
2 changes: 1 addition & 1 deletion src/shared/components/layouts/DashboardLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export default function DashboardLayout({ children }) {

{/* Sidebar - Mobile: full viewport height with proper scroll containment */}
<div
className={`fixed inset-y-0 left-0 z-50 transform lg:hidden transition-transform duration-300 ease-in-out h-dvh overflow-y-auto ${
className={`fixed inset-y-0 start-0 z-50 transform lg:hidden transition-transform duration-300 ease-in-out h-dvh overflow-y-auto ${
sidebarOpen ? "translate-x-0" : "-translate-x-full"
}`}
>
Expand Down
117 changes: 117 additions & 0 deletions tests/unit/rtl-logical-classes.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/**
* RTL layout regression test — #3541 (partial, core layout)
*
* For RTL locales (ar/fa/he/ur) the sidebar and key overlays must flip
* correctly when `dir=rtl` is set on <html>. Tailwind v4 supports logical
* direction utilities natively (`start-*`, `end-*`, `ps-*`, `pe-*`, `ms-*`,
* `me-*`, `text-start`, `text-end`) which auto-mirror under dir=rtl without
* extra `rtl:` variants.
*
* This test reads the source files and asserts that the four high-impact
* components use logical classes instead of physical directional ones where
* those classes govern layout placement / offset.
*
* Runner: Vitest (included via `tests/unit/**\/*.test.tsx` in vitest.config.ts)
* Command: npm run test:vitest -- --reporter=verbose --testPathPattern=rtl-logical
*/

import { describe, it, expect } from "vitest";
import { readFileSync } from "fs";
import { resolve } from "path";

const cwd = process.cwd();

function src(rel: string) {
return readFileSync(resolve(cwd, rel), "utf-8");
}

// ─── DashboardLayout.tsx ──────────────────────────────────────────────────────

describe("DashboardLayout — mobile sidebar drawer uses logical classes", () => {
const code = src("src/shared/components/layouts/DashboardLayout.tsx");

it('uses start-0 (not bare left-0) for the mobile sidebar drawer', () => {
// The drawer must anchor to the inline-start edge so it flips in RTL.
expect(code).toContain("start-0");
});

it('does not use bare left-0 for the mobile sidebar drawer', () => {
// "inset-y-0 left-0" is the old physical class that breaks RTL.
expect(code).not.toContain("inset-y-0 left-0");
});

it('uses -translate-x-full only when combined with RTL-aware transform or logical alternative', () => {
// We keep -translate-x-full/translate-x-0 for slide animation but anchor
// with start-0 so physical translate still produces correct visual result
// in both LTR and RTL (the drawer always slides in from its own start edge).
// Just confirm start-0 is present (already asserted above).
expect(code).toContain("start-0");
});
});

// ─── Sidebar.tsx ─────────────────────────────────────────────────────────────

describe("Sidebar — collapse toggle button uses logical margin", () => {
const code = src("src/shared/components/Sidebar.tsx");

it('uses ms-auto (not ml-auto) for the macOS collapse-toggle alignment', () => {
// ml-auto is a physical class; ms-auto mirrors in RTL.
expect(code).toContain("ms-auto");
});

it('does not use bare ml-auto in the collapse-toggle className', () => {
expect(code).not.toContain("ml-auto");
});
});

// ─── LanguageSelector.tsx ─────────────────────────────────────────────────────

describe("LanguageSelector — dropdown anchors to inline-end", () => {
const code = src("src/shared/components/LanguageSelector.tsx");

it('uses end-0 (not right-0) for the dropdown panel', () => {
expect(code).toContain("end-0");
});

it('does not use bare right-0 for the dropdown panel', () => {
expect(code).not.toContain("right-0");
});

it('uses text-start (not text-left) for lang name text alignment', () => {
expect(code).toContain("text-start");
});

it('does not use text-left for lang name text alignment', () => {
expect(code).not.toContain("text-left");
});
});

// ─── Select.tsx ───────────────────────────────────────────────────────────────

describe("Select — chevron icon uses logical inset classes", () => {
const code = src("src/shared/components/Select.tsx");

it('uses end-0 (not right-0) for the chevron wrapper', () => {
expect(code).toContain("end-0");
});

it('does not use bare right-0 for the chevron wrapper', () => {
expect(code).not.toContain("right-0");
});

it('uses pe-3 (not pr-3) for chevron wrapper padding', () => {
expect(code).toContain("pe-3");
});

it('does not use bare pr-3 for chevron wrapper padding', () => {
expect(code).not.toContain("pr-3");
});

it('uses pe-10 (not pr-10) for the select element right-pad (space for chevron)', () => {
expect(code).toContain("pe-10");
});

it('does not use bare pr-10 for the select element', () => {
expect(code).not.toContain("pr-10");
});
});