ar-terminal (RTL Terminal)
- مخزن: https://github.com/alzahrani-khalid/ar-terminal · کامیت خواندهشده: fa0690f (v0.3.4، 2026-03-29) · مجوز: ندارد. فایل LICENSE وجود ندارد.
package.json:6وREADME.md:154میگویند «MIT»، اما بدون متن مجوز، کد را غیرقابل استفادهٔ دوباره در نظر میگیریم. · زبان: TypeScript · سکو: افزونهٔ VS Code (macOS/Linux/Windows)، node-pty + xterm.js 6 درون یک webview - دسته: رندرکنندهٔ لایهٔ رویی (مسیر اصلی) + پراکسی PTY / تبدیل خروجی (مسیر فرعی)
این پروژه چیست
Section titled “این پروژه چیست”افزونهای برای VS Code که ترمینال خودش را بهصورت یک webview در زبانهٔ ویرایشگر باز میکند: node-pty پوسته را اجرا میکند و یک xterm.js بستهبندیشده ترمینال را شبیهسازی میکند. سطرهایی که نقطهٔ کد RTL (یا آیکونهای Nerd Font) دارند، با یک <div> HTML با موقعیت مطلق پوشانده میشوند تا موتور متن Chromium آن سطرها را شکلدهی (shaping) و بازچینی کند. مسیر کد دوم، یک پروفایل vscode.Pseudoterminal، جریان PTY را به فرمهای نمایشی (presentation forms) و به ترتیب دیداری (visual order) بازنویسی میکند.
چگونه کار میکند (تأییدشده در کد)
Section titled “چگونه کار میکند (تأییدشده در کد)”مسیر A، لایهٔ رویی (overlay) در webview (rtlTerminal.newTerminal، src/extension.ts:55-65، src/webview-terminal.ts):
- بایتهای PTY خام به xterm.js میروند (
webview-terminal.ts:133-136). فقط دنبالههای OSC عنوان حذف میشوند (:119-131). xterm.js متن منطقی را در بافرش نگه میدارد و آن را روی بوم (canvas) خودش رسم میکند. - قلابکردن به xterm.js: این نه یک فورک است، نه رندرکنندهٔ سفارشی، نه API تزئین (decoration) و نه character joiner. یک لایهٔ DOM همسطح است به نام
#arabic-overlayباpointer-events:noneوz-index:10(:222-243). در رویدادهایonWriteParsed،onScroll،onResizeوonSelectionChange(:627-630)، لایهٔ رویی پس از یک debounce شانزدهمیلیثانیهای از نو ساخته میشود (:620-625). updateArabicOverlay(:463-535) سطرهای قابلمشاهدهٔterm.buffer.activeرا پیمایش میکند. سطری پوشانده میشود که هر یک از خانههایش RTL یا گلیف PUA از Nerd Font باشد (:474-482، بازهها در:363-378). برای هر سطرِ پوشاندهشده،cell.getChars()را به ترتیب منطقیِ خانهها به هم میچسباند و بر اساس رنگ پیشزمینه/bold/dim/italic در<span>ها گروهبندی میکند (:495-520). رنگ پسزمینه، زیرخط و inverse کنار گذاشته میشوند.divسطر مات است (backgroundدر:242)، پس سطر بوم زیر خود را پنهان میکند.- شکلدهی و دوجهتی (bidi) را Chromium انجام میدهد، نه کد برنامه. div دارای
direction:ltr; unicode-bidi:normal; white-space:preاست (:233-243)، پس Blink الگوریتم UAX #9 را با سطح پاراگراف LTR اجرا میکند و با HarfBuzz و هر قلمی که از زنجیرهٔ جایگزینی (fallback) برسد شکلدهی میکند. فیلدهایArabicReshaperوBidiEngineدر این کلاس (:22-24) استفاده نمیشوند.reshapeArabicInStream(:159-176) کد مرده است و هیچ فراخوانندهای ندارد. - از خانهها به گلیفها: هیچ نگاشتی وجود ندارد. لایهٔ رویی از چیدمان متناسب (proportional) مرورگر استفاده میکند که از x=0 سطر شروع میشود (
left:0). فقطtop/heightسطر ازterm._core._renderService.dimensionsخصوصیِ xterm گرفته میشود (:420-432). متن عربیِ شکلدهیشده باریکتر یا پهنتر از N خانهٔ خودش است، پس هر چه پس از آن در سطر بیاید از شبکهٔ xterm فاصله میگیرد. - مکاننما (cursor): یک میلهٔ چشمکزن ساختگی که در
buf.cursorX * cellWقرار میگیرد (:522-529). این ستونِ خانهٔ منطقی است که با گلیفهای بازچینی و بازچیدهشده روی لایهٔ رویی جور درنمیآید. - انتخاب: همچنان انتخاب خودِ xterm.js روی شبکهٔ بوم پنهان است (ستونهای منطقی). لایهٔ رویی فقط با شکستن spanها در افستهای نویسهایِ منطقی که برابر ستونهای انتخاباند، برجستهسازی را دوباره رسم میکند (
:538-618). بنابراین کشیدن ماوس بر اساس خانههای منطقیِ نامرئی انتخاب میکند، نه بر اساس آنچه کاربر میبیند. - کپی: یک شنوندهٔ
copy(:756-806) متن را از spanهای لایهٔ رویی میسازد که نویسههای بافر را به ترتیب منطقی در خود دارند، و بر اساس ستونsubstring(startX, endX)را برمیدارد. نتیجه ترتیب منطقی با نقاط کد اصلی است (بدون فرمهای نمایشی). احتمال جابهجایی اندیسها وجود دارد، چون خانههای خالی یا پهن رد میشوند (:499). - در این مسیر هیچ بررسیای برای صفحهٔ جایگزین (alternate screen) وجود ندارد، پس سطرهای عربیِ vim/htop هم پوشانده میشوند. هیچ addon پهنای Unicode 11 بارگذاری نمیشود. تنظیمات
rtlTerminal.modeوreshapeInputاینجا هرگز خوانده نمیشوند.
مسیر B، پروفایل Pseudoterminal (پروفایل ترمینال «RTL Terminal»، extension.ts:30-46, 90-150):
RtlPipeline.process(src/rtl-pipeline.ts:18-83) ابتدا کدهای ANSI را حذف میکند، سپس برای هر سطری که RTL دارد: (۱)ArabicReshaperحروف را با Presentation Forms-B جایگزین میکند (arabic-reshaper.ts:27-139، جدول درarabic-data.ts)، (۲)BidiEngineبا استفاده از bidi-js 1.0.3 (سطوح UAX #9 + قرینهسازی) runها را بهطور فیزیکی وارونه و به ترتیب دیداری درمیآورد (bidi-engine.ts:15-68)، و سپس (۳) کدهای ANSI را در موقعیتهای پیش از بازچینی سر جایشان برمیگرداند (rtl-pipeline.ts:82). نگاشتهای اندیس محاسبه میشوند اما دور ریخته میشوند.- جدول شکلدهی فقط U+0621–U+064A بهعلاوهٔ لامالف را پوشش میدهد (
arabic-data.ts). حروف فارسیِ پ چ ژ گ ک ی وجود ندارند، پس بهصورت حروف پایهٔ نامتصل میمانند و اتصال حروف همسایه را هم خراب میکنند. نیمفاصله (ZWNJ) پردازش نمیشود. - ورودیِ تایپشدهای که بازتاب (echo) میشود، نویسه به نویسهٔ عربی گرفته میشود و با
\b×N و بافر بازشکلدهیشده دوباره رسم میشود (rtl-pipeline.ts:95-139)، بدون هیچ پردازش دوجهتی. - صفحهٔ جایگزین (
?1049h/?47h) را به حالت عبور مستقیم (pass-through) میبرد (:20-29).
پوشش لایهها
Section titled “پوشش لایهها”| لایه | وضعیت | شاهد |
|---|---|---|
| ۱ رمزگذاری و یکسانسازی | ❌ | هیچ یکسانسازیای نیست. مسیر B متن را به فرمهای نمایشی (نویسههای سازگاری) تبدیل میکند. |
| ۲ ورودی | ◐ | مسیر B ترفندِ بازتاب را بازشکلدهی میکند (rtl-pipeline.ts:110-139). هیچ منطقی برای IME یا نیمفاصله وجود ندارد. میانبرهای Cmd/Alt در Mac به دنبالههای readline نگاشت میشوند (keybindings در package.json). |
| ۳ پهنا | ❌ | گلیفهای لایهٔ رویی با خانهها همتراز نیستند (چیدمان متناسب). مسیر B برای هر فرم نمایشی یک خانه فرض میکند. |
| ۴ شکلدهی | ◐ | مسیر A: شکلدهی واقعی با HarfBuzz از طریق Chromium. مسیر B: فرمهای نمایشی با جدول جستوجو، فقط عربی. |
| ۵ دوجهتی | ◐ | مسیر A: UAX #9 در Blink برای هر سطر، با پایهٔ LTR اجباری و محدود به همان سطر. مسیر B: UAX #9 با bidi-js، اما ترتیب دیداری را در خود جریان ثابت میکند. |
| ۶ مکاننما، انتخاب و کپی | ◐ | در مسیر A کپی منطقی است (:756-806)، اما آزمون برخوردِ انتخاب و مکاننما از ستونهای منطقی روی یک رندر دیداری استفاده میکنند. مسیر B فرمهای نمایشیِ وارونهشده را کپی میکند. |
| ۷ قلمها | ◐ | زیرمجموعهای از نمادهای Nerd Font را همراه دارد (:203-210). عربی از جایگزینی قلم مرورگر استفاده میکند و قلم عربیِ تکفاصله (monospace) ندارد. |
| ۸ مالتیپلکسرها | ❌ | پرداخته نشده. بازترسیمِ tmux/Zellij صرفاً سطرهای بیشتری برای پوشاندن است. |
| ۹ برنامهها و چارچوبهای TUI | ◐ | در تبلیغاتش Claude Code را هدف گرفته است. لایهٔ رویی برای برنامههای تمامصفحه غیرفعال نمیشود (مسیر A). |
ادعاشده اما تأییدنشده
Section titled “ادعاشده اما تأییدنشده”- «vim / htop / less — TUI apps work normally (overlay auto-disables)» (
README.md:76). در لایهٔ رویی webview هیچ بررسیای برای صفحهٔ جایگزین وجود ندارد. فقط مسیر B چنین بررسیای دارد. - «Auto-detection / mode auto/on/off» (
README.md:44, 105-108). حالت (mode) فقط در مسیر B بررسی میشود. فرمان پیشفرض (مسیر A) آن را نادیده میگیرد. - «Full support for 16, 256, and RGB colors» (
README.md:46). لایهٔ رویی فقط رنگ پیشزمینه را نگه میدارد و پسزمینه، inverse و زیرخط را کنار میگذارد (:501-518). - پشتیبانی از «Persian, Urdu» (
README.md:43). در مسیر B، حروف فارسی و اردوِ بیرون از بازهٔ 0621–064A شکلدهی نمیشوند. مسیر A به مرورگر وابسته است. - README اعتراف میکند که «Text selection on Arabic lines copies from xterm.js underneath» (
README.md:146). اما کد در عمل کپی را با متن لایهٔ رویی جایگزین میکند، پس README قدیمی شده است. - سند طراحی (
docs/superpowers/specs/2026-03-25-…md:28) رویکرد Pseudoterminal همراه با بازچینی را توصیف میکند. اما پیشفرضِ منتشرشده به لایهٔ رویی تغییر کرده است.
آنچه میآموزیم یا برمیداریم
Section titled “آنچه میآموزیم یا برمیداریم”- هیچ کدی قابل استفادهٔ دوباره نیست (فایل مجوز ندارد). فقط ایدهها:
- یافتهٔ مربوط به لایهٔ رویی مفید است: منطقی نگه داشتن بافر و سپردن رسم سطر به یک موتور متن واقعی (HarfBuzz + UAX #9)، RTLِ شکلدهیشده با ترتیب درست و کپیِ منطقی به دست میدهد. این همان مدل دادهای درست است. آنچه کم است، چیدمانی لنگرانداخته به خانهها و یک نگاشت دیداری↔منطقی برای آزمون برخورد است.
- شکاف xterm.js را تأیید میکند: نه شکلدهی، نه دوجهتی، و نه هیچ API عمومی برای جایگزین کردن رندر سطر. لایهٔ رویی به
_core._renderServiceخصوصی وابسته است (:421-423). - bidi-js (MIT، lojjic) بهعنوان پیادهسازیِ قابلجاسازیِ UAX #9 کار میکند و سطوح، بخشهای بازچینی و قرینهسازی را فراهم میکند. گزینهای برای پیشگوهای آزمون (test oracles) ماست.
- فایلهای آزمون (
*.test.ts) موارد مشخصی را فهرست میکنند: لامالف همراه با اعراب، سطرهای با جهت مختلط، و رفتوبرگشت ANSI.
محدودیتها و الگوهای نادرست
Section titled “محدودیتها و الگوهای نادرست”- مسیر B دقیقاً همان چیزی است که اصول ما رد میکند: جایگزینی با فرمهای نمایشی، جریانی که از پیش به ترتیب دیداری وارونه شده، و کدهای ANSI که در افستهای منطقیِ کهنه دوباره درج میشوند (رنگها روی نویسههای اشتباه مینشینند). در نتیجه خروجی برای کپی، جستوجو و لاگها خراب میشود و هر نشانیدهیِ مکاننما توسط برنامهها از کار میافتد.
- دو رندر از یک سطر. شبکهٔ بوم و متن DOM در هندسه با هم توافق ندارند، پس مکاننما، انتخاب با ماوس و آزمون برخورد پیوندها بر اساس خانههای نامرئی کار میکنند.
- لایهٔ رویی روی TUIهای تمامصفحه بدون محافظ صفحهٔ جایگزین و با بازسازی کامل DOM در هر نوشتن، بهعلاوهٔ چشمک زدنِ متنِ شکلدهینشده که خودِ پروژه به آن اذعان دارد.
- پاراگراف LTR اجباری (
direction:ltr). سطری که تماماً RTL است هرگز راستچین نمیشود و هیچ تشخیص جهت پایهای (P2/P3) ندارد. - دادههای شکلدهیِ فقط عربی (بدون حروف فارسی، بدون پردازش ZWNJ/ZWJ) در همان مسیر کدی که شکلدهی را خودش انجام میدهد.