dsh-rtl-fix
Verifieddsh-rtl-fix Β· v1.0.2 Β· MIT Β· Web UI
Proper RTL rendering for DeepSeek Harness web chat: any line containing RTL scripts renders fully RTL (right-aligned, correct bidi word order) even when it embeds English words, plus RTL-aware composer input.
Install
dsh plugin add dsh-rtl-fix Confirm the layer applied with dsh --profile default --dump-config β see the install guide.
Source
Creators
Readme
dsh-rtl-fix
Proper RTL rendering for DeepSeek Harness (dsh) web chat.
Arabic, Hebrew (and any other RTL script) chat lines render fully RTL β right-aligned, correct bidi word order, list markers on the right β even when the line embeds English words, e.g.:
ShiftController::show()β Ψ£ΨΆΩΨͺ ΨΨ³Ψ§Ψ¨ ΨΉΨ―Ψ― ΨΩΩΩΩ
- β Any line containing at least one RTL letter is treated as a full RTL line
- β English/paths inside an RTL line keep their correct internal order
- β
Ordered/unordered list markers (
1./β’) render on the right - β
Code blocks (
pre/code) stay LTR β JSON, paths and code never flip - β The composer input and its mirror follow the draft as you type
- β Streaming-safe: directions re-apply automatically on every re-render
- β No configuration needed β works out of the box
Install
In a DeepSeek Harness web or desktop profile. From npm:
dsh plugin --profile desktop add [email protected]
Or for the web profile:
dsh plugin --profile web add [email protected]
Or directly from GitHub (no npm involved):
dsh plugin --profile desktop add github:ahmedtohamy1/dsh-rtl-fix#main
Either can also be run through npx:
npx -y @deepseek-ai/dsh plugin --profile desktop add [email protected] # npm
npx -y @deepseek-ai/dsh plugin --profile desktop add github:ahmedtohamy1/dsh-rtl-fix#main # GitHub
Then restart dsh and refresh the browser. That's it β RTL lines in the chat now render correctly.
Troubleshooting β
ERR_PNPM_IGNORED_BUILDS: Ifdsh plugin addfails with[ERR_PNPM_IGNORED_BUILDS] Ignored build scripts: @google/genai, protobufjs, those are host dependencies of dsh itself (not this plugin) that pnpm 11 requires you to approve once per profile. Openpnpm-workspace.yamlin your web profile β%USERPROFILE%\.dsh\profiles\web\pnpm-workspace.yamlon Windows,~/.dsh/profiles/web/pnpm-workspace.yamlon macOS/Linux β and set:allowBuilds: '@google/genai': true protobufjs: truethen re-run the same
dsh plugin --profile web add ...command. One-time only β future installs won't ask again. Alternatively runpnpm approve-buildsinside that profile directory.
Verify
- Open a chat containing Arabic text. Lines with mixed English should be right-aligned and read right-to-left; numbered/bulleted lists put their markers on the right.
- Console handle for manual checks:
__dshRtlFix.scanAll() // re-apply directions now, returns a report object
Uninstall
dsh plugin --profile web remove dsh-rtl-fix
How it works
A tiny client-only plugin (one loader row + a browser bundle; the host half only carries the small diagnostics route below). It watches the conversation DOM and, for every block-level text element (paragraphs, list items, ol/ul, table cells, leaf text containers) inside the chat scroll scope, sets dir="rtl" whenever the element's text contains any RTL character β overriding the browser's dir="auto" first-strong-character heuristic, which is exactly what made lines starting with an English word render LTR.
dir is enough: the chat styles never pin text-align, so the CSS initial text-align: start follows the direction β RTL lines right-align automatically. A MutationObserver plus a periodic sweep re-apply the fix after any re-render (streaming, session switches, etc.).
What stays LTR
- Markdown/code blocks (
pre,code,kbd,samp,var) - Pure-LTR lines (direction is set to
ltrexplicitly if a parent forced RTL) - Non-conversation UI (sidebar, settings, titles) β untouched by design
Diagnostics
The host half exposes a tiny loopback endpoint the client pings on startup and whenever it changes directions, appended to:
<DSH_HOME>/plugins/dsh-rtl-fix/pings.log
pings.log is runtime data, never tracked in the repository. Useful when reporting issues β delete it any time; it regenerates.
Development
dsh-rtl-fix/
βββ package.json # dsh bundle + web client metadata (name: dsh-rtl-fix)
βββ cordis.patch.yml # inserts the loader row (id: rtl-fix β package dsh-rtl-fix)
βββ lib/
β βββ index.js # host half: ping/telemetry route (no core logic)
β βββ client.js # the RTL scanner (module-loader entry, all logic here)
βββ LICENSE # MIT
βββ README.md
Local iteration while developing:
# point the profile at your checkout with an existing real path
dsh plugin --profile web add link:C:\path\to\dsh-rtl-fix
Client changes are served from disk per request β a browser hard refresh (Ctrl+F5) applies them without a host restart. Host-side changes (like the ping route) need a dsh restart.
The scanner at a glance
- Scope: elements under
[data-conversation-scroll](the chat column) - Target selectors:
p, li, blockquote, h1..h6, dt, dd, td, th, ol, ul+ leaf text containers - Rule: any RTL character in the element's
textContentβdir="rtl" - Exclusion: anything inside
pre, code, kbd, samp, var
If a future dsh release changes the markdown DOM, adjust these in lib/client.js β the debug handle __dshRtlFix.scanAll() returns { scope, flow, arabicFlow, changed, composer, err, β¦ } to help you see what the scan is matching. The chat scan deliberately skips the composer subtree ([data-composer-card] / [data-composer-seat] / Lexical editor nodes); the composer is driven separately by syncComposer(), which supports both the legacy textarea + backdrop/mirror composer (β€ 0.1.1) and the Lexical div[contenteditable][data-composer-input] composer (β₯ 0.1.5).
Requirements
- DeepSeek Harness
0.1.x(web or desktop profile) β dsh must expose the standard client-plugin surface (dsh.clientbundles + loader rows) - A Chromium/Firefox/WebKit browser β the fix is pure DOM/CSS, no dependencies, no network calls, no API keys
- Verified on dsh
0.1.1-rc.2(Windows Β· Chrome/Edge) and macOS, and on dsh0.1.5-rc.2(DSH Desktop Β· desktop profile) β if something doesn't look right on your combo, open an issue and attach the output of__dshRtlFix.scanAll()
License
MIT β see LICENSE.