math in Persian text: lists read right-to-left, every content text goes through MathText, chat input picks its direction per line
Measured by rendering every question, option and several tutor answers in
Chrome and comparing the on-screen x of each number (2026-09-29):
- A list inside a sentence ('از −۳ به −۲، −۱، ۰ میرسیم', 'الگویِ ۲، ۵، ۸')
was locked as one left-to-right run, so all 482 read backwards. '،' and
'؛' outside brackets now split the run; sets and coordinates in brackets
stay one run. Locked runs are isolates (isolate-override), otherwise the
comma between two of them was still pulled left-to-right.
- Without MathText 704 of 931 minus signs and 602 of 983 expressions were
misplaced; lesson and island titles, the lesson intro, unit blurb,
notification bodies and ticket messages now go through it.
- The chat box takes its direction from each line's first letter, so a
line of pure math stays left-to-right while typing. A line that starts
in Persian can still show its math reordered inside the box; a textarea
cannot isolate part of its text. The sent message renders correctly.
Four expressions with Persian words inside them ('f(x) = (ریشه دوم x) + ۵۰')
remain ambiguous.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import type { HomeNode } from '@/api';
|
||||
import { ZigzagPath, zigzagPositions } from '@/components/ZigzagPath';
|
||||
import { iconFor } from '@/components/NodeIcon';
|
||||
import { shade } from '@/lib/color';
|
||||
import { MathText } from '@/lib/MathText';
|
||||
|
||||
const NODE_SIZE = 76;
|
||||
const SPACING = 128;
|
||||
@@ -97,7 +98,7 @@ export const IslandPath = forwardRef<HTMLDivElement, {
|
||||
points[i].x >= 50 ? 'right-full mr-3 text-right' : 'left-full ml-3 text-left'
|
||||
} ${current ? 'text-[12px] text-ink' : locked ? 'text-faint' : 'text-muted'}`}
|
||||
>
|
||||
<span className="line-clamp-2">{node.title}</span>
|
||||
<span className="line-clamp-2"><MathText text={node.title} /></span>
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useAsync } from '@/lib/useAsync';
|
||||
import { Sheet } from '@/components/Sheet';
|
||||
import { ErrorState, PrimaryButton } from '@/components/ui';
|
||||
import { toFa } from '@/lib/format';
|
||||
import { MathText } from '@/lib/MathText';
|
||||
|
||||
/**
|
||||
* برگهی جزیره. کارِ اصلی یک دکمه است: «شروعِ آزمونِ جزیره». فهرستِ درسها
|
||||
@@ -41,7 +42,7 @@ export function IslandSheet({ node, color, onClose }: { node: HomeNode | null; c
|
||||
const list = lessons.data?.lessons.filter((l) => playable.has(Number(l.id))) ?? [];
|
||||
|
||||
return (
|
||||
<Sheet open={node !== null} onClose={close} title={node?.title}>
|
||||
<Sheet open={node !== null} onClose={close} title={node ? <MathText text={node.title} /> : undefined}>
|
||||
{locked ? (
|
||||
<div className="flex flex-col items-center gap-3 py-4 text-center">
|
||||
<span className="flex h-14 w-14 items-center justify-center rounded-full bg-soft">
|
||||
@@ -93,7 +94,7 @@ export function IslandSheet({ node, color, onClose }: { node: HomeNode | null; c
|
||||
) : (
|
||||
<Circle size={18} className="flex-shrink-0 text-faint" />
|
||||
)}
|
||||
<span className={`flex-1 ${lesson.done ? 'text-muted' : 'text-ink'}`}>{lesson.title}</span>
|
||||
<span className={`flex-1 ${lesson.done ? 'text-muted' : 'text-ink'}`}><MathText text={lesson.title} /></span>
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user