name: cursor-rtl-visual-fix description: Fix Cursor RTL rendering issues end-to-end with visual verification. Use when the user provides a cursor-ext-rtl GitHub issue, mentions RTL visual bugs, Markdown tables, Hebrew/Arabic/Persian layout, Cursor chat rendering, or asks to develop and visually test a fix.
Cursor RTL Visual Fix
Use this skill to take an RTL rendering issue from report to verified fix in this repository.
Operating Rules
- Treat the issue screenshot/description as the product requirement.
- Do not commit or push unless the user explicitly asks.
- Use
npm run packageto create a VSIX. Do not runvsce packagedirectly. - Preserve unrelated user changes. Do not revert files you did not need to touch.
- Visual evidence is required for UI/layout fixes. DOM checks are supporting evidence only.
- Never approve or update visual baselines without explicit user confirmation after they review
visual/actual.
End-to-End Workflow
Understand the issue.
- If given a GitHub issue URL, use
gh issue view <number> --repo motcke/cursor-ext-rtl --commentswhen possible. - Extract the failing scenario, expected layout, actual layout, affected UI surface, and any screenshot clues.
- For example, issue
#8is about mixed Hebrew and English text being misaligned in Markdown tables.
- If given a GitHub issue URL, use
Inspect the current implementation.
- Start with
resources/rtl.js; most visual behavior is injected from there. - Check related selectors, direction rules,
unicode-bidi,text-align, table handling, code block exclusions, and scanner behavior. - Search before editing. Prefer the repo's existing CSS/DOM-injection patterns.
- Start with
Reproduce visually in real Cursor.
- Cursor must be launched with CDP:
& "$env:LOCALAPPDATA\Programs\cursor\Cursor.exe" --remote-debugging-port=9222
- If
http://localhost:9222/jsonis unavailable, stop and ask the user to relaunch Cursor with the flag. - Use the real Cursor chat/composer to create or locate a scenario matching the issue. For table issues, use a Markdown table containing mixed Hebrew and English.
- When iterating on
resources/rtl.js, prefer direct CDP evaluation for temporary runtime testing before rebuilding/restarting Cursor. See "Runtime Injection During Development" below.
- Implement the smallest fix.
- Prefer targeted changes in
resources/rtl.js. - Keep code blocks and editor internals LTR.
- Avoid broad
direction: rtlrules on large containers unless the issue proves that is necessary. - Prefer CSS logical properties and per-element direction behavior over hard-coded left/right when possible.
- Apply the Direction Stability Rules below.
- Prefer targeted changes in
Direction Stability Rules
These prevent flicker (direction oscillating many times per second).
- Classify each element before setting direction:
- Chrome (headers, labels, buttons, steppers, icons, counters): force
direction: ltr+unicode-bidi: isolate. Never derive their direction from content. - Content (question/answer text, fully-Hebrew blocks): set direction from the element's own text. A container that is wholly RTL should align RTL as one unit.
- Chrome (headers, labels, buttons, steppers, icons, counters): force
- Derive a container's direction only from a stable signal (its own text). Never from a value the scanner itself writes.
- Never let CSS that reacts to a scanned direction also change layout/size (e.g.
:has([dir="rtl"])flipping padding). The scanneddir→ CSS layout change → mutation → re-scan loop is the flicker root cause. - Only leaf text elements should receive a computed
dir; strip and re-assert chrome direction in the scan so staledircannot accumulate.
Build and package.
- Run
npm run lint. - Run
npm run packagewhen a VSIX/reinstall is needed. - If the fix requires the Cursor patch to reload, install/reapply the extension and restart/reload Cursor as the repository workflow requires.
- Run
Capture visual evidence.
- Run:
npm run visual:capture
- Review these artifacts:
visual/actual/cursor-right-pane.pngvisual/actual/cursor-full.pngvisual/actual/cursor-inspection.json
- Use
VISUAL_CLIPif the right pane crop is not focused enough:
$env:VISUAL_CLIP = "900,80,700,900"
npm run visual:capture
Remove-Item Env:\VISUAL_CLIP
- Compare against baseline.
- If a baseline already exists, run:
npm run visual
- If comparison fails, inspect
visual/diff. - Only ask the user to approve a new baseline after explaining what changed and why it is expected:
npm run visual:approve
- Report results.
- Include the issue summary, root cause, implementation approach, and visual verification result.
- Mention whether
npm run lint,npm run package,npm run visual:capture, andnpm run visualpassed. - If visual approval is still needed, say exactly which image the user should review.
Runtime Injection During Development
Use this only for local visual debugging while Cursor is already running with --remote-debugging-port=9222. This does not replace the real extension/loader verification path.
- Inject the current
resources/rtl.jsinto active Cursor workbench pages with CDPRuntime.evaluate:
node -e "const { chromium } = require('playwright'); const fs = require('fs'); (async () => { const browser = await chromium.connectOverCDP('http://localhost:9222'); const script = fs.readFileSync('resources/rtl.js', 'utf8'); for (const context of browser.contexts()) { for (const page of context.pages()) { if (!page.url().includes('workbench.html')) continue; await page.evaluate(script); await page.waitForTimeout(1000); const state = await page.evaluate(() => ({ title: document.title, hasScan: typeof window.__cursorRtlScanAll === 'function', styles: Array.from(document.querySelectorAll('style')).some(s => (s.textContent || '').includes('.markdown-table-container')) })); console.log(JSON.stringify(state)); } } await browser.close(); })().catch(e => { console.error(e); process.exit(1); });"
Do not use
page.addScriptTag({ path })for Cursor workbench injection. Cursor may enforce Trusted Types and reject script tag text assignment.After direct runtime injection, run:
npm run visual:capture
Treat direct injection as supporting evidence only. For final verification, run
npm run package, install the VSIX, and restart Cursor when the loader or patching behavior changed.Check the active window really has the runtime API before trusting visual results:
node -e "const { chromium } = require('playwright'); (async () => { const browser = await chromium.connectOverCDP('http://localhost:9222'); for (const context of browser.contexts()) { for (const page of context.pages()) { if (!page.url().includes('workbench.html')) continue; const state = await page.evaluate(() => ({ title: document.title, hasScan: typeof window.__cursorRtlScanAll === 'function', injectedStyles: Array.from(document.querySelectorAll('style')).some(s => (s.textContent || '').includes('.markdown-table-container')) })); console.log(JSON.stringify(state)); } } await browser.close(); })().catch(e => { console.error(e); process.exit(1); });"
If
visual:capturesaysRTL script detected: no, orhasScanis false, the active workbench page did not receivertl.js. Do not debug CSS or direction logic until injection is confirmed.If editing
resources/cursor-rtl-loader.cjs, remember that the loader runs in Electron main.Developer: Reload Windowis not enough to reload loader changes; use a full Cursor restart for end-to-end loader verification.A known loader failure mode is injecting too early while
webContents.getURL()is still empty, marking the webContents as injected, then skipping the laterworkbench.htmlload. The loader should inject only into URLs containingworkbench.htmland should mark success afterexecuteJavaScriptresolves.
Visual Reproduction Prompts
Use issue-specific content in Cursor chat. For Markdown table alignment, a useful prompt is:
תציג לי טבלת Markdown עם עמודות:
| שדה | English value | הערות |
|-----|---------------|-------|
| שם משתמש | userName | טקסט בעברית mixed with English |
| סטטוס | active | בדיקה עם API ו-JSON |
| נתיב | C:\repos\cursor-ext-rtl | נתיב צריך להישאר LTR |
For code block regressions, include Hebrew prose plus fenced code. The prose should behave RTL; code should remain LTR.
Root-Cause Checklist
Before fixing, identify which category applies:
- Selector miss: the affected UI element is not covered by
DIR_SELECTORor CSS. - Over-broad direction: a parent/container forces the wrong direction.
- Table layout: table container, table, cell, or inline content has conflicting
direction/text-align. - Mixed inline text:
unicode-bidi,dir="auto", orplaintextis missing or applied at the wrong level. - Exclusion problem: code/editor/mermaid/table internals are being scanned when they should not be.
- Timing problem: content appears after initial scans and the MutationObserver does not catch it.
- Feedback-loop flicker: the scanned
dirtriggers CSS that changes layout, which re-triggers the scan with a different result. Measure elementx/dirover time (not single snapshots) to detect it; fix per the Direction Stability Rules.
When Playwriter Fits
Use Playwriter for browser-side helpers such as viewing a generated report, opening GitHub pages, or inspecting external web content. Do not rely on Playwriter to capture Cursor itself; Cursor screenshots come from the CDP workflow and npm run visual:capture.