Skip to content

Feat: allow link to ^id anchor#7744

Open
linonetwo wants to merge 9 commits into
TiddlyWiki:masterfrom
linonetwo:feat/section-mark
Open

Feat: allow link to ^id anchor#7744
linonetwo wants to merge 9 commits into
TiddlyWiki:masterfrom
linonetwo:feat/section-mark

Conversation

@linonetwo

@linonetwo linonetwo commented Sep 15, 2023

Copy link
Copy Markdown
Contributor

This is block-level link. Prepare for block-level backlink. (wait for #6081 so I can continue modify back-indexer)

Add a new ^id syntax, see #7537 for details.

Also modify pretty wiki link's parsing rule to allow link to such an id. And modify link widget to allow id widget to react to link jumping.

(Currently, if New Tiddler is not currently opened, hookFocusElementEvent can't highlight the element, I have to use setTimeout to wait for the dom element actually show. Is there a better solution?)

And sadly, we can't use #xxx as ID, otherwise will encounter Invalid selector: ##BlockLevelLinksID1

Demo: https://tiddlywiki5-git-fork-linonetwo-feat-d29eaa-jermolenes-projects.vercel.app/#Block%20Level%20Links%20in%20WikiText:%5B%5BBlock%20Level%20Links%20in%20WikiText%5D%5D%20%5B%5BLinking%20in%20WikiText%5D%5D

title: New Tiddler 1

[[New Tiddler^sdfd]]

[[New Tiddler^bbbb]]

[[ddd|New Tiddler^ddd]]

sdfa
title: New Tiddler

\```css
.main {
  display: none;
}
\```

^sdfd

asfd ^bbbb

* adfgd ^ccc
** dsaffda ^ddd
** adsfad

@vercel

vercel Bot commented Sep 15, 2023

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated (UTC)
tiddlywiki5 ✅ Ready (Inspect) Visit Preview Jun 9, 2024 3:44pm

@kookma

kookma commented Sep 16, 2023

Copy link
Copy Markdown
Contributor

This is a minor comment: The highlighted color would be nice to follow the palette in use.
This is another minor comment: a toolbar button/shortcut to create unique/sequence number or identifier when creating section mark.

@kookma

kookma commented Sep 16, 2023

Copy link
Copy Markdown
Contributor

Some use cases:

  1. with footnotes, click to show the footnote at the tiddler bottom
  2. with references like refnotes, click to highlight the reference in the bibliograpgy/references tiddler
  3. partial transclusion when start and end of section is defined

@linonetwo

linonetwo commented Sep 16, 2023

Copy link
Copy Markdown
Contributor Author

The highlighted color would be nice to follow the palette in use.

Sure, I'm using the existing <<colour highlight-background>>, I'm not sure if there are some better configurable alternatives.

https://github.com/Jermolene/TiddlyWiki5/blob/07130c2ad094dcb04c65fb8ce8496a40afb8a2ac/themes/tiddlywiki/vanilla/base.tid#L2416-L2420

a toolbar button/shortcut to create unique/sequence number or identifier

I'm not sure if this is necessary, I will auto create id on target tiddler, when you write a link in another, when using the WYSIWYG editor (or maybe codemirror-enhannced), this is the UX of Obsidian. While it is reasonable to fall back to a menu button, if user is using empty edition, but that will have a huge UX gap between Obsidian.

footnotes

Yes, it can be used to write footnotes and in refnotes. But its main purpose is for block-level backlinks, and block-level transclusion, like in Obsidian. We need to have all that they have!

partial transclusion when start and end of section is defined

I think Obsidian can only transclude a single block. I'm not sure if transclude syntax can be extended to reference 2 id to create a reference...

@kookma

kookma commented Sep 16, 2023

Copy link
Copy Markdown
Contributor

Sure, I'm using the existing <<colour highlight-background>>, I'm not sure if there are some better configurable alternatives.

I think this is good to go!

I think Obsidian can only transclude a single block.

That is fine.

Thank you

@kookma

kookma commented Sep 17, 2023

Copy link
Copy Markdown
Contributor

If a line started with space or tab then TW does not recognize the ^id. For example

 ^a110 this is a test

Note to the space before ^a110. So it seems leading white spaces on a line started with an ^id is an issue here.

@kookma

kookma commented Sep 17, 2023

Copy link
Copy Markdown
Contributor

Suggestion:

  1. Is there any limitation for choosing an id? If so please add these limitation to documentation
  2. I would recommend to not allow special chars in id like {} or << etc. Keep it simple
  3. The length of id: is it one char, or at least two there chars?

@linonetwo

linonetwo commented Sep 17, 2023

Copy link
Copy Markdown
Contributor Author

The doc is at https://tiddlywiki5-git-fork-linonetwo-feat-section-mark-jermolene.vercel.app/#Block%20Level%20Links%20in%20WikiText

You can modify it by PR to my branch.

1 char is enough, but I'd use 6 char hash when auto generate it.

@pmario

pmario commented Sep 17, 2023

Copy link
Copy Markdown
Member

@linonetwo In the discussion #7744 you wrote, that the IDs would be "namespaced". So if the same ID is used in 2 tiddlers that are visible at the same time it would be possible to deterministically link to them.

In the preview that's not the case atm.

Both "instances" which render the content have enough information to make it happen.
eg: Both links using the code below need to work if they are visible at the same time.

But I think it can work.

title: aaa

some text ^link1

[[ccc^link1]]

and

title: ccc

some text ^link1

[[aaa^link1]]

  1. When the ID element ^link1 is rendered, the name of the tiddler is known. So the HTML id attribute can be created with a unique prefix based on the title of the tiddler.
  2. When the [[aaa^link1]] is created, it should have enough information to link to the same ID as described in 1.
  3. Due to the limiting rules for ID attribute values we should do some prefix normalisation similar to the "qualify" mechanism.

So I think it should be possible. I'm experimenting atm.

@linonetwo

linonetwo commented Sep 17, 2023

Copy link
Copy Markdown
Contributor Author

Okay, I forgot this. I think simply prefixing the id with the tiddler title is enough.

But blockid ast node don't know itself is in which tiddler...So blocked widget don't know this too! Can you get title in the BlockIdWidget.prototype.render method?

For the backlink scenario current implementation is enough, because we can get backlink title, then just search for the id in that tiddler's text.

@pmario

pmario commented Sep 17, 2023

Copy link
Copy Markdown
Member

But blockid ast node don't know itself is in which tiddler...So blocked widget don't know this too! Can you get title in the BlockIdWidget.prototype.render method?

Should be possible to get the currentTiddler or the transclude variable. ID's cannot be prefixed with the title since not every character is allowed. Have a look at my MDN link from the other post

@linonetwo

linonetwo commented Sep 17, 2023

Copy link
Copy Markdown
Contributor Author

Ok, Now it works, I also update the doc to use same id in two tiddlers.

Since I'm using data selector, I will use data selector for id, instead of id selector, so we can use any char in the id, like [[Link to Title|Block Level Links in WikiText^🤗→AddingIDforblock]]

@linonetwo

linonetwo commented Sep 17, 2023

Copy link
Copy Markdown
Contributor Author

Can this totally replace the Linking within tiddlers - "anchor links" usage?

Comment thread core/modules/widgets/blockid.js Outdated

BlockIdWidget.prototype.hookNavigationAddHistoryEvent = function(event) {
// DEBUG: console this._isNavigateToHere(event)
console.log(`this._isNavigateToHere(event)`, this._isNavigateToHere(event));

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.

In the TW core we still use ES5 only. So JS template literals are not possible atm: `this._isNavigateToHere(event)`
So this code needs to be changed.

@linonetwo linonetwo Sep 18, 2023

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks, this is debug statement, forget to delete...

@linonetwo linonetwo Sep 18, 2023

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Created by this in vscode BTW

  "Print to console": {
    "prefix": "lo",
    "scope": "javascript,javascriptreact,typescript,typescriptreact",
    "body": ["// DEBUG: console $1", "console.log(`$1`, $1);"],
    "description": "Log formated JSON output and var name to console"
  },

@pmario

pmario commented Sep 19, 2023

Copy link
Copy Markdown
Member

@CrossEye -- Can you have a closer look at this one. IMO it's related to your implementation of the permalink creation.

@Jermolene Jermolene 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.

Thank you @linonetwo and apologies for the delay in reviewing this.

I have a number of concerns about the complexity and brittleness of this implementation:

  • The use of hooks for widgets to communicate with one another is not a technique that we use in the core. Hooks are provided for plugin developers to be able to experiment with modifications to the core behaviour without having to modify the core code. For core improvements, we can make any changes we need, and so don't need the blunt instrument of hooks
  • The way that the parse rule can refer to the previous or current block is deeply confusing
  • I don't understand the need for the "blockid" widget; I would expect the syntax rule for an anchor just to generate a simple <a> element. I suspect that this is because of the way that block ids can apply to the previous block

I think it would be clearer to avoid the term "block"; in TiddlyWiki, we use the word when we're talking about parser modes, but we do not refer to blocks within tiddlers. It would be clearer to just call them "anchors".

@Jermolene

Copy link
Copy Markdown
Member

Hi @linonetwo

I think that was the previous version of this PR?

But that requires what you said:

widgets to reach up the tree and address their parents and siblings.

so when finding what it points to, it could find the real "block" (by block, I mean Block Mode WikiText).

I don't recall the details, but my current thinking is that we would only need to descend the widget tree to find the anchor widgets.

If you mean

<$anchor id="myId">
A paragraph.
</$anchor>

instead of ^id, it would be difficult to write by hand, only possible to be generated by editor.

The ^id syntax would be a shortcut for the widget.

@Jermolene Do you still want the word to be "anchor"? Like [[XXX]backlinks:anchor[]] [<sourceTitle>anchors[TargetTitle]] , adding ^id is adding an anchor, and the id part is "anchor name"?

Yes, I think the current terminology works well.

@TiddlyWiki TiddlyWiki deleted a comment from github-actions Bot Feb 23, 2026
@TiddlyWiki TiddlyWiki deleted a comment from github-actions Bot Feb 23, 2026
@TiddlyWiki TiddlyWiki deleted a comment from github-actions Bot Feb 23, 2026
@TiddlyWiki TiddlyWiki deleted a comment from github-actions Bot Feb 23, 2026
@TiddlyWiki TiddlyWiki deleted a comment from github-actions Bot Feb 23, 2026
@TiddlyWiki TiddlyWiki deleted a comment from github-actions Bot Feb 23, 2026
@TiddlyWiki TiddlyWiki deleted a comment from github-actions Bot Feb 23, 2026
@TiddlyWiki TiddlyWiki deleted a comment from netlify Bot Feb 23, 2026
- Remove all temporary/marker node types and inline anchor rule
- Add .utils.extractInlineAnchor(nodes) for trailing ^id detection
- parseBlock, heading, and list rules now directly extract and wrap anchors
- No post-processing, no marker nodes, no _anchorId, no wrapAnchors
- All tests pass, lint clean

BREAKING CHANGE: Only trailing ^id syntax is supported for block anchors. No more anchor-marker or inline anchor rule.
- Remove anchor.js inline rule file
- Update all block rule and serializer files to match trailing ^id anchor architecture
- Update all anchor-related test data and documentation
- All tests pass, lint clean

This commit ensures all anchor-related files are consistent with the new trailing ^id syntax and no temporary node types are used.
…chitecture

Replace the dual anchorblock-rule + extractInlineAnchor approach with a
clean two-layer design:

1. New inline anchor rule (anchor.js) matches trailing " ^id" on any line
   and emits a name anchor node — an empty placeholder consumed by the
   serializer to output " ^id".

2. wrapAnchorsInTree() (parsetree.js), called once at the end of
   parseBlock(), recursively finds name anchors inside block elements and
   wraps those elements in a target anchor container.

Key changes:
- codeblock/typedblock opening lines now parsed via parseInlineRun so
  inline anchor rule fires naturally (e.g. ```css ^id, $$$type ^id)
- quoteblock cite parsed without skipWhitespace so " ^id" is visible to
  the inline rule (e.g. <<< ^id or <<< Some cite ^id)
- list.js parser fully restored to origin/master (no anchor logic)
- anchorblock parser rule removed; ^id-on-own-line syntax dropped
- anchor widget: name anchors render nothing; target anchors render the
  wrapping container with data-tw-anchor / id / tabindex attributes
- quoteblock serializer updated to round-trip cite + anchor
- All serializers updated to use name/target anchor distinction

Docs: split Block Level Links into separate Block Level Transclusion
tiddler; add real anchor examples and clarify ^start..^end range syntax.

Tests: 1480 specs, 0 failures
@github-actions

Copy link
Copy Markdown

✅ Change Note Status

All change notes are properly formatted and validated!

📝 $:/changenotes/5.5.0/#7744

Type: feature | Category: hackability
Release: 5.5.0

Support block-level linking with anchors

🔗 #7744

👥 Contributors: linonetwo


📖 Change Note Guidelines

Change notes help track and communicate changes effectively. See the full documentation for details.

@github-actions

Copy link
Copy Markdown

📊 Build Size Comparison: empty.html

Branch Size
Base (master) 2486.7 KB
PR 2510.3 KB

Diff: ⬆️ Increase: +23.6 KB

🔴 Significant Increase

⚠️ Warning: Size increased significantly.

…to ^start^end

Since anchor ids cannot contain ^, the second ^ is an unambiguous separator.
This makes {{Title^start^end}} consistent with {{Title^anchor}} — no new
delimiter characters needed.

Update parseTextReference regex, all tests, serializer, and documentation.
@github-actions

Copy link
Copy Markdown

✅ Change Note Status

All change notes are properly formatted and validated!

📝 $:/changenotes/5.5.0/#7744

Type: feature | Category: hackability
Release: 5.5.0

Support block-level linking with anchors

🔗 #7744

👥 Contributors: linonetwo


📖 Change Note Guidelines

Change notes help track and communicate changes effectively. See the full documentation for details.

@github-actions

Copy link
Copy Markdown

📊 Build Size Comparison: empty.html

Branch Size
Base (master) 2486.7 KB
PR 2510.5 KB

Diff: ⬆️ Increase: +23.8 KB

🔴 Significant Increase

⚠️ Warning: Size increased significantly.

@linonetwo

Copy link
Copy Markdown
Contributor Author

Now, Any block can be tagged with a trailing ^id on its opening line:

A paragraph. ^para1

!! Heading ^h1

(code fence ` ` `)css ^codeBlock
.foo { color: red; }
(code fence ` ` `)

<<< some cite ^quoteBlock
Some quote.
<<<

* list item ^listItem

How it works (two-layer parse)

  1. Inline anchor rule — a new wikirule of type inline matches ^id at end of a line and emits an empty placeholder node ({type:"anchor", name:"true"}). This is transparent to all existing block rules; they don't need to know about anchors.

  2. wrapAnchorsInTree() — called once after parseBlock() returns. It recursively scans the result tree, finds placeholder nodes inside block elements (p, h1h6, li, blockquote, codeblock, void), and wraps the enclosing block in a target anchor container ({type:"anchor", target:"true", children:[block]}).

Transclusion

  • Single block: {{Title^anchorId}}
  • Range: {{Title^startId^endId}}^ introduces the start anchor, second ^ introduces the end anchor (anchor name don't allow containing ^)

No big changes to existing rules, no accessing parient widget

codeblock, typedblock, quoteblock, list — their opening lines are now parsed with parseInlineRun (as quoteblock already did for cite text), which naturally picks up the inline anchor rule. Zero special-casing inside block rules.

And now wrapAnchorsInTree read child ast node, not modifying children ast node, not accessing parient. Only wrap a container node.
At rendering time, no interaction between widgets are required.

@github-actions

Copy link
Copy Markdown

✅ Change Note Status

All change notes are properly formatted and validated!

📝 $:/changenotes/5.5.0/#7744

Type: feature | Category: hackability
Release: 5.5.0

Support block-level linking with anchors

🔗 #7744

👥 Contributors: linonetwo


📖 Change Note Guidelines

Change notes help track and communicate changes effectively. See the full documentation for details.

@github-actions

Copy link
Copy Markdown

📊 Build Size Comparison: empty.html

Branch Size
Base (master) 2486.7 KB
PR 2510.5 KB

Diff: ⬆️ Increase: +23.8 KB

🔴 Significant Increase

⚠️ Warning: Size increased significantly.

@linonetwo linonetwo changed the title Feat: allow link to section mark Feat: allow link to ^id anchor Feb 25, 2026
…s; fix spec comment

prettylink.js: tighten regex so [[Title|]] and [[Title^]] do not produce empty
alias/anchor captures. Change:
  (?:\|(.*?)?)?  ->  (?:\|(.*?))?
  (?:\^([^|\s^]+)?)?  ->  (?:\^([^|\s^]+))?

playwright.spec.js: update stale ..^ range syntax comment to ^start^end.

Add E2E fixture tiddlers for playwright anchor tests:
  editions/test/tiddlers/Anchor/Marks.tid
  editions/test/tiddlers/Anchor/Links.tid
  editions/test/tiddlers/Anchor/Transclusion.tid
@github-actions

Copy link
Copy Markdown

✅ Change Note Status

All change notes are properly formatted and validated!

📝 $:/changenotes/5.5.0/#7744

Type: feature | Category: hackability
Release: 5.5.0

Support block-level linking with anchors

🔗 #7744

👥 Contributors: linonetwo


📖 Change Note Guidelines

Change notes help track and communicate changes effectively. See the full documentation for details.

@github-actions

Copy link
Copy Markdown

📊 Build Size Comparison: empty.html

Branch Size
Base (master) 2486.7 KB
PR 2510.6 KB

Diff: ⬆️ Increase: +23.9 KB

🔴 Significant Increase

⚠️ Warning: Size increased significantly.

@linonetwo
linonetwo marked this pull request as ready for review March 8, 2026 16:39
@saqimtiaz saqimtiaz moved this to Backlog in Planning for v5.5.0 Jul 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

discussion Not actionable yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

10 participants