- Fix false positive when cursor is between two code blocks: the old
upward search couldn't distinguish closing fences from opening ones
- Fix nested fence support: closing fence must be >= opening fence
length per CommonMark spec; shorter inner fences are now treated
as content lines
- Extract detection logic to pure function detectCodeBlockFromLines
in src/codeBlockDetect.ts for testability
- Add 37 unit tests covering the bug scenario, nested fences,
unclosed blocks, all CodeBlockBehavior modes, and fence-line edges
- Add CodeBlockBehavior enum: COPY_CONTENT / COPY_WITH_FENCES / GENERATE_BLOCK_LINK / DISABLED
- Add CODEBLOCK ContextType, detectCodeBlock() method in main.ts
- Code block detection runs before block ID generation (conflict resolution)
- Add Code Block settings group in settingTab.ts with dropdown
- Add CHANGELOG.md with full version history
- Update release workflow to extract notes from CHANGELOG.md
The maintainer reviews and writes comments in Chinese. To minimize
review friction and maximize merge likelihood, translate all
user-authored English comments across src/ into Simplified Chinese,
matching the upstream comment style. Test files included; describe/it
strings stay English (functional test names). Pre-existing English
comments authored by upstream contributors are left untouched.
Adds CONTRIBUTING-zh.md as a sibling translation of CONTRIBUTING.md
(matching the README.md / README-zh.md cross-linked convention).
Also corrects one Chinese comment in type.ts that became stale after
extending paste-time path resolution to all three Link format options.
No behavior change. 184 tests pass; typecheck and lint clean (the
pre-existing unrelated lint error in detectBlockId remains as before).
Designed, carefully reviewed, and edited by @lightmotive in collaboration with Claude Code.
Pull out computeDisplayText, shouldSimplifyHeading,
formatWikiHeadingLink, formatMarkdownHeadingLink as private helpers.
buildHeadingLink becomes a thin orchestrator: stripWikiBrackets →
computeDisplayText → shouldSimplifyHeading → wikiExactMatch policy →
formatX. Formatters are pure string-producers; the orchestrator owns
the WIKI [[filename]] exact-match policy (the A1.5 exception).
Pure refactor — no behavior change, all 184 existing tests pass without
modification.
Also extract stripWikiBrackets to its own export and reuse in
buildHeadingCopyMetadata (removes duplicated [[…]] stripping).
Designed, carefully reviewed, and edited by @lightmotive in collaboration with Claude Code.
Previously the paste handler only fired when linkFormat was "Follow
Obsidian settings". Users on explicit Wiki or Markdown got no
paste-time cleanup — pasting a heading into the same file produced the
verbose [[Filename#Heading|Heading]] instead of [[#Heading]].
Drop the OBSIDIAN-only gate in shouldRegisterPasteHandler and
decidePasteResolution. handlePaste branches by linkFormat: OBSIDIAN
keeps using app.fileManager.generateMarkdownLink (full newLinkFormat
support); explicit Wiki/Markdown uses app.metadataCache.fileToLinktext
(shortest-unique paths only). The same-file alias-redundancy fix from
the previous commit applies to both branches.
Add buildExplicitPasteLink in linkBuilder.ts as a pure string-producer
for the explicit-format branch — caller decides omitAlias via
shouldOmitAliasForSameFile, formatter consumes the boolean. Toggle now
shown for all three linkFormat options; description updated to reflect
format-aware behavior across en/zh/zh-tw.
Tests: new buildExplicitPasteLink suite covering wiki/md, same-file
vs cross-file, block links, and encoding behavior. pasteResolution
tests updated for the broadened gate (linkFormat field removed from
PasteResolutionInput; OBSIDIAN-only assertions dropped).
Trade-off: explicit Wiki/Markdown get shortest-path-only resolution;
users wanting relative/absolute path styles keep using "Follow
Obsidian settings" (which honors the vault's newLinkFormat config).
Designed, carefully reviewed, and edited by @lightmotive in collaboration with Claude Code.
When pasting a heading link into the same file it was copied from, the
paste handler now produces [[#Heading]] instead of [[#Heading|Heading]].
The alias was redundant — Obsidian's natural rendering of [[#Heading]]
already shows just "Heading" since there's no file portion to
disambiguate.
Cross-file pastes keep the alias (without it, Obsidian renders
"Filename > Heading" instead of just "Heading"). Markdown-format pastes
also keep the alias (it IS the visible link text in markdown). The
omission is wiki-only, same-file-only, useHeadingAsDisplayText-only.
Adds shouldOmitAliasForSameFile pure helper in pasteResolution.ts with
truth-table tests; handlePaste consults it before calling
generateMarkdownLink.
Designed, carefully reviewed, and edited by @lightmotive in collaboration with Claude Code.
Under "Follow Obsidian settings", defer link format/path choice to
Obsidian's vault config — but Obsidian's own generateMarkdownLink would
never auto-collapse a heading link to a file link when the filename
matches the heading. Keeping simplifiedHeadingToNoteLink active under
OBSIDIAN injects an Easy Copy opinion that conflicts with that intent.
Hide the toggle (and its strict-match child) from settings UI when
linkFormat is OBSIDIAN, and gate simplification inert at the call site
in copyHeadingLink. Stored values persist across format changes,
matching the precedent set by resolveLinkPathOnPaste.
Designed, carefully reviewed, and edited by @lightmotive in collaboration with Claude Code.
When useHeadingAsDisplayText is false, the user has chosen to see
"filename#heading" in the link display. Auto-simplifying the link
target to just the filename in that case produces a misleading
[filename#heading](filename) — display promises a heading link the
target doesn't deliver.
Add useHeadingAsDisplayText to the simplification gate. The WIKI
exact-match special case [[filename]] is intentionally exempt —
Obsidian renders it cleanly as just the filename, no alias, no
mismatch.
4 new MD A1.5 tests + 5 WIKI A1.5 tests covering the gate and the
exact-match exception. One existing test (frontmatterTitle +
note-link simplification, wiki) renamed to reflect gated behavior.
161 tests pass; typecheck clean.
Designed, carefully reviewed, and edited by @lightmotive in
collaboration with Claude Code.
The MDLINK branch in buildHeadingLink now uses linkContent (which the
early simplification block already computes), so a matching filename
and heading collapses [foo](foo#foo) → [foo](foo) just like the wiki
form already did. Was effectively a no-op for MD before — the toggle
existed but didn't affect markdown output.
10 new tests covering exact / case-insens / space-removed / substring /
strict-mode simplification cases. The MDLINK + matching-filename test
and the noSimplify MD sanity test are renamed to reflect their actual
intent now that simplification works for both formats.
107 tests pass; typecheck clean.
Designed, carefully reviewed, and edited by @lightmotive in
collaboration with Claude Code.
Addresses #35 maintainer feedback: gate paste-time resolution behind a
default-off toggle with 5-min TTL on lastCopyMeta. Switch from DOM
paste (capture phase) to editor-paste workspace event with documented
defaultPrevented yield protocol; register only when active.
Also fixes a pre-existing bug where simplifiedHeadingToNoteLink was
never read — the simplification fired unconditionally when filename
matched heading.
Designed, carefully reviewed, and edited by @lightmotive in collaboration with Claude Code.
Move the inline metadata-construction logic from the three copy paths
(block/heading/file) into pure helpers in src/copyMetadata.ts, matching
the project's pure-function test style. Adds 25 unit tests covering
alias, subpath, and embed computation for each copy type.
Intercept paste (DOM capture phase) to regenerate Easy Copy links via
generateMarkdownLink(), resolving shortest/relative/absolute paths based
on the destination file. Metadata is cleared on manual copy to prevent
false interception of non-Easy-Copy content.
Strip # | ^ : %% [[ ]] from heading link targets to match Obsidian's
autocomplete behavior. Fix compareIgnoreCase to use strict equality
instead of includes(), preventing false-positive note-link simplification.
Obsidian doesn't parse Markdown links with literal spaces in the URL
portion (e.g. `[text](file#My Heading)`). This encodes spaces as `%20`
in the URL part of all Markdown-format links — heading links, block
links, and file links — so they render correctly.
Closes#29
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>