Skip to content

feat(windows): support displaying and opening .lnk shortcut files#87

Open
DryheeHuang-Here wants to merge 1 commit into
Tkaixiang:trunkfrom
DryheeHuang-Here:feature/windows-shortcut-support
Open

feat(windows): support displaying and opening .lnk shortcut files#87
DryheeHuang-Here wants to merge 1 commit into
Tkaixiang:trunkfrom
DryheeHuang-Here:feature/windows-shortcut-support

Conversation

@DryheeHuang-Here
Copy link
Copy Markdown

feat(windows): support displaying and opening .lnk shortcut files

Allow MarkText to show Windows shortcut (.lnk) files in the sidebar file tree and resolve them to their target files when opened. This enhancement treats shortcuts as if they were the actual files, improving the Windows experience.

Key changes:

  • Add "resolveShortcut" helper function that parses .lnk files and returns the resolved target path (or the original path if resolution fails)
  • In the sidebar file tree, filter .lnk files by checking whether the resolved target's extension is included in "MARKDOWN_EXTENSIONS"; only display those that point to supported file types
  • Update file opening logic to call "resolveShortcut" on the given path before reading the file with "fsPromises.readFile" in "loadMarkdownFile", ensuring the actual target content is loaded

Screenshots

This screenshot shows ".lnk" shortcut files now appearing in the sidebar and successfully opening the target Markdown document.

image

Show .lnk shortcut files in the sidebar only when they point to file types natively supported by MarkText (e.g., .md, .txt). When such a shortcut is clicked, its target file is opened directly. Shortcuts pointing to unsupported file types are filtered out and do not appear in the sidebar.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds Windows .lnk shortcut awareness so MarkText can surface shortcuts in file-related flows and open the shortcut’s target markdown content rather than the .lnk file itself.

Changes:

  • Introduce resolveShortcut helper to resolve Windows .lnk files to their target paths.
  • Update markdown extension detection to consider a shortcut’s resolved target extension.
  • Update markdown file loading to read from the resolved shortcut target path.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/main/filesystem/markdown.js Loads markdown content from a resolved shortcut target before decoding.
src/common/filesystem/paths.js Adds shortcut resolution helper and uses it in markdown-extension detection.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

// encoding on the first 256/512 bytes.

let buffer = await fsPromises.readFile(path.resolve(pathname))
let buffer = await fsPromises.readFile(path.resolve(resolveShortcut(pathname)))
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

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

loadMarkdownFile now reads from resolveShortcut(pathname) but still treats pathname/filename as the original input later in the function. If a user opens a .lnk, the tab will keep the .lnk pathname and saving will write back to the shortcut path (potentially corrupting the .lnk) instead of saving to the target markdown file. Consider resolving once up-front (after path.resolve) and using the resolved target path consistently for reading and for the returned pathname/filename (optionally also preserving the original shortcut path separately if needed).

Copilot uses AI. Check for mistakes.
export const hasMarkdownExtension = (filename) => {
if (!filename || typeof filename !== 'string') return false
return MARKDOWN_EXTENSIONS.some((ext) => filename.toLowerCase().endsWith(`.${ext}`))
return MARKDOWN_EXTENSIONS.some((ext) => resolveShortcut(filename).toLowerCase().endsWith(`.${ext}`))
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

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

hasMarkdownExtension now calls resolveShortcut(filename) on every invocation. On Windows this causes shell.readShortcutLink to run for non-.lnk paths as well (including simple names like "foo" from the renderer), which can be a hot path (watcher filtering, quick open, etc.) and may rely on exceptions for control flow. Consider making resolveShortcut a no-op unless the path ends with .lnk (and ideally exists/is a file) so that normal markdown extension checks stay cheap.

Suggested change
return MARKDOWN_EXTENSIONS.some((ext) => resolveShortcut(filename).toLowerCase().endsWith(`.${ext}`))
const normalizedFilename = filename.toLowerCase()
const candidate = isWindows && normalizedFilename.endsWith('.lnk')
? resolveShortcut(filename)
: filename
return MARKDOWN_EXTENSIONS.some((ext) => candidate.toLowerCase().endsWith(`.${ext}`))

Copilot uses AI. Check for mistakes.
Comment thread src/common/filesystem/paths.js
@Tkaixiang
Copy link
Copy Markdown
Owner

Hi, this implementation works but there is already existing logic to resolve Linux/MacOS link files. Perhaps you could consider adding on to that instead of doing resolving here?

Another issue of this approach is as what Copilot pointed out, it will return the symlink as the pathname instead and updates will write to it. This creates a significant bug.

See normalizeAndResolvePath in src\main\filesystem\markdown.js for resolving
and isMarkdownFile for allowing .lnk files to be allowed

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.

3 participants