fix: reuse image on case-insensitive path match (#3119) by dzid26 · Pull Request #5326 · marktext/marktext · GitHub
Skip to content

fix: reuse image on case-insensitive path match (#3119) - #5326

Merged
Jocs merged 4 commits into
marktext:developfrom
dzid26:path-case-insensitive
Sep 14, 2026
Merged

Jocs merged 4 commits into
marktext:developfrom
dzid26:path-case-insensitive

Conversation

@dzid26

@dzid26 dzid26 commented Sep 13, 2026 •

Copy link
Copy Markdown
Contributor

Closes #3119

Summary

The #3119 still was the issue (hashed file being copied) if for example the Relative folder was named images, but actual folder on the disk was Images. On Windows it is expected to reuse the folder regardless of the letter casing!

Use OS aware isSamePathSync function instead of string comparison.

Type of change

  • Bug fix (non-breaking, fixes an issue)
  • New feature (non-breaking, adds functionality)
  • Breaking change (causes existing functionality to change)
  • Documentation update

Test plan

  • New tests added (or explain why not needed)
  • Manually tested on: Windows

Notes for reviewers [optional]

Worth first merging #5323 which will trigger CI on Windows target.

By submitting this pull request, I confirm that my contribution is made under the terms of the MIT license.

dzid26 and others added 4 commits September 13, 2026 12:58
…arktext#3119)

When the dropped image matches outputDir only ignoring case, return the
outputDir-based path instead of the image's own casing. pathe.relative
compares segments case-sensitively, and the document path keeps whatever
casing it was opened with, so relativizing the image's casing produced
links like `../../../Users/me/proj/assets/x.png` that break on
case-sensitive systems. The outputDir-based path yields `assets/x.png`,
matching the links written for hashed copies.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011TMdsfzfm4LYG7MkBckAqQ
…mments (marktext#3119)

Drive letters can't differ in case here (pathe upper-cases them on both
sides), so explain the real source of the mismatch: the dropped file's
on-disk casing versus the preference and document path casing. Note that
the isSamePathSync stub models a case-insensitive filesystem, drop its
redundant strict-equality branch, and stop referencing the removed
`noHashPath === imagePath` check.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011TMdsfzfm4LYG7MkBckAqQ

@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 the fix, @dzid26! The approach is right. I checked the real common/filesystem/paths.isSamePathSync on a case-insensitive APFS folder: Images/foo.png vs images/foo.png gives === → false (the old code copied a hashed duplicate) and isSamePathSync → true. A missing file still returns false. Drag-and-drop reaches this branch through muya's getPathForFile, so #3119 benefits on macOS as well as Windows.

One regression, fixed in 07b5fea

The match returned toResult(imagePath), which keeps the dropped file's on-disk casing. toResult relativizes with pathe.relative, which compares segments case-sensitively. The main process only path.resolves opened files and never canonicalizes their casing. So when the document path's ancestor casing differs from the disk (e.g. marktext c:\users\me\proj\a.md typed in a terminal), the inserted link climbs out of the document folder:

Document opened as Dropped image (disk casing) Before this PR With imagePath With noHashPath
C:/users/me/proj/a.md C:\Users\me\proj\assets\x.png assets/<hash>.png ../../../Users/me/proj/assets/x.png assets/x.png
C:/Users/me/proj/a.md C:\Users\me\Proj\assets\x.png assets/<hash>.png ../Proj/assets/x.png assets/x.png
C:/Users/me/proj/a.md …\proj\Images\x.png images/<hash>.png Images/x.png images/x.png

Those links still open on Windows but break on GitHub, Linux, or a clone elsewhere. I pushed a commit that returns toResult(noHashPath). The link now always goes through outputDir, spelled like the links written for hashed copies. It also adds an isRelative: true case with mismatched ancestor casing. Without the fix that case yields ../../../../../../VAR/FOLDERS/…/ASSETS/Already.PNG; with it, assets/Already.PNG.

Spec tidy-up, in 273cb6d

  • The "drive-letter case" comment can't happen in production: window.path is pathe, which upper-cases drive letters on both sides. The comment now names the real source, the dropped file's on-disk casing versus the preference and document-path casing.
  • The isSamePathSync stub now says it models a case-insensitive filesystem (it never stats), and its redundant na === nb branch is gone.
  • An older comment still referenced the removed noHashPath === imagePath check.

Lint (0 errors), typecheck, and the spec (9/9) pass locally.

Out of scope, for a follow-up

getContentHash(imagePath) hashes the path string, not the image bytes. Re-saving a screenshot at the same source path and dropping it again reuses the hashed name, so copy overwrites the earlier asset that other documents may still reference. That predates this PR, so I left it alone.

@Jocs
Jocs merged commit 7c55177 into marktext:develop Sep 14, 2026
10 checks passed
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.

Image files are saved as different file names when using drag-and-drop

2 participants