# dsh-arabic [العربية](README.md) · **English** **Arabic for DeepSeek Harness — proper bidi/RTL rendering, a full Arabic UI language pack, and thmanyah's serif as the interface font.** [![CI](https://github.com/SMSMy/dsh-arabic/actions/workflows/ci.yml/badge.svg)](https://github.com/SMSMy/dsh-arabic/actions/workflows/ci.yml) [![npm](https://img.shields.io/npm/v/dsh-arabic)](https://www.npmjs.com/package/dsh-arabic) [![downloads](https://img.shields.io/npm/dm/dsh-arabic?label=downloads)](https://www.npmjs.com/package/dsh-arabic) [![license](https://img.shields.io/badge/license-MIT-blue)](LICENSE) DSH has no RTL support and no Arabic locale. Mixing Arabic prose with English identifiers, paths and code inside the GUI makes text look scrambled, and every menu is English-only. This plugin fixes both, without patching the app. ## What it does ### 1. Bidi-safe RTL rendering (works regardless of UI language) Direction is decided **per block by script dominance**, not by the first strong character. The tokenizer splits on whitespace only, so `@deepseek-ai/dsh` counts as *one* word rather than three: | Content | Direction | Why | |---|---|---| | `كيف حالك Hello` | RTL | Arabic words dominate | | `Hello كيف حالك` | RTL | still Arabic words — `dir="auto"` would get this **wrong** | | `Error: فشل الاتصال بالخادم` | RTL | the sentence is Arabic, the prefix is not | | `npm install ثم أعد التشغيل` | RTL | 3 Arabic words vs 2 Latin ones | | `شغّل npx @deepseek-ai/dsh web` | RTL | the code token binds `npx … web` into **one** unit, so 1 Arabic vs 1 Latin | | `راجع commit a4c1025b قبل النشر` | RTL | a bare sha does not vote | | `نسبة النجاح 15/15` | RTL | a ratio is neutral | | `افتح src/index.ts ثم عدّل الدالة` | RTL | a path does not vote | | `"C:\Users\…\الخطوط\thmanyahseriftext"` | untouched | a Windows path is a code token even with an Arabic folder inside | | `@deepseek-ai/dsh مهم جدًا` | RTL | a leading identifier is one unit | | `Hello نص` | RTL | a tie resolves to RTL | | `The build failed while parsing سلام in the file` | untouched | English prose quoting a word stays LTR | | `شغّل "git status"` | RTL | a quotation is **one unit**: the quoted span votes once | | `pre`, `code`, inline code | LTR always | code is never judged and never mirrored | | composer, search boxes | follows typing | RTL while Arabic dominates, otherwise native `auto` | The four rules behind the table: 1. **Code-like tokens do not vote** — URLs, paths (Windows backslashes included), `@scope/name`, shas, `15/15`, dotted file names. One URL or sha can otherwise outweigh a whole Arabic sentence, and a code token also **binds the Latin words around it** into one technical unit. A path whose folder carries Arabic letters is still a path: counting it as prose is what used to flip a block of paths to RTL, which then re-ordered each path's own Latin runs around that Arabic word. 2. **Words vote, not letters** — an Arabic word against a Latin word, because Latin technical terms are longer in characters but fewer in words. A tie goes to Arabic; a block with no Arabic word is released. 3. **Hysteresis while streaming** — a block that is already RTL stays RTL until the text is clearly Latin (twice as many Latin words), so a growing answer cannot flicker between directions. 4. **A quotation is one unit** — `"git status"` is a quoted phrase, a command, a title, and votes once, not twice. `شغّل "git status"` therefore reads as the Arabic sentence it is, while the same words unquoted stay LTR (see the limits below). An unclosed quote — a streamed answer mid-quotation — runs to the end of the text, which is the unit its closed form will produce. **Only text blocks are flipped — chrome never is.** A flex or grid container reorders its children when it flips, and a row that owns buttons is a toolbar: both are excluded, so the composer's send button keeps its side even though the Arabic permission label sits in that same row. The Arabic text inside such a row lives in its own block and is still handled. Two shapes needed more than a plain block decision, and both are pinned by tests and by `data/card-pins.json`: - **a text cell CSS has blockified.** The question card the assistant uses to ask a question draws each option as a flex `