Skip to content

Aho-Corasick Freelinks Enhancement for Large Wikis and Non-Latin Titles #9084

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 63 commits into
base: master
Choose a base branch
from

Conversation

s793016
Copy link
Contributor

@s793016 s793016 commented Jun 8, 2025

This PR enhances the Freelinks plugin (plugins/tiddlywiki/freelinks/text.js) with Aho-Corasick for faster title linking, optimized for large wikis (11,714+ tiddlers) and non-Latin languages like Chinese. Developed for my 13,000-tiddler wiki, it addresses long titles and full-width symbols (e.g., , U+FF1A).

Changes

  • Replaces regex with Aho-Corasick (100-500ms vs. 1-5s for 11,714 tiddlers).
  • Chunks titles (100/chunk, 118 chunks).
  • Adds cache toggle ($:/config/Freelinks/PersistAhoCorasickCache).
  • Preserves CamelCase linking via wikilink.js.
  • Supports long titles (e.g., “雪狼湖:活動”) without splitting.

Performance

  • 11,714 tiddlers: mainRefresh 2.4-4.6s, linkification 100-500ms.
  • Import: ~60-70s, reducible to 20-30s with batching (1000 tiddlers/batch).

Testing

  • Install plugin, set $:/config/Freelinks to yes, $:/config/wikilinks to no.
  • Test tiddler “雪狼” with text “MyCamelCaseTiddler 和 雪狼湖:活動” → [[MyCamelCaseTiddler]] 和 [[雪狼湖:活動]].

Motivation

My wiki relies on Freelinks for long Chinese titles (e.g., “雪狼湖:活動”) in read-only content like student submissions. This addresses performance issues with long titles, as noted by @linonetwo, making Freelinks viable for similar wikis.

Related discussion: [link to discussion, e.g., https://github.com/Jermolene/TiddlyWiki5/discussions/XXXX]

@linonetwo @Jermolene

Replaces regex with Aho-Corasick, adds chunking (100 titles/chunk), cache toggle, and Chinese full-width symbol support. Tested with 11,714 tiddlers.
Copy link

github-actions bot commented Jun 8, 2025

@s793016 It appears that this is your first contribution to the project, welcome.

With apologies for the bureaucracy, please could you prepare a separate PR to the 'tiddlywiki-com' branch with your signature for the Contributor License Agreement (see contributing.md).

Copy link

netlify bot commented Jun 8, 2025

Deploy Preview for tiddlywiki-previews ready!

Name Link
🔨 Latest commit b254d13
🔍 Latest deploy log https://app.netlify.com/projects/tiddlywiki-previews/deploys/686d16bd6364700008eccd68
😎 Deploy Preview https://deploy-preview-9084--tiddlywiki-previews.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.


\*/
(function(){
Copy link
Contributor

Choose a reason for hiding this comment

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

On master branch we already remove, you need to let AI take latest code as reference. You can drag several js file to AI context. And don't remember to let it follow ES5 and other tw code style.

(function(){
/*jslint node: true, browser: true */
/*global $tw: false */

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry I'm a noob, I only use forums when I use github.

/*jslint node: true, browser: true */
/*global $tw: false */

deleted

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Modified it again and it's now functioning properly

}

// Aho-Corasick implementation with enhanced error handling
function AhoCorasick() {
Copy link
Contributor

Choose a reason for hiding this comment

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

refactor this class to another js, and require it in this file instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

move AhoCorasick to AhoCorasick.js ok

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Modified it again and it's now functioning properly

// Get the current tiddler title
var currentTiddlerTitle = this.getVariable("currentTiddler") || "";
// Check if persistent caching is enabled
var persistCache = self.wiki.getTiddlerText(PERSIST_CACHE_TIDDLER, "no").trim() === "yes";
Copy link
Contributor

Choose a reason for hiding this comment

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

no need to trim, like other config tiddlers.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

delete .trim ok

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Modified it again and it's now functioning properly

this.wiki.getPersistentCache(cacheKey, function() {
return computeTiddlerTitleInfo(self, ignoreCase);
}) :
this.wiki.getGlobalCache(cacheKey, function() {
Copy link
Contributor

Choose a reason for hiding this comment

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

why use both getPersistentCache and getGlobalCache?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Cache logic clarification

Keeps the two caching policies, but adds a note saying “Unified Caching Policy”
This is actually a meaningful design:

getPersistentCache: cross-session persistence, suitable for large wikis
getGlobalCache: single-session cache, suitable for general use.

Selected dynamically based on $:/config/Freelinks/PersistAhoCorasickCache configuration.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, I search the repo, there is no getPersistentCache, so this is Grok's illusion. So there is only getGlobalCache. While I hope we cound have a getPersistentCache in indexed-db, but that is for another PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, “getPersistentCache“ is not an illusion, it's an additional static caching of large data (default: off).

Persistent Cache Toggle:

Optional persistent caching of the Aho-Corasick automaton via $:/config/Freelinks/PersistAhoCorasickCache (default: no).

Enabled (yes) reduces rebuild time by ~100-200ms per refresh.

Cache invalidates on title changes, ensuring consistency.

Copy link
Member

@pmario pmario Jun 10, 2025

Choose a reason for hiding this comment

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

The global cache is very short lived. Everytime the wiki.addTiddler() function is called the global cache will be cleared. So basically whenever there is any UI interaction. Since UI interactions most to the time use state- or temp-tiddlers

this.clearGlobalCache();

Also see: freelinks.js uses globalCache object, which is destroyed most of the time #4572

titleChunks.push(titles.slice(i, i + chunkSize));
}
// Log chunking details for debugging
console.log("Total titles:", titles.length, ", Chunk size:", chunkSize, ", Number of chunks:", titleChunks.length, ", Persistent cache:", self.wiki.getTiddlerText(PERSIST_CACHE_TIDDLER, "no").trim());
Copy link
Contributor

Choose a reason for hiding this comment

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

remove logs before merging.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

remove log ok

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Modified it again and it's now functioning properly

// Add remaining text
if(currentPos < text.length) {
newParseTree.push({
type: "plain-text",
Copy link
Contributor

Choose a reason for hiding this comment

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

do we have this kind of parse tree node? I haven't encounter it before.

Copy link
Contributor Author

@s793016 s793016 Jun 8, 2025

Choose a reason for hiding this comment

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

@linonetwo Thanks for the feedback! I tried changing plain-text to text, but it caused TiddlyWiki to fail starting, likely due to parser conflicts. I've reverted to plain-text for now to ensure stability.

Copy link
Contributor

Choose a reason for hiding this comment

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

I see, now I know it!

s793016 added a commit to s793016/TiddlyWiki5 that referenced this pull request Jun 8, 2025
Signed Contributor License Agreement for my first contribution (PR TiddlyWiki#9084).
@linonetwo
Copy link
Contributor

linonetwo commented Jun 8, 2025

@s793016 Please add this plugin to tw5-com edition, so we could try it in the preview site. See https://github.com/TiddlyWiki/TiddlyWiki5/pull/8991/files#diff-c0054677a142c9f59b544336b05ce6bcf927594230e045f290e2b2eaf2a8409c for example of adding core plugins.

this.parentDomNode = parent;
this.computeAttributes();
this.execute();
this.renderChildren(parent, nextSibling);
Copy link
Member

Choose a reason for hiding this comment

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

TW uses tabs for indentation. These spaces need to be replaced.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

modified ok

text: this.getAttribute("text", this.parseTreeNode.text || "")
}];
// Only process links if not disabled and we're not within a button or link widget
if (this.getVariable("tv-wikilinks", { defaultValue: "yes" }).trim() !== "no" &&
Copy link
Member

Choose a reason for hiding this comment

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

indentation needs to be checked here too

Copy link
Contributor Author

Choose a reason for hiding this comment

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

modified ok


\*/
(function() {
Copy link
Member

Choose a reason for hiding this comment

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

This function() wrapper is not needed anymore. We removed it from the whole repo

Copy link
Contributor Author

Choose a reason for hiding this comment

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

modify ok

};

exports.text = TextNodeWidget;

})();
Copy link
Member

Choose a reason for hiding this comment

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

same here -- This belongs to the function() wrapper. Should not be needed any more

Copy link
Contributor Author

Choose a reason for hiding this comment

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

modify ok

Copy link

github-actions bot commented Jun 9, 2025

📊 Build Size Comparison: empty.html

Branch Size
Base (master) 2527.7 KB
PR 2532.2 KB

Diff: ⬆️ Increase: +4.4 KB

for TiddlyWiki freelinking functionality.

\*/
(function(){
Copy link
Contributor

Choose a reason for hiding this comment

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

We don't need this. You need to provide latest code for AI. Seems it doesn't know we already don't use this?

Also seems they don't use jsdoc for now, those /**. I like jsdoc, but currently it don't exist in the repo.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

remove ok

s793016 added a commit to s793016/TiddlyWiki5 that referenced this pull request Jun 9, 2025
Integrate text.js and aho-corasick.js into tw5-com per @linonetwo's request in PR TiddlyWiki#9084.
s793016 added a commit to s793016/TiddlyWiki5 that referenced this pull request Jun 9, 2025
Integrate text.js and aho-corasick.js into tw5-com per @linonetwo's request in PR TiddlyWiki#9084.
@s793016
Copy link
Contributor Author

s793016 commented Jun 9, 2025

@s793016 Please add this plugin to tw5-com edition, so we could try it in the preview site. See https://github.com/TiddlyWiki/TiddlyWiki5/pull/8991/files#diff-c0054677a142c9f59b544336b05ce6bcf927594230e045f290e2b2eaf2a8409c for example of adding core plugins.

add freelink into editions/tw5.com/tiddlywiki.info ok

@@ -8,6 +8,7 @@
"tiddlywiki/menubar",
"tiddlywiki/railroad",
"tiddlywiki/tour"
Copy link
Contributor

Choose a reason for hiding this comment

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

missing a comma here. Maybe you should use VSCode to edit, this kind of error will have visual warning on edit time. But anyway, just a small error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

add comma here, ok

Copy link
Contributor

@linonetwo linonetwo Jun 11, 2025

Choose a reason for hiding this comment

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

It might be editions/prerelease/tiddlywiki.info , sorry @s793016 , I'm also confused about this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

try add it to editions/prerelease for testing ok

AhoCorasick.prototype.buildFailureLinks = function() {
var queue = [];
var root = this.trie;
this.failure[root] = root;

/* 初始化根節點的直接子節點 */
Copy link
Member

Choose a reason for hiding this comment

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

Comments need to be in English

Copy link
Contributor

Choose a reason for hiding this comment

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

We need to understand it is AI generated, let him do a full check for all problems we mentioned above when feature and bugs are stable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Organize and delete comments ok

@linonetwo
Copy link
Contributor

linonetwo commented Jun 13, 2025

It looks better now, when you are done, you can restore other core plugins so we can check if there are any conflict.
图片

And don't forget to recheck the problem we mentioned before, thanks.

@s793016
Copy link
Contributor Author

s793016 commented Jun 13, 2025

@linonetwo I have completed all the requested changes and testing:

Core plugins restored: All core plugins have been restored to their original state
Conflict testing: No conflicts detected with existing core plugins
Previous issues resolved: I have double-checked and addressed all the problems mentioned earlier in the review
Functionality verified: All features are working as expected in my test environment

The PR should now be ready for final review. Please let me know if you need any additional changes or testing.

@linonetwo
Copy link
Contributor

Yes, it looks fine to me now. Wait for others.

s793016 added 2 commits June 14, 2025 15:48
The plugin supports non-Western language tiddler titles (e.g., Chinese) and prioritizes longer tiddler titles for matching, ensuring accurate linking in diverse contexts. 

Furthermore, the current tiddler title within its own content is excluded from generating links to avoid self-referencing.
@saqimtiaz
Copy link
Member

@s793016 thank you for your work on this, it looks really interesting.

A few questions:

  • I appreciate that you acknowledge the use of LLMs in this PR, thank you. Have you studied the resultant code and can you vouch for it? That would be useful to know before others peer review it.
  • Could the algorithm implementation be made reusable, and perhaps be used to improve search results?

@s793016
Copy link
Contributor Author

s793016 commented Jun 17, 2025

@saqimtiaz Thank you for your interest in this PR! I’m grateful for your questions and the opportunity to address them.

To be honest, I’m not very skilled with coding. My programming background is limited to BASIC, which I learned as a child, and even then, I wasn’t particularly good at it. The only way I can judge whether the code works is by testing the AI-generated output to see if it achieves the functionality I need—and that’s the approach I’ve taken here.

The current code successfully addresses the bugs mentioned by @linonetwo and, in my opinion, offers acceptable performance. For example, in a pure HTML environment with 15,165 tiddlers (no Node.js), each link click takes approximately 900ms, which I think is impressive.

圖片
圖片

About a month ago, I revisited a data organization project I started six years ago. In searching for the best way to present these data, I explored various wiki-like tools and eventually discovered TiddlyWiki. I found that Freelinks would be the ideal solution, but soon realized it was designed primarily for Western languages. This prompted me to frequently consult various AIs to address TiddlyWiki’s performance issues. After extensive collaboration with Grok, we initially reduced the link click response time from a slow state to 3 seconds. This led me to search the issues page, leave a comment, and eventually submit this PR.

PS: Before this, my GitHub skills were limited to using it like a forum.

Regarding the reusability of the algorithm:
Yes, it certainly has potential, but I think that would be a consideration after this PR is approved. Looking ahead, my vision is to extract the algorithm from Freelinks and integrate it into the core TiddlyWiki functionality. Freelinks could then be merged with CamelCase as a core feature, leaving the Freelinks plugin for versions below 5.3.6. Other search-related system features could also reuse this algorithm, potentially with modifications tailored to specific needs. I suspect this is why @linonetwo initially asked me to separate it.

I jokingly told Claude that the main code was produced by you two (Grok and Claude), while I just kept pestering you both all day to do this and that!

I hope this response doesn’t scare you. XD

I’m eager to learn and improve with your guidance! 😄

@saqimtiaz
Copy link
Member

@s793016 thank you for your considered and honest reply. This does indeed look like promising work.

The only consequence of relying on LLMs to this extent is that there is always a worry that there might be issues in the code that simple manual testing is unable to surface. However, personally I do not see an issue with this, beyond the need for peer review - ideally by more than one person - to thoroughly study the code and identify any potential issues, which might take a little time.

@s793016 s793016 requested a review from linonetwo July 7, 2025 08:59

//Perfect for wikis with thousands of tiddlers - stress tested and ready for action!//

!! 🙏 Credits & Thanks
Copy link
Contributor

Choose a reason for hiding this comment

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

don't need to mention AI, they are our tool (I don't use the word "slave" because they don't feel), we can just take the work as ours.

And I think this is like thanking VSCode or Windows. Nowadays I believe most programmer use AI for years, so crediting this feels like you just start using AI, which is a shame...

Copy link
Contributor

Choose a reason for hiding this comment

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

No offence, but I think it is time to 「看山不是山」

var filteredTitles = [];
for(var i = 0; i < titles.length; i++) {
var title = titles[i];
if(title && title.length > 0 && title.substring(0,3) !== "$:/") {
Copy link
Contributor

Choose a reason for hiding this comment

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

use title && !$tw.utils.isSystemTiddler(title)

Copy link
Contributor

Choose a reason for hiding this comment

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

And also move

var escapedTitle = escapeRegExp(title);
		if(escapedTitle) {

here, so don't need 2 var filteredTitles and escapedTitle

Copy link
Contributor Author

Choose a reason for hiding this comment

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

modify ok

}
}

var sortedTitles = filteredTitles.sort(function(a,b) {
Copy link
Contributor

Choose a reason for hiding this comment

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

why need to sort?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The sorting is actually important for this algorithm - it prioritizes longer titles over shorter ones to avoid partial matches. For example, if you have titles "Java" and "JavaScript", you want "JavaScript" to match first to avoid incorrectly linking just "Java" within "JavaScript".

Copy link
Contributor

Choose a reason for hiding this comment

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

I see, then this is good.

@linonetwo
Copy link
Contributor

linonetwo commented Jul 7, 2025

It bacically OK, just some improvments. I read the code, core part is in computeTiddlerTitleInfo. Other places are just TW boilerplates, don't have something wrong now.

@s793016 Do you know you can already drag the plugin from https://deploy-preview-9084--tiddlywiki-previews.netlify.app/ to you wiki so you can starting using it in your wiki?

Copy link
Member

@pmario pmario left a comment

Choose a reason for hiding this comment

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

I did review the code. It looks OK so far.
I did not run a single-step debug session yet to have a closer look at the aho-corasick trie structure yet. I intend to do that.

}

if(processedNodes >= maxNodes) {
throw new Error("Aho-Corasick: buildFailureLinks exceeded maximum nodes (" + maxNodes + ")");
Copy link
Member

Choose a reason for hiding this comment

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

There is the question if we want to "throw" an RSOD error here, or if we can fail more silently.
IMO the wiki is not broken -- the index has a problem. -- I'm not sure

We will need a translatable string here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a valid concern. Instead of throwing a hard error that could crash the application, you might want to log a warning and gracefully degrade functionality. The error message should indeed be translatable for internationalization.

For the discussion points: These need more consideration and discussion before implementing changes, as they involve functionality and internationalization concerns.

var afterChar = end < text.length ? text[end] : '';

var isWordChar = function(char) {
return /[a-zA-Z0-9_]/.test(char);
Copy link
Member

Choose a reason for hiding this comment

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

Is this test good enough for "non Latin" characters

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Regarding the regex /[a-zA-Z0-9_]/ for non-Latin characters:
The current regex is actually sufficient for the intended use case. Here's why:

For CJK languages (Chinese, Japanese, Korean): These languages don't have word boundaries in the traditional sense. CJK characters will return false when tested against /[a-zA-Z0-9_]/, which means they're treated as non-word characters. This actually works perfectly for mixed-language text (e.g., English words within Chinese text).
The word boundary logic works correctly:

When searching for "cat" in "我有cat貓" → both before and after characters are non-word chars → match succeeds
When searching for "cat" in "category" → after character is a word char → match fails

For languages that do use spaces: The current regex covers the primary use cases (Latin alphabet + numbers + underscore).
The main limitation would be European languages with accented characters (é, ñ, ü, etc.), but expanding to /[\w]/u might be overkill for TiddlyWiki's freelinking needs.

The current implementation strikes a good balance between functionality and simplicity for the majority of use cases.

Copy link
Contributor Author

@s793016 s793016 Jul 8, 2025

Choose a reason for hiding this comment

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

After discussion, here's the analysis:

Current regex /[a-zA-Z0-9_]/ works well because:

For space-using languages (English, Russian, Arabic, Hebrew, Greek, etc.): Spaces return false and act as word boundaries correctly - the specific script doesn't matter.

For non-space languages (Chinese, Japanese, Thai, etc.): These characters return false and are treated as non-word characters, which is the correct behavior.

The regex only needs to identify "word characters" - everything else becomes a boundary by default.

Suggested improvement: Extend to Latin-1 (ASCII < 255) to support European accented characters:

var isWordChar = function(char) {
    return /[a-zA-Z0-9_\u00C0-\u00FF]/.test(char);
};

This single line change would add support for:

Accented letters: á, é, í, ó, ú, à, è, ì, ò, ù, ä, ë, ï, ö, ü, ñ, ç, etc.

Most Western European languages (Spanish, French, German, Italian, Portuguese, etc.)

The fundamental approach is sound - we just need this small enhancement for better European language support.


var sortedTitles = filteredTitles.sort(function(a,b) {
var lenDiff = b.length - a.length;
return lenDiff !== 0 ? lenDiff : a.localeCompare(b, 'zh', {sensitivity: 'base'});
Copy link
Member

Choose a reason for hiding this comment

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

The a.localeCompare(b, "zh") ... is language specific. So we probably need to make it configurable. -- Investigation with eg: Greek and / or Japanese language needed.

TW strings need double quotes "zh"

Copy link
Contributor Author

@s793016 s793016 Jul 8, 2025

Choose a reason for hiding this comment

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

Looking at the code more carefully, the sorting is primarily for length-based prioritization to avoid partial matches. The sorting logic is:

var sortedTitles = filteredTitles.sort(function(a,b) {
	var lenDiff = b.length - a.length;
	return lenDiff !== 0 ? lenDiff : a.localeCompare(b, 'zh', {sensitivity: 'base'});
});

The primary purpose is length-based sorting (longer titles first), not language-specific sorting.

So we don't need to specify 'zh' at all! The locale-specific sorting is just for tie-breaking when titles have the same length, and the default locale comparison should work fine.

The main purpose of sorting is length-based prioritization, and the secondary alphabetical sorting is just for consistency when titles have the same length. We don't need language-specific sorting at all.

s793016 and others added 8 commits July 8, 2025 16:42
Added locale configuration support - Added LOCALE_CONFIG_TIDDLER constant to make the sorting locale configurable instead of hardcoded "zh"
Optimized title processing - Combined the filtering and escaping logic into a single pass to reduce duplication
Added trim() for ignoreCase - Applied .trim() to the ignore case variable for consistency
Enhanced refresh logic - Added locale configuration tiddler to the refresh check
Improved comments - Added explanation for why sorting is necessary (prioritizing longer titles)
we don't need to specify 'zh' at all
This single line change would add support for:

Accented letters: á, é, í, ó, ú, à, è, ì, ò, ù, ä, ë, ï, ö, ü, ñ, ç, etc.
Most Western European languages (Spanish, French, German, Italian, Portuguese, etc.)
@s793016
Copy link
Contributor Author

s793016 commented Jul 8, 2025

@s793016 Do you know you can already drag the plugin from https://deploy-preview-9084--tiddlywiki-previews.netlify.app/ to you wiki so you can starting using it in your wiki?

I previously edited the main page to add [[$:/plugins/tiddlywiki/freelinks]], and then dragged it to install. Was that the right way?

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.

4 participants