Skip to content

fix(desktop): keep title bar path segments in order under the Bidi algorithm - #5317

Merged
Jocs merged 3 commits into
marktext:developfrom
dronov-dmitry:fix/titlebar-filename-rtl-reorder
Sep 18, 2026
Merged

Jocs merged 3 commits into
marktext:developfrom
dronov-dmitry:fix/titlebar-filename-rtl-reorder

Conversation

@dronov-dmitry

@dronov-dmitry dronov-dmitry commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

Problem

When opening a file named 003_report.md in MarkText on Windows x64, the filename in the window title bar was displayed as report.md_003 instead of 003_report.md. Characters in the filename were reordered.

Root cause

The CSS property direction: rtl on the outer <span> container in the title bar activates the Unicode Bidi Algorithm. On Windows, breadcrumb segments never render (because PATH_SEPARATOR from pathe is always /, so Windows paths like C:\Users\me\Desktop\003_report.md don't split). Without a preceding strong LTR character, the Bidi Algorithm reorders neutral characters — digits 003 resolve as RTL, and the runs swap.

On Linux, breadcrumb segments (e.g. Desktop ›) render first, providing a strong LTR context that keeps digits in order.

Fix

Isolate LTR content inside the RTL container using <bdi dir=ltr>:

  • Keep direction: rtl on the outer <span> for GH#339 left-side clipping (long paths are clipped from the left, filename stays visible)
  • Wrap breadcrumbs, filename, and save-dot inside <bdi dir=ltr> to prevent the Bidi Algorithm from reordering characters
  • Add unicode-bidi: isolate on inner spans to prevent RTL context inheritance
  • Move save-dot margin-right to margin-left (correct positioning in LTR context)

Screenshot

Bug on Windows — filename reordered

Testing

Check Result
pnpm run lint Passed (0 errors)
pnpm run typecheck Passed
Manual test on Windows Filename displays correctly as 003_report.md

Related

  • Fixes GH#339 workaround regression on Windows
  • direction: rtl was originally added for GH#339 (clip long paths from the left)

dronov-dmitry added a commit to dronov-dmitry/marktext that referenced this pull request Sep 12, 2026
@dronov-dmitry

Copy link
Copy Markdown
Contributor Author

Bug on Windows 0.19.1 — filename reordered

@Jocs Jocs left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tracking this down, @dronov-dmitry. The reordering is real, but replacing direction: rtl with text-align: right introduces two layout regressions. I measured each variant in Chromium using the title bar's markup and CSS.

Regressions

  1. Long paths now hide the filename (GH#339 is back). When inline content overflows its line box, CSS aligns it to the start edge and it overflows at the end edge, whatever text-align is set to (CSS Text 3, text-align). With LTR, the end edge is on the right. The leading folders stay visible, while the filename and the unsaved dot are clipped (in my test the filename's right edge was 248px past the clip edge). direction: rtl was there to put the overflow on the left.
  2. Titles are no longer centered. The span used to inherit text-align: center from .title. text-align: right overrides that, so every title (including the MarkText placeholder) is pushed against the right padding.
  3. The unsaved dot now touches the filename. Under RTL the dot was drawn to the left of the title, and margin-right: .25rem provided the gap. In LTR it follows the filename, and the margin ends up on its far side.

Root cause

This isn't a DirectWrite/FreeType difference. It's the standard Unicode bidi algorithm, and Chrome on macOS renders report.md_003 too. In an RTL paragraph, the digits 003 only stay in place if a strong LTR character comes before them. Otherwise they resolve as RTL, _ stays at the paragraph level, and the runs swap.

It only shows up on Windows because the breadcrumb segments never render there. window.path.sep comes from pathe, which is always /, and titleBar/index.vue:217 splits the raw Windows pathname on it. C:\Users\me\Desktop\003_report.md produces no segments, so nothing precedes the digits. On Linux, e.g. Desktop › comes first and keeps them in order. That split is a separate bug and should be fixed on its own.

The PR also doesn't handle RTL folder names. With a Hebrew folder (Users › מסמכים › 003_report.md), this version still renders the segments out of order.

Suggested fix

Keep the RTL box for the left-side clipping, and isolate the LTR content inside it:

<span v-else>
  <bdi dir="ltr">
    <span v-for="(path, index) of paths" :key="index">…</span>
    <span class="filename" …>{{ filename }}</span>
    <span class="save-dot" :class="{ show: !isSaved }" />
  </bdi>
</span>
div.title > span {
  /* Workaround for GH#339 */
  display: block;
  direction: rtl;
  overflow: hidden;
  text-overflow: clip;
  white-space: nowrap;
}
div.title > span > bdi > span {
  unicode-bidi: isolate;
}
.active .save-dot {
  margin-left: 0.25rem; /* instead of margin-right */
}

Measured in Chromium:

Case current develop this PR suggested
003_report.md, no breadcrumbs report.md_003 ❌ order OK, right-aligned ❌ order OK, centered ✅
Users › me › Desktop › 003_report.md OK, centered right-aligned ❌ centered ✅
Hebrew folder + 003_report.md reordered ❌ reordered ❌ correct ✅
long path overflow clipped left, filename visible filename clipped ❌ clipped left, filename visible ✅
unsaved dot on long path clipped (drawn on the left) clipped visible ✅

Other notes

  • Please drop bag_windows_0.19.1.png from the commit. It's a binary at the repo root (committed with mode 100755) that's only used by the PR description. A PR comment attachment works for that.
  • The new CSS comment describes the change ("replaced … with …") and gives the Windows explanation. Once the fix above is in, the original /* Workaround for GH#339 */ is enough.

@dronov-dmitry
dronov-dmitry force-pushed the fix/titlebar-filename-rtl-reorder branch from 2adbfb5 to 8c79b4c Compare September 13, 2026 13:50
dronov-dmitry and others added 3 commits September 18, 2026 21:03
…indows

Wrap the title bar filename and breadcrumb spans inside a `<bdi dir=ltr>`
element to isolate LTR content from the outer RTL container. This prevents
the Unicode Bidi Algorithm from reordering characters in filenames
(e.g. "003_report.md" was displayed as "report.md_003" on Windows
where breadcrumb segments don't render).

- Keep `direction: rtl` on the outer span for GH#339 left-side clipping
- Add `unicode-bidi: isolate` on inner spans to prevent RTL inheritance
- Move save-dot margin from right to left (correct positioning in LTR)

Co-Authored-By: dronov-dmitry <dronov-dmitry@users.noreply.github.com>
The branch predates marktext#5318, whose lint-staged config still ran
`prettier --write` over .ts/.vue on commit. The hook reflowed the whole
template, burying a fifteen-line fix in sixty lines of churn and leaving
this component as the only one not using one attribute per line.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Without a note, `unicode-bidi: isolate` reads as belt-and-braces next to
the `<bdi>` and invites removal. It is load-bearing: drop it and a
Hebrew or Arabic folder name pulls its separator across the segments
beside it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Jocs
Jocs force-pushed the fix/titlebar-filename-rtl-reorder branch from 8c79b4c to 5c667fa Compare September 18, 2026 13:17
@Jocs

Jocs commented Sep 18, 2026

Copy link
Copy Markdown
Member

Thanks @dronov-dmitry — this revision is the right fix, and I've verified it in the real app on macOS before merging.

It isn't Windows-only

Worth recording, because it changes how the bug should be described: direction: rtl reorders the title bar on any platform whenever the last three path segments contain no strong LTR character. Windows hits it every time only because of a second, independent bug (below) that leaves paths empty.

Measured on macOS by launching the built app and sorting the title bar's characters by their rendered x position:

Path opened Before After
~/notes/2024/09/18/003_report.md report.md_003 › 18 › 09 › 2024 2024 › 09 › 18 › 003_report.md
~/notes/2024/09/18/report.md report.md › 18 › 09 › 2024 2024 › 09 › 18 › report.md
~/notes/2024/מסמכים/09/003_report.md segments reversed 2024 › מסמכים › 09 › 003_report.md
~/notes/Reports/2024/09/003_report.md correct already unchanged

Row 2 is the interesting one: the filename contains no digits at all, yet the breadcrumbs still render backwards. Row 4 is the control — its first segment Reports supplies the strong LTR character that rows 1–3 lack. Date-shaped archive folders (2024/09/18) are common enough that this was reachable on macOS all along.

The parts of the fix I checked

  • unicode-bidi: isolate is load-bearing, not belt-and-braces. With <bdi dir="ltr"> alone, a Hebrew or Arabic folder name drags its separator across the neighbouring segments: 003 › 2024 › מסמכים_report.md instead of מסמכים › 2024 › 003_report.md. I added a comment above the rule so it doesn't get tidied away later.
  • GH#339 still holds. Long paths continue to clip from the left with the filename kept visible.
  • margin-left fixes a second thing. With margin-right, an overflowing path pushed the save dot outside the clip region; it now stays visible.

Two commits pushed on top

  1. style(desktop) — rebased onto develop and restored the component's original formatting. The whole-template Prettier reflow wasn't your doing: this branch predates chore: run only eslint --fix on staged desktop TS/Vue files #5318, where lint-staged still ran prettier --write over .ts/.vue on commit, so the hook reflowed the file behind you. On current develop the hook only runs eslint --fix, so the reflow no longer happens. The diff is back to 32/23 lines, all of it the actual fix.
  2. docs(desktop) — the rationale comment on the isolate rule.

Follow-up, not this PR

Windows renders no breadcrumbs at all, which is why it always lacks a strong LTR character: PATH_SEPARATOR is window.path.sep, and the preload's pathe reports / on every platform, so a C:\Users\… pathname never splits and paths is always []. Splitting on /[\\/]/ would fix it. I'll track that separately — this PR is correct with or without it.

@Jocs Jocs changed the title fix: prevent filename character reordering in title bar on Windows fix(desktop): keep title bar path segments in order under the Bidi algorithm Sep 18, 2026
@Jocs
Jocs merged commit 965705a into marktext:develop Sep 18, 2026
10 checks passed
Jocs added a commit that referenced this pull request Sep 19, 2026
…ath (#5467) (#5468)

* fix(desktop): put the unsaved dot back on the left of the title bar path (#5467)

`div.title > span` carries `direction: rtl` so that an over-long path clips
from the left and leaves the filename readable (the GH#339 workaround). The
save dot used to be the last child of that RTL block, and an empty atomic
inline is a neutral: under an RTL base direction a trailing neutral resolves to
the paragraph level, which laid the dot out at the visual left of the line.

#5317 wrapped the breadcrumb in `<bdi dir="ltr">` to stop the Bidi algorithm
reordering numeric path segments, and carried the dot into the isolate with it.
Inside an LTR isolate the dot is simply the last item in logical order, so it
moved to the right of the filename — a user-visible change that was never the
point of that PR.

Lay the title out with flexbox instead: the dot becomes a flex item preceding
the breadcrumb, and the RTL clip context moves onto the `.path` box alone. The
dot is back on the left, #5317's isolation is untouched, and because the dot
now sits outside the clip box an over-long path can no longer swallow it — as
it did before #5317.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(desktop): pin the title bar dot position and breadcrumb order (#5467)

Neither property had a guard: #5317 could move the dot without a test going
red, and the Bidi ordering it fixed was never pinned either.

Geometry is the only honest assertion here — the layout falls out of the
Unicode Bidi Algorithm and of overflow clipping, neither of which jsdom
computes — so these specs drive the real app under Playwright and compare
boxes: the dot left of the first path segment, the dot still inside the clip
box when a long path is truncated, and numeric segments in path order.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants