mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 18:39:33 +01:00
fix(response-viewer): lay out Persian, Arabic and Hebrew right to left (#715)
The Response Viewer and the File Viewer's markdown view gave no block a dir, so a right-to-left answer was left-aligned with its punctuation on the wrong end and its bullets and quote bar on the left. _renderMarkdown now gives each outermost prose block dir="auto" (nested blocks inherit, so a list keeps its markers on one side), pins pre and code to dir="ltr", and keeps a dir the agent wrote. _linkifyFilePaths pins the path anchors it creates to ltr. Because dir="auto" skips descendants that carry a dir, code and paths no longer decide a sentence's direction. The list indent, quote bar and table borders move to logical properties, and the phone Compose textarea gets dir="auto". The terminal pane is unchanged: xterm.js has no right-to-left support (xtermjs/xterm.js#701). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
3e06f3c141
commit
538fcae708
5 files changed
+233
-7
No files matched your search
@@ -2835,6 +2835,29 @@ class CodemanApp {
|
||||
a.setAttribute('target', '_blank');
|
||||
a.setAttribute('rel', 'noopener noreferrer');
|
||||
});
|
||||
// Right-to-left text (issue #715). With no `dir`, every block was laid
|
||||
// out left to right, so a Persian, Arabic or Hebrew answer was
|
||||
// left-aligned, its punctuation landed on the wrong end and its lists
|
||||
// and quotes hung off the left edge. Each OUTERMOST prose block now
|
||||
// takes its direction from its own first letter (`dir="auto"`, what
|
||||
// GitHub does for rendered markdown), so an RTL paragraph beside an
|
||||
// English one reads correctly in both; nested blocks inherit, which
|
||||
// keeps a list's markers and indent on one side. A `dir` the agent
|
||||
// wrote (the sanitizer allows it) is kept, and so is what it covers.
|
||||
//
|
||||
// Code and preformatted text are always left to right. `dir="ltr"`
|
||||
// also isolates them, and `dir="auto"` skips any descendant that
|
||||
// carries a `dir`, so `npm install` at the head of a Persian sentence
|
||||
// no longer turns the whole sentence left to right.
|
||||
tmpl.content
|
||||
.querySelectorAll('p, h1, h2, h3, h4, h5, h6, ul, ol, dl, blockquote, .rv-table-wrap')
|
||||
.forEach((el) => {
|
||||
if (el.parentElement?.closest('[dir]') || el.hasAttribute('dir')) return;
|
||||
el.setAttribute('dir', 'auto');
|
||||
});
|
||||
tmpl.content.querySelectorAll('pre, code').forEach((el) => {
|
||||
if (!el.hasAttribute('dir')) el.setAttribute('dir', 'ltr');
|
||||
});
|
||||
return tmpl.innerHTML;
|
||||
} catch { /* fall through */ }
|
||||
}
|
||||
@@ -3064,6 +3087,9 @@ class CodemanApp {
|
||||
link.dataset.path = path;
|
||||
link.dataset.linkified = '1';
|
||||
if (folder) link.dataset.folder = '1';
|
||||
// A path reads left to right, and in a right-to-left sentence it must
|
||||
// not vote on the sentence's direction (`_renderMarkdown`, issue #715).
|
||||
link.dir = 'ltr';
|
||||
link.title = path;
|
||||
link.textContent = path;
|
||||
frag.appendChild(link);
|
||||
|
||||
@@ -1296,7 +1296,7 @@ const KeyboardAccessoryBar = {
|
||||
<strong>Compose prompt</strong>
|
||||
<span>Enter adds a new line</span>
|
||||
</div>
|
||||
<textarea class="paste-textarea prompt-composer-textarea" aria-label="Prompt" placeholder="Write your prompt…" autocorrect="on" autocapitalize="sentences" spellcheck="true"></textarea>
|
||||
<textarea class="paste-textarea prompt-composer-textarea" dir="auto" aria-label="Prompt" placeholder="Write your prompt…" autocorrect="on" autocapitalize="sentences" spellcheck="true"></textarea>
|
||||
<div class="paste-actions prompt-composer-actions">
|
||||
<button type="button" class="prompt-composer-terminal">Use terminal keyboard</button>
|
||||
<button type="button" class="paste-image">📎 Attach</button>
|
||||
@@ -1309,6 +1309,8 @@ const KeyboardAccessoryBar = {
|
||||
|
||||
this._composerOverlay = overlay;
|
||||
const textarea = overlay.querySelector('.prompt-composer-textarea');
|
||||
// The textarea's dir="auto" lays a prompt written in Persian, Arabic or
|
||||
// Hebrew out right to left, from its first letter (#715).
|
||||
// i18n.js skips <textarea> subtrees (what is typed there is user content),
|
||||
// so the placeholder and label are translated here, when the dialog is built.
|
||||
const i18n = typeof window !== 'undefined' ? window.CodemanI18n : undefined;
|
||||
|
||||
@@ -15085,7 +15085,7 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
.rv-text ul, .rv-text ol,
|
||||
.response-viewer-body > ul, .response-viewer-body > ol {
|
||||
margin: 0.6em 0;
|
||||
padding-left: 1.5em;
|
||||
padding-inline-start: 1.5em;
|
||||
}
|
||||
|
||||
.rv-text li,
|
||||
@@ -15097,12 +15097,16 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
|
||||
.rv-text blockquote,
|
||||
.response-viewer-body > blockquote {
|
||||
border-left: 3px solid var(--accent);
|
||||
border-inline-start: 3px solid var(--accent);
|
||||
background: rgba(var(--accent-rgb), 0.06);
|
||||
margin: 0.8em 0;
|
||||
padding: 0.5em 14px;
|
||||
color: var(--text-dim);
|
||||
border-radius: 0 6px 6px 0;
|
||||
/* Logical corners: the bar sits on the start side, right-to-left too (#715). */
|
||||
border-start-start-radius: 0;
|
||||
border-end-start-radius: 0;
|
||||
border-start-end-radius: 6px;
|
||||
border-end-end-radius: 6px;
|
||||
}
|
||||
|
||||
.rv-text strong,
|
||||
@@ -15172,9 +15176,9 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
.response-viewer-body > table td,
|
||||
.rv-table-wrap th, .rv-table-wrap td {
|
||||
border-bottom: 1px solid var(--border);
|
||||
border-right: 1px solid var(--border);
|
||||
border-inline-end: 1px solid var(--border);
|
||||
padding: 8px 12px;
|
||||
text-align: left;
|
||||
text-align: start;
|
||||
vertical-align: top;
|
||||
white-space: normal;
|
||||
}
|
||||
@@ -15183,7 +15187,7 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
.response-viewer-body > table th:last-child,
|
||||
.response-viewer-body > table td:last-child,
|
||||
.rv-table-wrap th:last-child, .rv-table-wrap td:last-child {
|
||||
border-right: none;
|
||||
border-inline-end: none;
|
||||
}
|
||||
|
||||
.rv-text tr:last-child td,
|
||||
|
||||
Reference in new issue
Block a user