From a3acbaa2f16f59e37893ee3a553117b08aa3a35b Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Fri, 20 Feb 2026 09:30:25 +0000 Subject: [PATCH 01/28] Update road map --- editions/tw5.com/tiddlers/roadmap/RoadMap.tid | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/editions/tw5.com/tiddlers/roadmap/RoadMap.tid b/editions/tw5.com/tiddlers/roadmap/RoadMap.tid index e038aeded..03c70972f 100644 --- a/editions/tw5.com/tiddlers/roadmap/RoadMap.tid +++ b/editions/tw5.com/tiddlers/roadmap/RoadMap.tid @@ -1,10 +1,14 @@ created: 20130823203800000 -modified: 2020051619421803 +modified: 20260220093010752 tags: About title: RoadMap type: text/vnd.tiddlywiki -TiddlyWiki 5 is now a mature, stable project that is relied upon by many people. Simultaneously, it is rapidly evolving in many directions thanks to the broad community of developers and users. This paradoxical situation is possible because the project strictly maintains backwards compatibility, adding new features alongside the existing ones. +TiddlyWiki was first released in 2013, 9 years after the original [[TiddlyWiki Classic]]. Since then there have been 41 releases, making TiddlyWiki steadily more flexible and reliable. We have ambitious plans for the future as well: v5.4.x, v5.5.x, and eventually TWXX. -There is no formal roadmap, but quite a few areas that have yet to be fully implemented, such as search and replace, and rich text editing. Current work can be found on ~GitHub at https://github.com/TiddlyWiki/TiddlyWiki5/ +Our version numbering follows the form v5.x.0. ~TiddlyWiki is currently on v<>, and so we are currently working towards v5.4.0. +There is a detailed project plan on GitHub, but the major milestones are planned as follows: + +* TiddlyWiki v5.4.0 will be followed by further v5.4.x releases to fix bugs and incrementally improve features. If there are significant backwards compatibility concerns then those improvements will be pushed into v5.5.0, the next release to make minor backwards comatibility tweaks. +* We'll go on to make v5.6.0, v5.7.0 etc releases until we're ready to release the next major redesign, that is not compromised by backwards compatibility. We are calling this release ~TiddlyWiki XX ("~TiddlyWiki Twenty", or TWXX). From 7a866b93e3be98dcd7747f60ad0e224eaa464219 Mon Sep 17 00:00:00 2001 From: Saq Imtiaz Date: Fri, 27 Feb 2026 18:53:58 +0100 Subject: [PATCH 02/28] fix: alt support for images in markdown (#9700) --- plugins/tiddlywiki/markdown/markdown-it-tiddlywiki.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/tiddlywiki/markdown/markdown-it-tiddlywiki.js b/plugins/tiddlywiki/markdown/markdown-it-tiddlywiki.js index 92a650529..9e180a9ed 100644 --- a/plugins/tiddlywiki/markdown/markdown-it-tiddlywiki.js +++ b/plugins/tiddlywiki/markdown/markdown-it-tiddlywiki.js @@ -420,7 +420,7 @@ function extendInlineParse(thisArg,origFunc,twInlineRules) { /// post processing /// function wikify(state) { - var href, title, src; + var href, title, src, alt; var tagStack = []; state.tokens.forEach(function(blockToken) { @@ -458,6 +458,7 @@ function wikify(state) { token.tag = "$image"; src = token.attrGet("src"); alt = token.attrGet("alt"); + token.attrSet("alt",alt); title = token.attrGet("title"); token.attrs[token.attrIndex("src")][0] = "source"; From 48cff401b2c2bfc967b6d0f1ca7317bf2687378e Mon Sep 17 00:00:00 2001 From: btheado Date: Sun, 1 Mar 2026 01:58:59 -0500 Subject: [PATCH 03/28] Add tests/examples for transclusion shortcut syntax (#9705) --- .../tiddlers/procedures/calls/Calls.tid | 8 +++- .../Calls/ProcedureDynamicAttributes.tid | 40 ++++++++++++++++ .../Calls/ProcedureStaticAttributes.tid | 48 +++++++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 editions/tw5.com/tiddlers/testcases/Calls/ProcedureDynamicAttributes.tid create mode 100644 editions/tw5.com/tiddlers/testcases/Calls/ProcedureStaticAttributes.tid diff --git a/editions/tw5.com/tiddlers/procedures/calls/Calls.tid b/editions/tw5.com/tiddlers/procedures/calls/Calls.tid index 48822120a..5e07f861e 100644 --- a/editions/tw5.com/tiddlers/procedures/calls/Calls.tid +++ b/editions/tw5.com/tiddlers/procedures/calls/Calls.tid @@ -1,6 +1,6 @@ caption: Calls created: 20221007130006705 -modified: 20260125212303316 +modified: 20260301030947969 tags: WikiText Procedures Functions Macros title: Calls type: text/vnd.tiddlywiki @@ -25,6 +25,12 @@ Each parameter value can be enclosed in `'`single quotes`'`, `"`double quotes`"` See the discussion about [[parser modes|WikiText parser mode: macro examples]] +!!! Examples + +<> + +<> + !! Calls with <<.wlink TranscludeWidget>> Widget The shortcut syntax expands to the <<.wlink TranscludeWidget>> widget with the `$variable` attribute specifying the name of the procedure to transclude. diff --git a/editions/tw5.com/tiddlers/testcases/Calls/ProcedureDynamicAttributes.tid b/editions/tw5.com/tiddlers/testcases/Calls/ProcedureDynamicAttributes.tid new file mode 100644 index 000000000..b57ebce6d --- /dev/null +++ b/editions/tw5.com/tiddlers/testcases/Calls/ProcedureDynamicAttributes.tid @@ -0,0 +1,40 @@ +created: 20260301005951330 +description: Procedure calls with dynamic attributes +modified: 20260301013907635 +tags: $:/tags/wiki-test-spec +title: TestCases/Calls/ProcedureDynamicAttributes +type: text/vnd.tiddlywiki-multiple + +title: Narrative + +New-style equals sign named parameters support dynamic values ++ +title: Output + +\function f(a) foo [] :and[join[ ]] +\procedure p(a1) <$text text=<>/> + +<$let v="foo 1" v2="foo 3"> +<

>>> - +<

>>> - +<

> - +<

> - +<

> - +<

> + ++ +title: my tiddler +myfield: foo 5 + +foo 4 ++ +title: ExpectedResult + +

+foo 1 - +foo 2 - +foo 3 - +foo 4 - +foo 5 - +foo 6 +

\ No newline at end of file diff --git a/editions/tw5.com/tiddlers/testcases/Calls/ProcedureStaticAttributes.tid b/editions/tw5.com/tiddlers/testcases/Calls/ProcedureStaticAttributes.tid new file mode 100644 index 000000000..c2c1be2e3 --- /dev/null +++ b/editions/tw5.com/tiddlers/testcases/Calls/ProcedureStaticAttributes.tid @@ -0,0 +1,48 @@ +created: 20260301000418824 +description: Procedure calls with static attributes +modified: 20260301031128810 +tags: $:/tags/wiki-test-spec +title: TestCases/Calls/ProcedureStaticAttributes +type: text/vnd.tiddlywiki-multiple + +title: Narrative + +Positional, old style colon named parameters, and new-style equals sign named parameters support static values ++ +title: Output + +\procedure p(a1) <$text text=<>/> + +<

> - +<

> - +<

> + +<

> - +<

> - +<

> + +<

> - +<

> - +<

> + +<

> - +<

> - +<

> + +<

> - +<

> - +<

> ++ +title: ExpectedResult + +

foo1 - +foo2 - +foo3

foo 4 - +foo 5 - +foo 6

foo 7 - +foo 8 - +foo 9

foo 10 - +foo 11 - +foo 12

foo 13 - +foo 14 - +foo 15

\ No newline at end of file From 9d5b5111d00a33bcd195f54358d92ff8df18700a Mon Sep 17 00:00:00 2001 From: Saq Imtiaz Date: Sun, 1 Mar 2026 08:02:18 +0100 Subject: [PATCH 04/28] Revert "Add locale support for sort operator (#9400)" (#9702) This reverts commit 98a61f01bb68a0261f61f8b32c26f2430e058f83. --- core/modules/filters/sort.js | 10 ++-- core/modules/wiki.js | 47 +++++++++++++++---- editions/tw5.com/tiddlers/Locale Example.tid | 6 --- .../tiddlers/filters/examples/sort.tid | 9 +--- editions/tw5.com/tiddlers/filters/nsort.tid | 16 +++---- editions/tw5.com/tiddlers/filters/nsortcs.tid | 4 +- editions/tw5.com/tiddlers/filters/sort.tid | 22 ++++----- editions/tw5.com/tiddlers/filters/sortan.tid | 4 +- editions/tw5.com/tiddlers/filters/sortcs.tid | 4 +- .../releasenotes/5.4.0/#9400 - collator.tid | 9 ---- .../tiddlers/releasenotes/5.4.0/#9400.tid | 10 ---- 11 files changed, 65 insertions(+), 76 deletions(-) delete mode 100644 editions/tw5.com/tiddlers/Locale Example.tid delete mode 100644 editions/tw5.com/tiddlers/releasenotes/5.4.0/#9400 - collator.tid delete mode 100644 editions/tw5.com/tiddlers/releasenotes/5.4.0/#9400.tid diff --git a/core/modules/filters/sort.js b/core/modules/filters/sort.js index 9393df749..cdfe79728 100644 --- a/core/modules/filters/sort.js +++ b/core/modules/filters/sort.js @@ -14,31 +14,31 @@ Export our filter function */ exports.sort = function(source,operator,options) { var results = prepare_results(source); - options.wiki.sortTiddlers(results,operator.operands[0] || "title",operator.prefix === "!",false,false,undefined,operator.operands[1]); + options.wiki.sortTiddlers(results,operator.operand || "title",operator.prefix === "!",false,false); return results; }; exports.nsort = function(source,operator,options) { var results = prepare_results(source); - options.wiki.sortTiddlers(results,operator.operands[0] || "title",operator.prefix === "!",false,true,undefined,operator.operands[1]); + options.wiki.sortTiddlers(results,operator.operand || "title",operator.prefix === "!",false,true); return results; }; exports.sortan = function(source, operator, options) { var results = prepare_results(source); - options.wiki.sortTiddlers(results, operator.operands[0] || "title", operator.prefix === "!",false,false,true,operator.operands[1]); + options.wiki.sortTiddlers(results, operator.operand || "title", operator.prefix === "!",false,false,true); return results; }; exports.sortcs = function(source,operator,options) { var results = prepare_results(source); - options.wiki.sortTiddlers(results,operator.operands[0] || "title",operator.prefix === "!",true,false,undefined,operator.operands[1]); + options.wiki.sortTiddlers(results,operator.operand || "title",operator.prefix === "!",true,false); return results; }; exports.nsortcs = function(source,operator,options) { var results = prepare_results(source); - options.wiki.sortTiddlers(results,operator.operands[0] || "title",operator.prefix === "!",true,true,undefined,operator.operands[1]); + options.wiki.sortTiddlers(results,operator.operand || "title",operator.prefix === "!",true,true); return results; }; diff --git a/core/modules/wiki.js b/core/modules/wiki.js index 19d90e1c7..aefe65110 100755 --- a/core/modules/wiki.js +++ b/core/modules/wiki.js @@ -369,16 +369,31 @@ Sort an array of tiddler titles by a specified field isDescending: true if the sort should be descending isCaseSensitive: true if the sort should consider upper and lower case letters to be different */ -exports.sortTiddlers = function(titles,sortField,isDescending,isCaseSensitive,isNumeric,isAlphaNumeric,locale) { +exports.sortTiddlers = function(titles,sortField,isDescending,isCaseSensitive,isNumeric,isAlphaNumeric) { var self = this; if(sortField === "title") { if(!isNumeric && !isAlphaNumeric) { - const sorter = new Intl.Collator(locale, { sensitivity: isCaseSensitive ? "variant" : "accent" }); - if(isDescending) { - titles.sort((a,b) => sorter.compare(b, a)); + if(isCaseSensitive) { + if(isDescending) { + titles.sort(function(a,b) { + return b.localeCompare(a); + }); + } else { + titles.sort(function(a,b) { + return a.localeCompare(b); + }); + } } else { - titles.sort((a,b) => sorter.compare(a, b)); - } + if(isDescending) { + titles.sort(function(a,b) { + return b.toLowerCase().localeCompare(a.toLowerCase()); + }); + } else { + titles.sort(function(a,b) { + return a.toLowerCase().localeCompare(b.toLowerCase()); + }); + } + } } else { titles.sort(function(a,b) { var x,y; @@ -399,8 +414,14 @@ exports.sortTiddlers = function(titles,sortField,isDescending,isCaseSensitive,is } } } - const sorter = new Intl.Collator(locale, { numeric: isAlphaNumeric, sensitivity: isAlphaNumeric ? "base" : isCaseSensitive ? "variant" : "accent" }); - return isDescending ? sorter.compare(b, a) : sorter.compare(a, b); + if(isAlphaNumeric) { + return isDescending ? b.localeCompare(a,undefined,{numeric: true,sensitivity: "base"}) : a.localeCompare(b,undefined,{numeric: true,sensitivity: "base"}); + } + if(!isCaseSensitive) { + a = a.toLowerCase(); + b = b.toLowerCase(); + } + return isDescending ? b.localeCompare(a) : a.localeCompare(b); }); } } else { @@ -442,8 +463,14 @@ exports.sortTiddlers = function(titles,sortField,isDescending,isCaseSensitive,is } a = String(a); b = String(b); - const sorter = new Intl.Collator(locale, { numeric: isAlphaNumeric, sensitivity: isAlphaNumeric ? "base" : isCaseSensitive ? "variant" : "accent" }); - return isDescending ? sorter.compare(b, a) : sorter.compare(a, b); + if(isAlphaNumeric) { + return isDescending ? b.localeCompare(a,undefined,{numeric: true,sensitivity: "base"}) : a.localeCompare(b,undefined,{numeric: true,sensitivity: "base"}); + } + if(!isCaseSensitive) { + a = a.toLowerCase(); + b = b.toLowerCase(); + } + return isDescending ? b.localeCompare(a) : a.localeCompare(b); }); } }; diff --git a/editions/tw5.com/tiddlers/Locale Example.tid b/editions/tw5.com/tiddlers/Locale Example.tid deleted file mode 100644 index 5d7898008..000000000 --- a/editions/tw5.com/tiddlers/Locale Example.tid +++ /dev/null @@ -1,6 +0,0 @@ -created: 20251001034405510 -list: A a Ä ä Z z O o Õ õ Ö ö Ü ü Y y -modified: 20251218023544134 -tags: -title: Locale Example -type: text/vnd.tiddlywiki \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/filters/examples/sort.tid b/editions/tw5.com/tiddlers/filters/examples/sort.tid index a0bdd36bc..e6edf8ecf 100644 --- a/editions/tw5.com/tiddlers/filters/examples/sort.tid +++ b/editions/tw5.com/tiddlers/filters/examples/sort.tid @@ -1,5 +1,5 @@ created: 20150124112340000 -modified: 20251218023608468 +modified: 20150124113250000 tags: [[sort Operator]] [[Operator Examples]] title: sort Operator (Examples) type: text/vnd.tiddlywiki @@ -11,10 +11,3 @@ type: text/vnd.tiddlywiki <<.operator-example 3 "one two Three four +[sort[]]">> <<.operator-example 4 "[prefix[Tiddl]sort[text]]">> <<.operator-example 5 "[has[created]sort[created]limit[10]]" "the oldest 10 tiddlers in the wiki">> - -! Using a custom locale -The following examples shows the differences when using the sort operator with default, Swedish and Estonian locale. - -<<.operator-example 6 "[list[Locale Example]sort[]]">> -<<.operator-example 7 "[list[Locale Example]sort[],[sv]]">> -<<.operator-example 8 "[list[Locale Example]sort[],[et]]">> diff --git a/editions/tw5.com/tiddlers/filters/nsort.tid b/editions/tw5.com/tiddlers/filters/nsort.tid index 8ae6c1b69..ab73c592e 100644 --- a/editions/tw5.com/tiddlers/filters/nsort.tid +++ b/editions/tw5.com/tiddlers/filters/nsort.tid @@ -1,15 +1,15 @@ -caption: nsort created: 20140410103123179 -modified: 20251227084602319 -op-input: a [[selection of titles|Title Selection]] -op-neg-output: the input, likewise sorted into descending order -op-output: the input, sorted into ascending order by field <<.field F>>, treating field values as numbers -op-parameter: accept same parameters as the [[sort Operator]] -op-parameter-name: F -op-purpose: sort the input by number field +modified: 20150203190051000 tags: [[Filter Operators]] [[Field Operators]] [[Order Operators]] [[Negatable Operators]] title: nsort Operator type: text/vnd.tiddlywiki +caption: nsort +op-purpose: sort the input by number field +op-input: a [[selection of titles|Title Selection]] +op-parameter: the name of a [[field|TiddlerFields]], defaulting to <<.field title>> +op-parameter-name: F +op-output: the input, sorted into ascending order by field <<.field F>>, treating field values as numbers +op-neg-output: the input, likewise sorted into descending order Non-numeric values are treated as having a higher value than any number, and the difference between capital and lowercase letters is ignored. Compare <<.olink nsortcs>>. diff --git a/editions/tw5.com/tiddlers/filters/nsortcs.tid b/editions/tw5.com/tiddlers/filters/nsortcs.tid index 746356037..c878db2a5 100644 --- a/editions/tw5.com/tiddlers/filters/nsortcs.tid +++ b/editions/tw5.com/tiddlers/filters/nsortcs.tid @@ -1,10 +1,10 @@ caption: nsortcs created: 20140410103123179 -modified: 20251227084615705 +modified: 20150417125717078 op-input: a [[selection of titles|Title Selection]] op-neg-output: the input, likewise sorted into descending order op-output: the input, sorted into ascending order by field <<.place F>>, treating field values as numbers -op-parameter: accept same parameters as the [[sort Operator]] +op-parameter: the name of a [[field|TiddlerFields]], defaulting to <<.field title>> op-parameter-name: F op-purpose: sort the input titles by number field, treating upper and lower case as different tags: [[Filter Operators]] [[Field Operators]] [[Order Operators]] [[Negatable Operators]] diff --git a/editions/tw5.com/tiddlers/filters/sort.tid b/editions/tw5.com/tiddlers/filters/sort.tid index bace0dd00..b20dc5430 100644 --- a/editions/tw5.com/tiddlers/filters/sort.tid +++ b/editions/tw5.com/tiddlers/filters/sort.tid @@ -1,22 +1,16 @@ -caption: sort created: 20140410103123179 -modified: 20251227084644651 -op-input: a [[selection of titles|Title Selection]] -op-neg-output: the input, likewise sorted into descending order -op-output: the input, sorted into ascending order by field <<.field F>>, treating field values as text -op-parameter: the <<.op sort>> operator accepts 1 or 2 parameters, see below for details -op-parameter-name: F -op-purpose: sort the input by text field +modified: 20150203191228000 tags: [[Filter Operators]] [[Common Operators]] [[Field Operators]] [[Order Operators]] [[Negatable Operators]] title: sort Operator type: text/vnd.tiddlywiki +caption: sort +op-purpose: sort the input by text field +op-input: a [[selection of titles|Title Selection]] +op-parameter: the name of a [[field|TiddlerFields]], defaulting to <<.field title>> +op-parameter-name: F +op-output: the input, sorted into ascending order by field <<.field F>>, treating field values as text +op-neg-output: the input, likewise sorted into descending order The difference between capital and lowercase letters is ignored. Compare <<.olink sortcs>>. -``` -[sort[],[]] -``` -* ''field'' : the name of a [[field|TiddlerFields]], defaulting to <<.field title>> -* ''locale'': (optional). A string with a [[BCP 47 language tag|https://developer.mozilla.org/en-US/docs/Glossary/BCP_47_language_tag]] - <<.operator-examples "sort">> diff --git a/editions/tw5.com/tiddlers/filters/sortan.tid b/editions/tw5.com/tiddlers/filters/sortan.tid index 8502ba44e..b6674c75b 100644 --- a/editions/tw5.com/tiddlers/filters/sortan.tid +++ b/editions/tw5.com/tiddlers/filters/sortan.tid @@ -1,10 +1,10 @@ caption: sortan created: 20180222071605739 -modified: 20251227084439804 +modified: 20180223012553446 op-input: a [[selection of titles|Title Selection]] op-neg-output: the input, likewise sorted into descending order op-output: the input, sorted into ascending order by field <<.field F>>, treating field values as alphanumerics -op-parameter: accept same parameters as the [[sort Operator]] +op-parameter: the name of a [[field|TiddlerFields]], defaulting to <<.field title>> op-parameter-name: F op-purpose: sort the input by text field considering them as alphanumerics tags: [[Filter Operators]] [[Common Operators]] [[Field Operators]] [[Order Operators]] [[Negatable Operators]] diff --git a/editions/tw5.com/tiddlers/filters/sortcs.tid b/editions/tw5.com/tiddlers/filters/sortcs.tid index a67535e7d..bcdff751a 100644 --- a/editions/tw5.com/tiddlers/filters/sortcs.tid +++ b/editions/tw5.com/tiddlers/filters/sortcs.tid @@ -1,10 +1,10 @@ caption: sortcs created: 20140410103123179 -modified: 20251227084416522 +modified: 20150417125704503 op-input: a [[selection of titles|Title Selection]] op-neg-output: the input, likewise sorted into descending order op-output: the input, sorted into ascending order by field <<.field F>>, treating field values as text -op-parameter: accept same parameters as the [[sort Operator]] +op-parameter: the name of a [[field|TiddlerFields]], defaulting to <<.field title>> op-parameter-name: F op-purpose: sort the input by text field, treating upper and lower case as different tags: [[Filter Operators]] [[Field Operators]] [[Order Operators]] [[Negatable Operators]] diff --git a/editions/tw5.com/tiddlers/releasenotes/5.4.0/#9400 - collator.tid b/editions/tw5.com/tiddlers/releasenotes/5.4.0/#9400 - collator.tid deleted file mode 100644 index 7f28d68fd..000000000 --- a/editions/tw5.com/tiddlers/releasenotes/5.4.0/#9400 - collator.tid +++ /dev/null @@ -1,9 +0,0 @@ -title: $:/changenotes/5.4.0/#9400/impacts/collator -changenote: $:/changenotes/5.4.0/#9400 -created: 20251114102355243 -modified: 20251114102355243 -tags: $:/tags/ImpactNote -description: Tiddlywiki no longer works on browsers that doesn't support [[Intl.Collator|https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator]] -impact-type: compatibility-break - - diff --git a/editions/tw5.com/tiddlers/releasenotes/5.4.0/#9400.tid b/editions/tw5.com/tiddlers/releasenotes/5.4.0/#9400.tid deleted file mode 100644 index 674fb1e19..000000000 --- a/editions/tw5.com/tiddlers/releasenotes/5.4.0/#9400.tid +++ /dev/null @@ -1,10 +0,0 @@ -title: $:/changenotes/5.4.0/#9400 -description: Add locale support for sort operators -release: 5.4.0 -tags: $:/tags/ChangeNote -change-type: feature -change-category: filters -github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9400 -github-contributors: Leilei332 - -Add a new parameter for `sort`, `nsort`, `sortan`, `sortcs` and `nsortcs` to support sorting with a custom locale. \ No newline at end of file From 02b4c04a56f93b9d648dbc17bf05ddb67c9a3274 Mon Sep 17 00:00:00 2001 From: Cameron Fischer Date: Tue, 3 Mar 2026 03:23:27 -0500 Subject: [PATCH 05/28] Release notes for PRs I've made, as requested (#9707) * Release notes for PRs I've made, as requested * Update #9297.tid blog -> blob --- .../tw5.com/tiddlers/releasenotes/5.4.0/#9280.tid | 10 ++++++++++ .../tw5.com/tiddlers/releasenotes/5.4.0/#9297.tid | 12 ++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 editions/tw5.com/tiddlers/releasenotes/5.4.0/#9280.tid create mode 100644 editions/tw5.com/tiddlers/releasenotes/5.4.0/#9297.tid diff --git a/editions/tw5.com/tiddlers/releasenotes/5.4.0/#9280.tid b/editions/tw5.com/tiddlers/releasenotes/5.4.0/#9280.tid new file mode 100644 index 000000000..dfed20ce1 --- /dev/null +++ b/editions/tw5.com/tiddlers/releasenotes/5.4.0/#9280.tid @@ -0,0 +1,10 @@ +title: $:/changenotes/5.4.0/#9280 +description: Fixed :reverse suffix being ignored by version sort filter +tags: $:/tags/ChangeNote +release: 5.4.0 +change-type: bugfix +change-category: filters +github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9280 +github-contributors: Flibbles + +When using the version suffix with the sort filter, the reverse flag had no effect. Fixed. diff --git a/editions/tw5.com/tiddlers/releasenotes/5.4.0/#9297.tid b/editions/tw5.com/tiddlers/releasenotes/5.4.0/#9297.tid new file mode 100644 index 000000000..fe7b19528 --- /dev/null +++ b/editions/tw5.com/tiddlers/releasenotes/5.4.0/#9297.tid @@ -0,0 +1,12 @@ +title: $:/changenotes/5.4.0/#9297 +description: Better support for downloading binary image tiddlers +tags: $:/tags/ChangeNote +release: 5.4.0 +change-type: enhancement +change-category: usability +github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9297 +github-contributors: Flibbles + +TiddlyWiki used to convert to blob all tiddlers intended to be downloaded, which is efficient, but interfered with a browser's ability to download binary image data in a way that it recognized. + +This isn't necessarily the best solution, but it makes image downloading possible in TiddlyWiki until we require a more sufficient solution. From 111a168b0caf330ee53d5b8dc14ab14c908d9db7 Mon Sep 17 00:00:00 2001 From: Saq Imtiaz Date: Fri, 6 Mar 2026 23:47:35 +0100 Subject: [PATCH 06/28] feat: added community card for saqimtiaz (#9701) --- community/people/SaqImtiaz.tid | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 community/people/SaqImtiaz.tid diff --git a/community/people/SaqImtiaz.tid b/community/people/SaqImtiaz.tid new file mode 100644 index 000000000..3b0c12f27 --- /dev/null +++ b/community/people/SaqImtiaz.tid @@ -0,0 +1,16 @@ +title: @saqimtiaz +tags: Community/Person +fullname: Saq Imtiaz +first-sighting: 2006-02-25 +talk.tiddlywiki.org: saqimtiaz +github: saqimtiaz +email: saq.imtiaz@gmail.com +avatar: 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 + +I am a Project Maintainer for TiddlyWiki 5. I have a background as a UX designer, developer, doctor, photographer, and a non-profit educational sector jack of all trades, plus a few other things I often forget. + +I first discovered TiddlyWiki in early 2005, and fully embraced it in 2006 while searching for a practical way to prototype software solutions for the educational non-profit I was working with at the time. What began as a tool quickly became a teacher. +Through TiddlyWiki, I learned how to code, earned my chops in JavaScript and frontend development, and grew into technical leadership and project lead roles. I later formalized this experience with a Master’s degree in IT, with a focus on user experience design. + +My work with TiddlyWiki today centers on using it as an application prototyping and development platform, with a particular focus on its true offline-first capabilities and its strength as a tool for thinking, building, and iterating without friction. + From 846deb30392f00e1bb38db990477649a9791dfbe Mon Sep 17 00:00:00 2001 From: Mario Pietsch Date: Mon, 9 Mar 2026 18:09:07 +0100 Subject: [PATCH 07/28] Git Ignore Claude AI local settings (#9719) --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 412759161..74f14dd11 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .c9/ .vs/ .vscode/ +.claude/ tmp/ output/ node_modules/ From 8773a0433ceb9facf26e41f066ed84cd7e68f300 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Wed, 11 Mar 2026 12:22:14 +0000 Subject: [PATCH 08/28] Dynaview: Fix RSOE with Update address bar while scrolling See https://talk.tiddlywiki.org/t/your-help-is-needed-to-test-v5-4-0/14945/52 --- plugins/tiddlywiki/dynaview/dynaview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tiddlywiki/dynaview/dynaview.js b/plugins/tiddlywiki/dynaview/dynaview.js index 2f8af31f5..af501cd61 100644 --- a/plugins/tiddlywiki/dynaview/dynaview.js +++ b/plugins/tiddlywiki/dynaview/dynaview.js @@ -155,7 +155,7 @@ function updateAddressBar() { var top = findTopmostTiddler(); if(top.element) { var hash = "#" + encodeURIComponent(top.title) + ":" + encodeURIComponent("[list[$:/StoryList]]"); - if(title && $tw.locationHash !== hash) { + if(top.title && $tw.locationHash !== hash) { $tw.locationHash = hash; window.location.hash = hash; } From a61331e6ed93a02fbbb9e15eb24ec23a81ccf9c0 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Wed, 11 Mar 2026 13:50:37 +0000 Subject: [PATCH 09/28] Fix readme for tw5.com-docs plugin Fixes #9721 --- plugins/tiddlywiki/tw5.com-docs/readme.tid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tiddlywiki/tw5.com-docs/readme.tid b/plugins/tiddlywiki/tw5.com-docs/readme.tid index 044ac24c0..0858c35c1 100644 --- a/plugins/tiddlywiki/tw5.com-docs/readme.tid +++ b/plugins/tiddlywiki/tw5.com-docs/readme.tid @@ -1,4 +1,4 @@ -title $:/plugins/tiddlywiki/tw5.com-docs/readme +title: $:/plugins/tiddlywiki/tw5.com-docs/readme This is an experimental packaging of the documentation from tiddlywiki.com into a plugin. From 3983086b96628704d81413a43c29883ecdd28a1c Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Wed, 11 Mar 2026 22:22:49 +0800 Subject: [PATCH 10/28] Document alternating table row styling (#9713) * Document alternating table row styling Resolves issue #8738 (and explains why my old wiki row styles were swapped). * Make note more prominent Use note macro for note Co-authored-by: Mario Pietsch --------- Co-authored-by: Mario Pietsch --- .../Tables in WikiText CSS Utility Classes.tid | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/editions/tw5.com/tiddlers/wikitext/Tables in WikiText CSS Utility Classes.tid b/editions/tw5.com/tiddlers/wikitext/Tables in WikiText CSS Utility Classes.tid index 14abc1c86..945de5151 100644 --- a/editions/tw5.com/tiddlers/wikitext/Tables in WikiText CSS Utility Classes.tid +++ b/editions/tw5.com/tiddlers/wikitext/Tables in WikiText CSS Utility Classes.tid @@ -79,3 +79,14 @@ The following is a table with maximum width. It contains [[TextWidget]]s with ma | Cell1|<$edit-text tiddler="$:/temp/test-table-input" tag="input" field="test"/> | |^ [[Link to a tiddler]]
some more text|<$edit-text tiddler="$:/temp/test-table-input" field="text"/> | """>> + +!! Table with Alternating Row Styles + +You will need to create your own stylesheet. See [[Using Stylesheets]]. The idea is to create rules using `:nth-of-type(even/odd)`. For example: + +``` +.myclass tbody tr:nth-of-type(even) { background-color: <>; } +.myclass tbody tr:nth-of-type(odd) { background-color: <>; } +``` + +<<.note """~TiddlyWiki automatically applies classes `evenRow` and `oddRow` to table rows. However, use of these classes is not recommended. Rows start as 'even' (opposite to that expected) and all the rows of the table are considered to be a single combined set of rows, regardless of if they appear in the header, footer, or body.""">> From fdfcd66c9bec8ba01eb15cc65be4f3fcdb9043ac Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Wed, 11 Mar 2026 17:14:47 +0000 Subject: [PATCH 11/28] dom-to-image fix indentation --- plugins/tiddlywiki/dom-to-image/startup.js | 148 ++++++++++----------- 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/plugins/tiddlywiki/dom-to-image/startup.js b/plugins/tiddlywiki/dom-to-image/startup.js index e67f1fac8..66d465041 100644 --- a/plugins/tiddlywiki/dom-to-image/startup.js +++ b/plugins/tiddlywiki/dom-to-image/startup.js @@ -8,85 +8,85 @@ dom-to-image initialisation \*/ (function(){ - /*jslint node: true, browser: true */ - /*global $tw: false */ - "use strict"; +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; - // Export name and synchronous status - exports.name = "dom-to-image"; - exports.after = ["rootwidget"]; - exports.before = ["render"]; - exports.synchronous = true; +// Export name and synchronous status +exports.name = "dom-to-image"; +exports.after = ["rootwidget"]; +exports.before = ["render"]; +exports.synchronous = true; - exports.startup = function() { - var getPropertiesWithPrefix = function(properties,prefix) { - var result = Object.create(null); - $tw.utils.each(properties,function(value,name) { - if(name.indexOf(prefix) === 0) { - result[name.substring(prefix.length)] = properties[name]; - } - }); - return result; - }; - $tw.rootWidget.addEventListener("tm-save-dom-to-image",function(event) { - var self=this, - params = event.paramObject || {}, - domToImage = require("$:/plugins/tiddlywiki/dom-to-image/dom-to-image-more.js"), - domNode = document.querySelector(params.selector || "body.tc-body"), - oncompletion = params.oncompletion, - variables = getPropertiesWithPrefix(params,"var-"); - if(domNode) { - var method = "toPng"; - switch(params.format) { - case "jpeg": - // Intentional fallthrough - case "jpg": - method = "toJpeg"; - break; - case "svg": - method = "toSvg"; - break; - } - domToImage[method](domNode,{ - height: $tw.utils.parseInt(params.height) || domNode.offsetHeight, - width: $tw.utils.parseInt(params.width) || domNode.offsetWidth, - quality: $tw.utils.parseNumber(params.quality), - scale: $tw.utils.parseNumber(params.scale) || 1 - }) - .then(function(dataUrl) { - // Save the image - if(params["save-file"]) { - var link = document.createElement("a"); - link.download = params["save-file"]; - link.href = dataUrl; - link.click(); - } - // Save the tiddler - if(params["save-title"]) { - if(dataUrl.indexOf("data:image/svg+xml;") === 0) { - var commaIndex = dataUrl.indexOf(","); - $tw.wiki.addTiddler(new $tw.Tiddler({ - title: params["save-title"], - type: "image/svg+xml", - "text": decodeURIComponent(dataUrl.substring(commaIndex + 1)) - })); - } else { - var parts = dataUrl.split(";base64,"); - $tw.wiki.addTiddler(new $tw.Tiddler({ - title: params["save-title"], - type: parts[0].split(":")[1], - "text": parts[1] - })); - } - } - self.wiki.invokeActionString(oncompletion,self,variables,{parentWidget: $tw.rootWidget}); - }) - .catch(function(error) { - console.error("oops, something went wrong!", error); - }); +exports.startup = function() { + var getPropertiesWithPrefix = function(properties,prefix) { + var result = Object.create(null); + $tw.utils.each(properties,function(value,name) { + if(name.indexOf(prefix) === 0) { + result[name.substring(prefix.length)] = properties[name]; } }); + return result; }; + $tw.rootWidget.addEventListener("tm-save-dom-to-image",function(event) { + var self=this, + params = event.paramObject || {}, + domToImage = require("$:/plugins/tiddlywiki/dom-to-image/dom-to-image-more.js"), + domNode = document.querySelector(params.selector || "body.tc-body"), + oncompletion = params.oncompletion, + variables = getPropertiesWithPrefix(params,"var-"); + if(domNode) { + var method = "toPng"; + switch(params.format) { + case "jpeg": + // Intentional fallthrough + case "jpg": + method = "toJpeg"; + break; + case "svg": + method = "toSvg"; + break; + } + domToImage[method](domNode,{ + height: $tw.utils.parseInt(params.height) || domNode.offsetHeight, + width: $tw.utils.parseInt(params.width) || domNode.offsetWidth, + quality: $tw.utils.parseNumber(params.quality), + scale: $tw.utils.parseNumber(params.scale) || 1 + }) + .then(function(dataUrl) { + // Save the image + if(params["save-file"]) { + var link = document.createElement("a"); + link.download = params["save-file"]; + link.href = dataUrl; + link.click(); + } + // Save the tiddler + if(params["save-title"]) { + if(dataUrl.indexOf("data:image/svg+xml;") === 0) { + var commaIndex = dataUrl.indexOf(","); + $tw.wiki.addTiddler(new $tw.Tiddler({ + title: params["save-title"], + type: "image/svg+xml", + "text": decodeURIComponent(dataUrl.substring(commaIndex + 1)) + })); + } else { + var parts = dataUrl.split(";base64,"); + $tw.wiki.addTiddler(new $tw.Tiddler({ + title: params["save-title"], + type: parts[0].split(":")[1], + "text": parts[1] + })); + } + } + self.wiki.invokeActionString(oncompletion,self,variables,{parentWidget: $tw.rootWidget}); + }) + .catch(function(error) { + console.error("oops, something went wrong!", error); + }); + } + }); +}; })(); \ No newline at end of file From 4cd84a6ba19423d143fea3bb0b5f6d032fbd58c0 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Wed, 11 Mar 2026 17:30:23 +0000 Subject: [PATCH 12/28] Geospatial plugin: remove residue of dom-to-image plugin Fixes #9683 --- plugins/tiddlywiki/geospatial/startup.js | 55 ------------------------ 1 file changed, 55 deletions(-) diff --git a/plugins/tiddlywiki/geospatial/startup.js b/plugins/tiddlywiki/geospatial/startup.js index dbe119ae0..43ee207dd 100644 --- a/plugins/tiddlywiki/geospatial/startup.js +++ b/plugins/tiddlywiki/geospatial/startup.js @@ -25,61 +25,6 @@ exports.startup = function() { require("$:/plugins/tiddlywiki/geospatial/leaflet.markercluster.js"); } // Install geolocation message handler - $tw.rootWidget.addEventListener("tm-save-dom-to-image",function(event) { - var params = event.paramObject || {}, - domToImage = require("$:/plugins/tiddlywiki/geospatial/dom-to-image-more.js"), - domNode = document.querySelector(params.selector || "body.tc-body"); - if(domNode) { - var method = "toPng"; - switch(params.format) { - case "jpeg": - // Intentional fallthrough - case "jpg": - method = "toJpeg"; - break; - case "svg": - method = "toSvg"; - break; - } - domToImage[method](domNode,{ - height: $tw.utils.parseInt(params.height) || domNode.offsetHeight, - width: $tw.utils.parseInt(params.width) || domNode.offsetWidth, - quality: $tw.utils.parseNumber(params.quality), - scale: $tw.utils.parseNumber(params.scale) || 1 - }) - .then(function(dataUrl) { - // Save the image - if(params["save-file"]) { - var link = document.createElement("a"); - link.download = params["save-file"]; - link.href = dataUrl; - link.click(); - } - // Save the tiddler - if(params["save-title"]) { - if(dataUrl.indexOf("data:image/svg+xml;") === 0) { - var commaIndex = dataUrl.indexOf(","); - $tw.wiki.addTiddler(new $tw.Tiddler({ - title: params["save-title"], - type: "image/svg+xml", - "text": decodeURIComponent(dataUrl.substring(commaIndex + 1)) - })); - } else { - var parts = dataUrl.split(";base64,"); - $tw.wiki.addTiddler(new $tw.Tiddler({ - title: params["save-title"], - type: parts[0].split(":")[1], - "text": parts[1] - })); - } - } - }) - .catch(function(error) { - console.error("oops, something went wrong!", error); - }); - } - }); - // Install geolocation message handler $tw.rootWidget.addEventListener("tm-request-geolocation",function(event) { var widget = event.widget, wiki = widget.wiki || $tw.wiki, From c07396c453a085a7378e84c071d6ffc1ba4edeb3 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Sun, 15 Mar 2026 10:46:02 +0000 Subject: [PATCH 13/28] Community: Add @kjharcombe as infrastructure team lead Please add your community card when you can, many thanks. --- community/project/teams/Infrastructure Team.tid | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/community/project/teams/Infrastructure Team.tid b/community/project/teams/Infrastructure Team.tid index 047c585a0..4fd0e62fd 100644 --- a/community/project/teams/Infrastructure Team.tid +++ b/community/project/teams/Infrastructure Team.tid @@ -1,6 +1,7 @@ created: 20250909171928024 modified: 20251110133437795 tags: Community/Team +leader: @kjharcombe team: @MotovunJack title: Infrastructure Team @@ -12,4 +13,4 @@ The infrastructure includes: * github.com/TiddlyWiki * tiddlywiki.com DNS * Netlify account for PR previews -* edit.tiddlywiki.com \ No newline at end of file +* edit.tiddlywiki.com From dda4c7fb10e8e7a3110e4f9877cdb3cf90df9675 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Sun, 15 Mar 2026 16:53:51 +0000 Subject: [PATCH 14/28] Update to latest version of dom-to-image-more library Include it in tw5.com for testing --- editions/prerelease/tiddlywiki.info | 3 ++- editions/tw5.com/tiddlywiki.info | 3 ++- .../tiddlywiki/dom-to-image/files/dom-to-image-more.min.js | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/editions/prerelease/tiddlywiki.info b/editions/prerelease/tiddlywiki.info index 9b37efef3..2d779069f 100644 --- a/editions/prerelease/tiddlywiki.info +++ b/editions/prerelease/tiddlywiki.info @@ -13,7 +13,8 @@ "tiddlywiki/menubar", "tiddlywiki/jszip", "tiddlywiki/confetti", - "tiddlywiki/tour" + "tiddlywiki/tour", + "tiddlywiki/dom-to-image" ], "themes": [ "tiddlywiki/vanilla", diff --git a/editions/tw5.com/tiddlywiki.info b/editions/tw5.com/tiddlywiki.info index 8905904c5..55ffdf3bf 100644 --- a/editions/tw5.com/tiddlywiki.info +++ b/editions/tw5.com/tiddlywiki.info @@ -7,7 +7,8 @@ "tiddlywiki/internals", "tiddlywiki/menubar", "tiddlywiki/railroad", - "tiddlywiki/tour" + "tiddlywiki/tour", + "tiddlywiki/dom-to-image" ], "themes": [ "tiddlywiki/vanilla", diff --git a/plugins/tiddlywiki/dom-to-image/files/dom-to-image-more.min.js b/plugins/tiddlywiki/dom-to-image/files/dom-to-image-more.min.js index 6be6a54eb..4217d45a1 100644 --- a/plugins/tiddlywiki/dom-to-image/files/dom-to-image-more.min.js +++ b/plugins/tiddlywiki/dom-to-image/files/dom-to-image-more.min.js @@ -1,3 +1,3 @@ -/*! dom-to-image-more 14-10-2024 */ -(l=>{let f=(()=>{let e=0;return{escape:function(e){return e.replace(/([.*+?^${}()|[\]/\\])/g,"\\$1")},isDataUrl:function(e){return-1!==e.search(/^(data:)/)},canvasToBlob:function(t){if(t.toBlob)return new Promise(function(e){t.toBlob(e)});return(r=>new Promise(function(e){var t=u(r.toDataURL().split(",")[1]),n=t.length,o=new Uint8Array(n);for(let e=0;e0<=s.search(e)).length),a.impl.options.useCredentials&&(r.withCredentials=!0),a.impl.options.corsImg&&0===s.indexOf("http")&&-1===s.indexOf(window.location.origin)){var i="POST"===(a.impl.options.corsImg.method||"GET").toUpperCase()?"POST":"GET";r.open(i,(a.impl.options.corsImg.url||"").replace("#{cors}",s),!0);let t=!1,n=a.impl.options.corsImg.headers||{},o=(Object.keys(n).forEach(function(e){-1!==n[e].indexOf("application/json")&&(t=!0),r.setRequestHeader(e,n[e])}),(e=>{try{return JSON.parse(JSON.stringify(e))}catch(e){l("corsImg.data is missing or invalid:"+e.toString())}})(a.impl.options.corsImg.data||""));Object.keys(o).forEach(function(e){"string"==typeof o[e]&&(o[e]=o[e].replace("#{cors}",s))}),r.send(t?JSON.stringify(o):o)}else r.open("GET",s,!0),r.send();let n;function l(e){console.error(e),t("")}a.impl.options.imagePlaceholder&&(i=a.impl.options.imagePlaceholder.split(/,/))&&i[1]&&(n=i[1])}));return e.promise},uid:function(){return"u"+("0000"+(Math.random()*Math.pow(36,4)<<0).toString(36)).slice(-4)+e++},delay:function(n){return function(t){return new Promise(function(e){setTimeout(function(){e(t)},n)})}},asArray:function(t){var n=[],o=t.length;for(let e=0;e{document.body.removeChild(n),t(e)},n.appendChild(o),o.src=r,document.body.appendChild(n)}):Promise.resolve()},width:function(e){var t=i(e,"width");if(!isNaN(t))return t;var t=i(e,"border-left-width"),n=i(e,"border-right-width");return e.scrollWidth+t+n},height:function(e){var t=i(e,"height");if(!isNaN(t))return t;var t=i(e,"border-top-width"),n=i(e,"border-bottom-width");return e.scrollHeight+t+n},getWindow:t,isElement:r,isElementHostForOpenShadowRoot:function(e){return r(e)&&null!==e.shadowRoot},isShadowRoot:n,isInShadowRoot:o,isHTMLElement:function(e){return e instanceof t(e).HTMLElement},isHTMLCanvasElement:function(e){return e instanceof t(e).HTMLCanvasElement},isHTMLInputElement:function(e){return e instanceof t(e).HTMLInputElement},isHTMLImageElement:function(e){return e instanceof t(e).HTMLImageElement},isHTMLLinkElement:function(e){return e instanceof t(e).HTMLLinkElement},isHTMLScriptElement:function(e){return e instanceof t(e).HTMLScriptElement},isHTMLStyleElement:function(e){return e instanceof t(e).HTMLStyleElement},isHTMLTextAreaElement:function(e){return e instanceof t(e).HTMLTextAreaElement},isShadowSlotElement:function(e){return o(e)&&e instanceof t(e).HTMLSlotElement},isSVGElement:function(e){return e instanceof t(e).SVGElement},isSVGRectElement:function(e){return e instanceof t(e).SVGRectElement},isDimensionMissing:function(e){return isNaN(e)||e<=0}};function t(e){e=e?e.ownerDocument:void 0;return(e?e.defaultView:void 0)||window||l}function n(e){return e instanceof t(e).ShadowRoot}function o(e){return null!=e&&void 0!==e.getRootNode&&n(e.getRootNode())}function r(e){return e instanceof t(e).Element}function i(t,n){if(t.nodeType===c){let e=m(t).getPropertyValue(n);if("px"===e.slice(-2))return e=e.slice(0,-2),parseFloat(e)}return NaN}})(),r=(()=>{let o=/url\(['"]?([^'"]+?)['"]?\)/g;return{inlineAll:function(t,o,r){if(!e(t))return Promise.resolve(t);return Promise.resolve(t).then(n).then(function(e){let n=Promise.resolve(t);return e.forEach(function(t){n=n.then(function(e){return i(e,t,o,r)})}),n})},shouldProcess:e,impl:{readUrls:n,inline:i}};function e(e){return-1!==e.search(o)}function n(e){for(var t,n=[];null!==(t=o.exec(e));)n.push(t[1]);return n.filter(function(e){return!f.isDataUrl(e)})}function i(n,o,t,e){return Promise.resolve(o).then(function(e){return t?f.resolveUrl(e,t):e}).then(e||f.getAndEncode).then(function(e){return n.replace((t=o,new RegExp(`(url\\(['"]?)(${f.escape(t)})(['"]?\\))`,"g")),`$1${e}$3`);var t})}})(),e={resolveAll:function(){return t().then(function(e){return Promise.all(e.map(function(e){return e.resolve()}))}).then(function(e){return e.join("\n")})},impl:{readAll:t}};function t(){return Promise.resolve(f.asArray(document.styleSheets)).then(function(e){let n=[];return e.forEach(function(t){var e=Object.getPrototypeOf(t);if(Object.prototype.hasOwnProperty.call(e,"cssRules"))try{f.asArray(t.cssRules||[]).forEach(n.push.bind(n))}catch(e){console.error("domtoimage: Error while reading CSS rules from "+t.href,e.toString())}}),n}).then(function(e){return e.filter(function(e){return e.type===CSSRule.FONT_FACE_RULE}).filter(function(e){return r.shouldProcess(e.style.getPropertyValue("src"))})}).then(function(e){return e.map(t)});function t(t){return{resolve:function(){var e=(t.parentStyleSheet||{}).href;return r.inlineAll(t.cssText,e)},src:function(){return t.style.getPropertyValue("src")}}}}let n={inlineAll:function t(e){if(!f.isElement(e))return Promise.resolve(e);return n(e).then(function(){return f.isHTMLImageElement(e)?o(e).inline():Promise.all(f.asArray(e.childNodes).map(function(e){return t(e)}))});function n(o){let e=["background","background-image"],t=e.map(function(t){let e=o.style.getPropertyValue(t),n=o.style.getPropertyPriority(t);return e?r.inlineAll(e).then(function(e){o.style.setProperty(t,e,n)}):Promise.resolve()});return Promise.all(t).then(function(){return o})}},impl:{newImage:o}};function o(n){return{inline:function(e){if(f.isDataUrl(n.src))return Promise.resolve();return Promise.resolve(n.src).then(e||f.getAndEncode).then(function(t){return new Promise(function(e){n.onload=e,n.onerror=e,n.src=t})})}}}let s={copyDefaultStyles:!0,imagePlaceholder:void 0,cacheBust:!1,useCredentials:!1,useCredentialsFilters:[],httpTimeout:3e4,styleCaching:"strict",corsImg:void 0,adjustClonedNode:void 0},a={toSvg:d,toPng:function(e,t){return i(e,t).then(function(e){return e.toDataURL()})},toJpeg:function(e,t){return i(e,t).then(function(e){return e.toDataURL("image/jpeg",(t?t.quality:void 0)||1)})},toBlob:function(e,t){return i(e,t).then(f.canvasToBlob)},toPixelData:function(t,e){return i(t,e).then(function(e){return e.getContext("2d").getImageData(0,0,f.width(t),f.height(t)).data})},toCanvas:i,impl:{fontFaces:e,images:n,util:f,inliner:r,urlCache:[],options:{}}},c=("object"==typeof exports&&"object"==typeof module?module.exports=a:l.domtoimage=a,("undefined"!=typeof Node?Node.ELEMENT_NODE:void 0)||1),m=(void 0!==l?l.getComputedStyle:void 0)||("undefined"!=typeof window?window.getComputedStyle:void 0)||globalThis.getComputedStyle,u=(void 0!==l?l.atob:void 0)||("undefined"!=typeof window?window.atob:void 0)||globalThis.atob;function d(e,r){let t=a.impl.util.getWindow(e);var n=r=r||{};void 0===n.copyDefaultStyles?a.impl.options.copyDefaultStyles=s.copyDefaultStyles:a.impl.options.copyDefaultStyles=n.copyDefaultStyles,a.impl.options.imagePlaceholder=(void 0===n.imagePlaceholder?s:n).imagePlaceholder,a.impl.options.cacheBust=(void 0===n.cacheBust?s:n).cacheBust,a.impl.options.corsImg=(void 0===n.corsImg?s:n).corsImg,a.impl.options.useCredentials=(void 0===n.useCredentials?s:n).useCredentials,a.impl.options.useCredentialsFilters=(void 0===n.useCredentialsFilters?s:n).useCredentialsFilters,a.impl.options.httpTimeout=(void 0===n.httpTimeout?s:n).httpTimeout,a.impl.options.styleCaching=(void 0===n.styleCaching?s:n).styleCaching;let i=[];return Promise.resolve(e).then(function(e){if(e.nodeType===c)return e;var t=e,n=e.parentNode,o=document.createElement("span");return n.replaceChild(o,t),o.append(e),i.push({parent:n,child:t,wrapper:o}),o}).then(function(e){return function l(t,s,r,u){let e=s.filter;if(t===h||f.isHTMLScriptElement(t)||f.isHTMLStyleElement(t)||f.isHTMLLinkElement(t)||null!==r&&e&&!e(t))return Promise.resolve();return Promise.resolve(t).then(n).then(o).then(function(e){return c(e,a(t))}).then(i).then(function(e){return d(e,t)});function n(e){return f.isHTMLCanvasElement(e)?f.makeImage(e.toDataURL()):e.cloneNode(!1)}function o(e){return s.adjustClonedNode&&s.adjustClonedNode(t,e,!1),Promise.resolve(e)}function i(e){return s.adjustClonedNode&&s.adjustClonedNode(t,e,!0),Promise.resolve(e)}function a(e){return f.isElementHostForOpenShadowRoot(e)?e.shadowRoot:e}function c(n,e){let o=t(e),r=Promise.resolve();if(0!==o.length){let t=m(i(e));f.asArray(o).forEach(function(e){r=r.then(function(){return l(e,s,t,u).then(function(e){e&&n.appendChild(e)})})})}return r.then(function(){return n});function i(e){return f.isShadowRoot(e)?e.host:e}function t(t){if(f.isShadowSlotElement(t)){let e=t.assignedNodes();if(e&&0t.style.removeProperty(e)),["left","right","top","bottom"].forEach(e=>{t.style.getPropertyValue(e)&&t.style.setProperty(e,"0px")})))}e(a,u)}function t(){let s=f.uid();function t(r){let i=m(a,r),l=i.getPropertyValue("content");if(""!==l&&"none"!==l){let e=u.getAttribute("class")||"",t=(u.setAttribute("class",e+" "+s),document.createElement("style"));function n(){let e=`.${s}:`+r,t=(i.cssText?n:o)();return document.createTextNode(e+`{${t}}`);function n(){return`${i.cssText} content: ${l};`}function o(){let e=f.asArray(i).map(t).join("; ");return e+";";function t(e){let t=i.getPropertyValue(e),n=i.getPropertyPriority(e)?" !important":"";return e+": "+t+n}}}t.appendChild(n()),u.appendChild(t)}}[":before",":after"].forEach(function(e){t(e)})}function n(){f.isHTMLTextAreaElement(a)&&(u.innerHTML=a.value),f.isHTMLInputElement(a)&&u.setAttribute("value",a.value)}function o(){f.isSVGElement(u)&&(u.setAttribute("xmlns","http://www.w3.org/2000/svg"),f.isSVGRectElement(u))&&["width","height"].forEach(function(e){let t=u.getAttribute(e);t&&u.style.setProperty(e,t)})}}}(e,r,null,t)}).then(r.disableEmbedFonts?Promise.resolve(e):p).then(g).then(function(t){r.bgcolor&&(t.style.backgroundColor=r.bgcolor);r.width&&(t.style.width=r.width+"px");r.height&&(t.style.height=r.height+"px");r.style&&Object.keys(r.style).forEach(function(e){t.style[e]=r.style[e]});let e=null;"function"==typeof r.onclone&&(e=r.onclone(t));return Promise.resolve(e).then(function(){return t})}).then(function(e){let n=r.width||f.width(e),o=r.height||f.height(e);return Promise.resolve(e).then(function(e){return e.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),(new XMLSerializer).serializeToString(e)}).then(f.escapeXhtml).then(function(e){var t=(f.isDimensionMissing(n)?' width="100%"':` width="${n}"`)+(f.isDimensionMissing(o)?' height="100%"':` height="${o}"`);return`${e}`}).then(function(e){return"data:image/svg+xml;charset=utf-8,"+e})}).then(function(e){for(;0{h&&(document.body.removeChild(h),h=null),v&&clearTimeout(v),v=setTimeout(()=>{v=null,w={}},2e4)})(),e})}function i(r,i){return d(r,i=i||{}).then(f.makeImage).then(function(e){var t="number"!=typeof i.scale?1:i.scale,n=((e,t)=>{let n=i.width||f.width(e),o=i.height||f.height(e);return f.isDimensionMissing(n)&&(n=f.isDimensionMissing(o)?300:2*o),f.isDimensionMissing(o)&&(o=n/2),(e=document.createElement("canvas")).width=n*t,e.height=o*t,i.bgcolor&&((t=e.getContext("2d")).fillStyle=i.bgcolor,t.fillRect(0,0,e.width,e.height)),e})(r,t),o=n.getContext("2d");return o.msImageSmoothingEnabled=!1,o.imageSmoothingEnabled=!1,e&&(o.scale(t,t),o.drawImage(e,0,0)),n})}let h=null;function p(n){return e.resolveAll().then(function(e){var t;return""!==e&&(t=document.createElement("style"),n.appendChild(t),t.appendChild(document.createTextNode(e))),n})}function g(e){return n.inlineAll(e).then(function(){return e})}function y(e,t,i,l,n){let s=a.impl.options.copyDefaultStyles?((t,e)=>{var n,o=(e=>("relaxed"!==t.styleCaching?e:e.filter((e,t,n)=>0===t||t===n.length-1)).join(">"))(e=(e=>{var t=[];do{if(e.nodeType===c){var n=e.tagName;if(t.push(n),E.includes(n))break}}while(e=e.parentNode);return t})(e));{if(w[o])return w[o];e=((e,t)=>{let n=e.body;do{var o=t.pop(),o=e.createElement(o);n.appendChild(o),n=o}while(0{if(h)return h.contentWindow;t=document.characterSet||"UTF-8",e=(e=document.doctype)?(`":"",(h=document.createElement("iframe")).id="domtoimage-sandbox-"+f.uid(),h.style.visibility="hidden",h.style.position="fixed",document.body.appendChild(h);var e,t,n=h,o="domtoimage-sandbox";try{return n.contentWindow.document.write(e+`${o}`),n.contentWindow}catch(e){}var r=document.createElement("meta");r.setAttribute("charset",t);try{var i=document.implementation.createHTMLDocument(o),l=(i.head.appendChild(r),e+i.documentElement.outerHTML);return n.setAttribute("srcdoc",l),n.contentWindow}catch(e){}return n.contentDocument.head.appendChild(r),n.contentDocument.title=o,n.contentWindow;function s(e){var t;return e?((t=document.createElement("div")).innerText=e,t.innerHTML):""}})()).document,e),n=((e,t)=>{let n={},o=e.getComputedStyle(t);return f.asArray(o).forEach(function(e){n[e]="width"===e||"height"===e?"auto":o.getPropertyValue(e)}),n})(n,e);var r=e;do{var i=r.parentElement;null!==i&&i.removeChild(r),r=i}while(r&&"BODY"!==r.tagName);return w[o]=n}})(e,t):{},u=n.style;f.asArray(i).forEach(function(e){var t,n=i.getPropertyValue(e),o=s[e],r=l?l.getPropertyValue(e):void 0;u.getPropertyValue(e)||(n!==o||l&&n!==r)&&(o=i.getPropertyPriority(e),r=u,n=n,o=o,t=0<=["background-clip"].indexOf(e=e),o?(r.setProperty(e,n,o),t&&r.setProperty("-webkit-"+e,n,o)):(r.setProperty(e,n),t&&r.setProperty("-webkit-"+e,n)))})}let v=null,w={},E=["ADDRESS","ARTICLE","ASIDE","BLOCKQUOTE","DETAILS","DIALOG","DD","DIV","DL","DT","FIELDSET","FIGCAPTION","FIGURE","FOOTER","FORM","H1","H2","H3","H4","H5","H6","HEADER","HGROUP","HR","LI","MAIN","NAV","OL","P","PRE","SECTION","SVG","TABLE","UL","math","svg","BODY","HEAD","HTML"]})(this); +/*! dom-to-image-more 22-10-2025 */ +(l=>{let m=(()=>{let e=0;return{escape:function(e){return e.replace(/([.*+?^${}()|[\]/\\])/g,"\\$1")},isDataUrl:function(e){return-1!==e.search(/^(data:)/)},canvasToBlob:function(t){if(t.toBlob)return new Promise(function(e){t.toBlob(e)});return(r=>new Promise(function(e){var t=s(r.toDataURL().split(",")[1]),n=t.length,o=new Uint8Array(n);for(let e=0;e0<=l.search(e)).length),d.impl.options.useCredentials&&(r.withCredentials=!0),d.impl.options.corsImg&&0===l.indexOf("http")&&-1===l.indexOf(window.location.origin)){var e="POST"===(d.impl.options.corsImg.method||"GET").toUpperCase()?"POST":"GET";r.open(e,(d.impl.options.corsImg.url||"").replace("#{cors}",l),!0);let t=!1,n=d.impl.options.corsImg.headers||{},o=(Object.keys(n).forEach(function(e){-1!==n[e].indexOf("application/json")&&(t=!0),r.setRequestHeader(e,n[e])}),(e=>{try{return JSON.parse(JSON.stringify(e))}catch(e){i("corsImg.data is missing or invalid:"+e.toString())}})(d.impl.options.corsImg.data||""));Object.keys(o).forEach(function(e){"string"==typeof o[e]&&(o[e]=o[e].replace("#{cors}",l))}),r.responseType="blob",r.send(t?JSON.stringify(o):o)}else r.open("GET",l,!0),r.responseType="blob",r.send()}));return e.promise},uid:function(){return"u"+("0000"+(Math.random()*Math.pow(36,4)<<0).toString(36)).slice(-4)+e++},asArray:function(t){var n=[],o=t.length;for(let e=0;e{document.body.removeChild(n),t(e)},n.appendChild(o),o.src=r,document.body.appendChild(n)}):Promise.resolve()},width:function(e){var t=i(e,"width");if(!isNaN(t))return t;var t=i(e,"border-left-width"),n=i(e,"border-right-width");return e.scrollWidth+t+n},height:function(e){var t=i(e,"height");if(!isNaN(t))return t;var t=i(e,"border-top-width"),n=i(e,"border-bottom-width");return e.scrollHeight+t+n},getWindow:t,isElement:r,isElementHostForOpenShadowRoot:function(e){return r(e)&&null!==e.shadowRoot},isShadowRoot:n,isInShadowRoot:o,isHTMLElement:function(e){return e instanceof t(e).HTMLElement},isHTMLCanvasElement:function(e){return e instanceof t(e).HTMLCanvasElement},isHTMLInputElement:function(e){return e instanceof t(e).HTMLInputElement},isHTMLImageElement:function(e){return e instanceof t(e).HTMLImageElement},isHTMLLinkElement:function(e){return e instanceof t(e).HTMLLinkElement},isHTMLScriptElement:function(e){return e instanceof t(e).HTMLScriptElement},isHTMLStyleElement:function(e){return e instanceof t(e).HTMLStyleElement},isHTMLTextAreaElement:function(e){return e instanceof t(e).HTMLTextAreaElement},isShadowSlotElement:function(e){return o(e)&&e instanceof t(e).HTMLSlotElement},isSVGElement:function(e){return e instanceof t(e).SVGElement},isSVGRectElement:function(e){return e instanceof t(e).SVGRectElement},isDimensionMissing:function(e){return isNaN(e)||e<=0}};function t(e){e=e?e.ownerDocument:void 0;return(e?e.defaultView:void 0)||window||l}function n(e){return e instanceof t(e).ShadowRoot}function o(e){return null!=e&&void 0!==e.getRootNode&&n(e.getRootNode())}function r(e){return e instanceof t(e).Element}function i(t,n){if(t.nodeType===f){let e=h(t).getPropertyValue(n);if("px"===e.slice(-2))return e=e.slice(0,-2),parseFloat(e)}return NaN}})(),r=(()=>{let o=/url\(\s*(["']?)((?:\\.|[^\\)])+)\1\s*\)/gm;return{inlineAll:function(t,o,r){if(!e(t))return Promise.resolve(t);return Promise.resolve(t).then(n).then(function(e){let n=Promise.resolve(t);return e.forEach(function(t){n=n.then(function(e){return i(e,t,o,r)})}),n})},shouldProcess:e,impl:{readUrls:n,inline:i,urlAsRegex:r}};function e(e){return-1!==e.search(o)}function n(e){for(var t,n=[];null!==(t=o.exec(e));)n.push(t[2]);return n.filter(function(e){return!m.isDataUrl(e)})}function r(e){return new RegExp(`url\\((["']?)(${m.escape(e)})\\1\\)`,"gm")}function i(n,o,t,e){return Promise.resolve(o).then(function(e){return t?m.resolveUrl(e,t):e}).then(e||m.getAndEncode).then(function(e){var t=r(o);return n.replace(t,`url($1${e}$1)`)})}})(),e={resolveAll:function(){return t().then(function(e){return Promise.all(e.map(function(e){return e.resolve()}))}).then(function(e){return e.join("\n")})},impl:{readAll:t}};function t(){return Promise.resolve(m.asArray(document.styleSheets)).then(function(e){let n=[];return e.forEach(function(t){var e=Object.getPrototypeOf(t);if(Object.prototype.hasOwnProperty.call(e,"cssRules"))try{m.asArray(t.cssRules||[]).forEach(n.push.bind(n))}catch(e){console.error("domtoimage: Error while reading CSS rules from: "+t.href,e.toString())}}),n}).then(function(e){return e.filter(function(e){return e.type===CSSRule.FONT_FACE_RULE}).filter(function(e){return r.shouldProcess(e.style.getPropertyValue("src"))})}).then(function(e){return e.map(t)});function t(t){return{resolve:function(){var e=(t.parentStyleSheet||{}).href;return r.inlineAll(t.cssText,e)},src:function(){return t.style.getPropertyValue("src")}}}}let n={inlineAll:function t(e){if(!m.isElement(e))return Promise.resolve(e);return n(e).then(function(){return m.isHTMLImageElement(e)?o(e).inline():Promise.all(m.asArray(e.childNodes).map(function(e){return t(e)}))});function n(o){let e=["background","background-image"],t=e.map(function(t){let e=o.style.getPropertyValue(t),n=o.style.getPropertyPriority(t);return e?r.inlineAll(e).then(function(e){o.style.setProperty(t,e,n)}):Promise.resolve()});return Promise.all(t).then(function(){return o})}},impl:{newImage:o}};function o(n){return{inline:function(e){if(m.isDataUrl(n.src))return Promise.resolve();return Promise.resolve(n.src).then(e||m.getAndEncode).then(function(t){return new Promise(function(e){n.onload=e,n.onerror=e,n.src=t})})}}}let i={copyDefaultStyles:!0,imagePlaceholder:void 0,cacheBust:!1,useCredentials:!1,useCredentialsFilters:[],httpTimeout:3e4,styleCaching:"strict",corsImg:void 0,adjustClonedNode:void 0,filterStyles:void 0},d={toSvg:u,toPng:function(e,t){return a(e,t).then(function(e){return e.toDataURL()})},toJpeg:function(e,t){return a(e,t).then(function(e){return e.toDataURL("image/jpeg",(t?t.quality:void 0)||1)})},toBlob:function(e,t){return a(e,t).then(m.canvasToBlob)},toPixelData:function(t,e){return a(t,e).then(function(e){return e.getContext("2d").getImageData(0,0,m.width(t),m.height(t)).data})},toCanvas:a,impl:{fontFaces:e,images:n,util:m,inliner:r,urlCache:[],options:{},copyOptions:function(e){void 0===e.copyDefaultStyles?d.impl.options.copyDefaultStyles=i.copyDefaultStyles:d.impl.options.copyDefaultStyles=e.copyDefaultStyles;d.impl.options.imagePlaceholder=(void 0===e.imagePlaceholder?i:e).imagePlaceholder;d.impl.options.cacheBust=(void 0===e.cacheBust?i:e).cacheBust;d.impl.options.corsImg=(void 0===e.corsImg?i:e).corsImg;d.impl.options.useCredentials=(void 0===e.useCredentials?i:e).useCredentials;d.impl.options.useCredentialsFilters=(void 0===e.useCredentialsFilters?i:e).useCredentialsFilters;d.impl.options.httpTimeout=(void 0===e.httpTimeout?i:e).httpTimeout;d.impl.options.styleCaching=(void 0===e.styleCaching?i:e).styleCaching}}},f=("object"==typeof exports&&"object"==typeof module?module.exports=d:l.domtoimage=d,(void 0===Node?void 0:Node.ELEMENT_NODE)||1),h=(void 0===l?void 0:l.getComputedStyle)||(void 0===window?void 0:window.getComputedStyle)||globalThis.getComputedStyle,s=(void 0===l?void 0:l.atob)||(void 0===window?void 0:window.atob)||globalThis.atob;function u(t,r){let n=d.impl.util.getWindow(t),o=(r=r||{},d.impl.copyOptions(r),[]);return Promise.resolve(t).then(function(e){if(e.nodeType===f)return e;var t=e,n=document.createElement("span");return t.replaceWith(n),n.append(e),o.push({child:t,wrapper:n}),n}).then(function(e){return function l(t,s,i,u){let e=s.filter;if(t===p||m.isHTMLScriptElement(t)||m.isHTMLStyleElement(t)||m.isHTMLLinkElement(t)||null!==i&&e&&!e(t))return Promise.resolve();return Promise.resolve(t).then(n).then(o).then(function(e){return c(e,a(t))}).then(r).then(function(e){return d(e,t)});function n(e){return m.isHTMLCanvasElement(e)?m.makeImage(e.toDataURL()):e.cloneNode(!1)}function o(e){return s.adjustClonedNode&&s.adjustClonedNode(t,e,!1),Promise.resolve(e)}function r(e){return s.adjustClonedNode&&s.adjustClonedNode(t,e,!0),Promise.resolve(e)}function a(e){return m.isElementHostForOpenShadowRoot(e)?e.shadowRoot:e}function c(n,e){let o=t(e),r=Promise.resolve();if(0!==o.length){let t=h(i(e));m.asArray(o).forEach(function(e){r=r.then(function(){return l(e,s,t,u).then(function(e){e&&n.appendChild(e)})})})}return r.then(function(){return n});function i(e){return m.isShadowRoot(e)?e.host:e}function t(t){if(m.isShadowSlotElement(t)){let e=t.assignedNodes();if(e&&0t.style.removeProperty(e)),["left","right","top","bottom"].forEach(e=>{t.style.getPropertyValue(e)&&t.style.setProperty(e,"0px")})))}e(a,u)}function n(){let s=m.uid();function t(r){let i=h(a,r),l=i.getPropertyValue("content");if(""!==l&&"none"!==l){let e=u.getAttribute("class")||"",t=(u.setAttribute("class",e+" "+s),document.createElement("style"));function n(){let e=`.${s}:`+r,t=(i.cssText?n:o)();return document.createTextNode(e+`{${t}}`);function n(){return`${i.cssText} content: ${l};`}function o(){let e=m.asArray(i).map(t).join("; ");return e+";";function t(e){let t=i.getPropertyValue(e),n=i.getPropertyPriority(e)?" !important":"";return e+": "+t+n}}}t.appendChild(n()),u.appendChild(t)}}[":before",":after"].forEach(function(e){t(e)})}function o(){m.isHTMLTextAreaElement(a)&&(u.innerHTML=a.value),m.isHTMLInputElement(a)&&u.setAttribute("value",a.value)}function r(){m.isSVGElement(u)&&(u.setAttribute("xmlns","http://www.w3.org/2000/svg"),m.isSVGRectElement(u))&&["width","height"].forEach(function(e){let t=u.getAttribute(e);t&&u.style.setProperty(e,t)})}}}(e,r,null,n)}).then(r.disableEmbedFonts?Promise.resolve(t):c).then(r.disableInlineImages?Promise.resolve(t):g).then(function(t){r.bgcolor&&(t.style.backgroundColor=r.bgcolor);r.width&&(t.style.width=r.width+"px");r.height&&(t.style.height=r.height+"px");r.style&&Object.keys(r.style).forEach(function(e){t.style[e]=r.style[e]});let e=null;"function"==typeof r.onclone&&(e=r.onclone(t));return Promise.resolve(e).then(function(){return t})}).then(function(e){let n=r.width||m.width(t),o=r.height||m.height(t);return Promise.resolve(e).then(function(e){return e.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),(new XMLSerializer).serializeToString(e)}).then(m.escapeXhtml).then(function(e){var t=(m.isDimensionMissing(n)?' width="100%"':` width="${n}"`)+(m.isDimensionMissing(o)?' height="100%"':` height="${o}"`);return`${e}`}).then(function(e){return"data:image/svg+xml;charset=utf-8,"+e})}).then(function(e){for(;0{p&&(document.body.removeChild(p),p=null),v&&clearTimeout(v),v=setTimeout(()=>{v=null,w={}},2e4)})(),e})}function a(r,i){return u(r,i=i||{}).then(m.makeImage).then(function(e){var t="number"!=typeof i.scale?1:i.scale,n=((e,t)=>{let n=i.width||m.width(e),o=i.height||m.height(e);return m.isDimensionMissing(n)&&(n=m.isDimensionMissing(o)?300:2*o),m.isDimensionMissing(o)&&(o=n/2),(e=document.createElement("canvas")).width=n*t,e.height=o*t,i.bgcolor&&((t=e.getContext("2d")).fillStyle=i.bgcolor,t.fillRect(0,0,e.width,e.height)),e})(r,t),o=n.getContext("2d");return o.msImageSmoothingEnabled=!1,o.imageSmoothingEnabled=!1,e&&(o.scale(t,t),o.drawImage(e,0,0)),n})}let p=null;function c(n){return e.resolveAll().then(function(e){var t;return""!==e&&(t=document.createElement("style"),n.appendChild(t),t.appendChild(document.createTextNode(e))),n})}function g(e){return n.inlineAll(e).then(function(){return e})}function y(i,l,s,u,e){let a=d.impl.options.copyDefaultStyles?((t,e)=>{var n,o=(e=>("relaxed"!==t.styleCaching?e:e.filter((e,t,n)=>0===t||t===n.length-1)).join(">"))(e=(e=>{var t=[];do{if(e.nodeType===f){var n=e.tagName;if(t.push(n),E.includes(n))break}}while(e=e.parentNode);return t})(e));{if(w[o])return w[o];e=((e,t)=>{let n=e.body;do{var o=t.pop(),o=e.createElement(o);n.appendChild(o),n=o}while(0{if(p)return p.contentWindow;t=document.characterSet||"UTF-8",e=(e=document.doctype)?(`":"",(p=document.createElement("iframe")).id="domtoimage-sandbox-"+m.uid(),p.style.top="-9999px",p.style.visibility="hidden",p.style.position="fixed",document.body.appendChild(p);var e,t,n=p,o="domtoimage-sandbox";try{return n.contentWindow.document.write(e+`${o}`),n.contentWindow}catch(e){}var r=document.createElement("meta");r.setAttribute("charset",t);try{var i=document.implementation.createHTMLDocument(o),l=(i.head.appendChild(r),e+i.documentElement.outerHTML);return n.setAttribute("srcdoc",l),n.contentWindow}catch(e){}return n.contentDocument.head.appendChild(r),n.contentDocument.title=o,n.contentWindow;function s(e){var t;return e?((t=document.createElement("div")).innerText=e,t.innerHTML):""}})()).document,e),n=((e,t)=>{let n={},o=e.getComputedStyle(t);return m.asArray(o).forEach(function(e){n[e]="width"===e||"height"===e?"auto":o.getPropertyValue(e)}),n})(n,e);var r=e;do{var i=r.parentElement;null!==i&&i.removeChild(r),r=i}while(r&&"BODY"!==r.tagName);return w[o]=n}})(i,l):{},c=e.style;m.asArray(s).forEach(function(e){var t,n,o,r;i.filterStyles&&!i.filterStyles(l,e)||(n=s.getPropertyValue(e),o=a[e],t=u?u.getPropertyValue(e):void 0,c.getPropertyValue(e))||(n!==o||u&&n!==t)&&(o=s.getPropertyPriority(e),t=c,n=n,o=o,r=0<=["background-clip"].indexOf(e=e),o?(t.setProperty(e,n,o),r&&t.setProperty("-webkit-"+e,n,o)):(t.setProperty(e,n),r&&t.setProperty("-webkit-"+e,n)))})}let v=null,w={},E=["ADDRESS","ARTICLE","ASIDE","BLOCKQUOTE","DETAILS","DIALOG","DD","DIV","DL","DT","FIELDSET","FIGCAPTION","FIGURE","FOOTER","FORM","H1","H2","H3","H4","H5","H6","HEADER","HGROUP","HR","LI","MAIN","NAV","OL","P","PRE","SECTION","SVG","TABLE","UL","math","svg","BODY","HEAD","HTML"]})(this); //# sourceMappingURL=dom-to-image-more.min.js.map \ No newline at end of file From 57c72756efb8bfb154d85f91dfefafaad74e1669 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Mon, 16 Mar 2026 16:57:55 +0000 Subject: [PATCH 15/28] Fix releasenote avatars Fixes #9746 Thanks @pmario --- editions/tw5.com/tiddlers/releasenotes/ReleasesProcedures.tid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editions/tw5.com/tiddlers/releasenotes/ReleasesProcedures.tid b/editions/tw5.com/tiddlers/releasenotes/ReleasesProcedures.tid index c6540ed54..0c4b60505 100644 --- a/editions/tw5.com/tiddlers/releasenotes/ReleasesProcedures.tid +++ b/editions/tw5.com/tiddlers/releasenotes/ReleasesProcedures.tid @@ -366,7 +366,7 @@ These are significant improvements that will benefit a broad range of users, and <$list filter="[enlistsort[]]" variable="username">
  • ] }}} class="tc-tiddlylink-external" target="_blank" rel="noopener noreferrer"> - addsuffix[.png?size=64]] }}} width="64" height="64"/> + addsuffix[?size=64]] }}} width="64" height="64"/> @<$text text=<>/>
  • From bb6d88f1444a0d82b8fc43dca789539b1f39e7af Mon Sep 17 00:00:00 2001 From: Xavier Cazin Date: Mon, 16 Mar 2026 18:08:06 +0100 Subject: [PATCH 16/28] fr-FR translation catchup before 5.4.0 (#9748) * fr-FR translation for the Alerts aria string * fr-FR translation for WikiInformation-related strings * fr-FR updates for TiddlySpot/TiddlyHost-related strings * fr-FR translation for Stability- and InfoTab-related strings --- languages/fr-FR/ControlPanel.multids | 20 ++++++++++++++------ languages/fr-FR/Misc.multids | 1 + 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/languages/fr-FR/ControlPanel.multids b/languages/fr-FR/ControlPanel.multids index baeeaab70..7925ca356 100644 --- a/languages/fr-FR/ControlPanel.multids +++ b/languages/fr-FR/ControlPanel.multids @@ -96,6 +96,10 @@ Plugins/PluginWillRequireReload: (rechargement requis) Plugins/Plugins/Caption: Plugins Plugins/Plugins/Hint: Plugins Plugins/Reinstall/Caption: réinstalle +Plugins/Stability/Deprecated: DÉPRÉCIÉ +Plugins/Stability/Experimental: EXPÉRIMENTAL +Plugins/Stability/Legacy: LEGACY +Plugins/Stability/Stable: STABLE Plugins/Themes/Caption: Thèmes Plugins/Themes/Hint: Plugins de thème Plugins/Update/Caption: mise à jour @@ -128,17 +132,16 @@ Saving/GitService/Gitea/Password: //Jeton d'accès// pour cette API (via l'inter Saving/TiddlySpot/Advanced/Heading: Paramètres avancés Saving/TiddlySpot/BackupDir: Dossier des //sauvegardes// Saving/TiddlySpot/Backups: Sauvegardes -Saving/TiddlySpot/Caption: Enregistreur ~TiddlySpot -Saving/TiddlySpot/ControlPanel: Panneau de contrôle ~TiddlySpot -Saving/TiddlySpot/Description: Ces paramètres ne servent que lors de la sauvegarde vers [[TiddlySpot|http://tiddlyspot.com]], [[TiddlyHost|https://tiddlyhost.com]] ou vers un serveur distant compatible. Voir [[ici|https://github.com/simonbaird/tiddlyhost/wiki/TiddlySpot-Saver-configuration-for-Tiddlyhost-and-Tiddlyspot]] pour plus d'informations sur la configuration de la sauvegarde sur ~TiddlySpot et ~TiddlyHost. +Saving/TiddlySpot/Caption: Enregistreur ~TiddlyHost +Saving/TiddlySpot/ControlPanel: Panneau de contrôle ~TiddlyHost +Saving/TiddlySpot/Description: Ces paramètres ne servent que lors de la sauvegarde vers [[TiddlyHost|https://tiddlyhost.com]] ou vers un serveur distant compatible. Voir [[ici|https://github.com/simonbaird/tiddlyhost/wiki/TiddlySpot-Saver-configuration-for-Tiddlyhost-and-Tiddlyspot]] pour plus d'informations sur la configuration de la sauvegarde sur ~TiddlyHost. Saving/TiddlySpot/Filename: Nom du fichier enregistré -Saving/TiddlySpot/Heading: ~TiddlySpot +Saving/TiddlySpot/Heading: ~TiddlyHost Saving/TiddlySpot/Hint: //L'URL par défaut est `http://.tiddlyspot.com/store.cgi`. Elle peut être remplacée par une adresse serveur personnalisée, comme `http://example.com/store.php`.// Saving/TiddlySpot/Password: Mot de passe -Saving/TiddlySpot/ReadOnly: Notez que [[TiddlySpot|http://tiddlyspot.com]] n'autorise plus la création de nouveaux sites. Pour les nouveaux sites vous pouvez utiliser [[TiddlyHost|https://tiddlyhost.com]], un nouveau service d'hébergement qui remplace ~TiddlySpot. Saving/TiddlySpot/ServerURL: URL du serveur Saving/TiddlySpot/UploadDir: Dossier des dépôts -Saving/TiddlySpot/UserName: Nom utilisé pour ce Wiki +Saving/TiddlySpot/UserName: Nom du Wiki Settings/AutoSave/Caption: Sauvegarde automatique Settings/AutoSave/Disabled/Description: Pas de sauvegarde automatique des modifications Settings/AutoSave/Enabled/Description: Sauvegarde automatique des modifications @@ -184,6 +187,8 @@ Settings/DefaultSidebarTab/Caption: Onglet par défaut sur la barre latérale Settings/DefaultSidebarTab/Hint: Indique l'onglet de la barre latérale qui sera affiché par défaut Settings/DefaultMoreSidebarTab/Caption: Onglet par défaut sous le Plus de la barre latérale Settings/DefaultMoreSidebarTab/Hint: Indique quel onglet sera affiché par défaut sous le Plus de la barre latérale +Settings/DefaultTiddlerInfoTab/Caption: Onglet par défaut pour les Infos du tiddler +Settings/DefaultTiddlerInfoTab/Hint: Spécifie l'onglet affiché par défaut lorsque le panneau d'information du tiddler est ouvert Settings/LinkToBehaviour/Caption: Comportement à l'ouverture du tiddler Settings/LinkToBehaviour/InsideRiver/Hint: Navigation depuis un emplacement //interne// au déroulé Settings/LinkToBehaviour/OutsideRiver/Hint: Navigation depuis un emplacement //externe// au déroulé @@ -245,3 +250,6 @@ ViewTemplateSubtitle/Caption: Visualisation du sous-titre ViewTemplateSubtitle/Hint: Cette cascade de règles est utilisée par le template de visualisation par défaut pour choisir dynamiquement le template d'affichage du sous-titre d'un tiddler. ViewTemplateTags/Caption: Visualisation des tags ViewTemplateTags/Hint: Cette cascade de règles est utilisée par le template de visualisation par défaut pour choisir dynamiquement le template d'affichage de la zone de tags d'un tiddler. +WikiInformation/Caption: Informations sur le Wiki +WikiInformation/Hint: Cette page rassemble quelques informations générales sur la configuration de ce ~TiddlyWiki. Elle est conçue pour permettre de partager rapidement certains aspects de sa configuration avec d'autres, par exemple lorsqu'on demande de l'aide sur un forum. Elle ne contient aucune information privée ni personnelle, et rien n'est partagé sans être explicitement copié-collé ailleurs. +WikiInformation/Drag/Caption: Déplacer ce lien pour copier cet outil sur un autre wiki \ No newline at end of file diff --git a/languages/fr-FR/Misc.multids b/languages/fr-FR/Misc.multids index 79e5e1273..7af909c6f 100644 --- a/languages/fr-FR/Misc.multids +++ b/languages/fr-FR/Misc.multids @@ -1,5 +1,6 @@ title: $:/language/ +Alerts: Alertes AboveStory/ClassicPlugin/Warning: On dirait que vous essayez de charger un plugin conçu pour ~TiddlyWiki Classic. Merci de noter que [[ces plugins ne fonctionnent pas avec TiddlyWiki version 5.x.x|https://tiddlywiki.com/#TiddlyWikiClassic]]. Plugins ~TiddlyWiki Classic détectés : BinaryWarning/Prompt: Ce tiddler contient des données binaires ClassicWarning/Hint: Ce tiddler est écrit au format TiddlyWiki Classic, qui n'est pas entièrement compatible avec TiddlyWiki version 5. Pour en savoir plus, rendez-vous à l'adresse https://tiddlywiki.com/static/Upgrading.html. From b7a34188238373daa9d218ecb7155209f31f2d3d Mon Sep 17 00:00:00 2001 From: Keiran Harcombe Date: Mon, 16 Mar 2026 21:58:42 +0000 Subject: [PATCH 17/28] Signing CLA (#9749) --- licenses/cla-individual.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/licenses/cla-individual.md b/licenses/cla-individual.md index c6197110f..e278daf7e 100644 --- a/licenses/cla-individual.md +++ b/licenses/cla-individual.md @@ -645,3 +645,5 @@ Rishu kumar, @rishu-7549, 2025/10/25 @peteratkins, 2025/12/29 @hsteve11, 2025/12/30 + +@kjharcombe, 2026/03/16 \ No newline at end of file From 1ece822d7af567d46b5d23889bb195415ddec529 Mon Sep 17 00:00:00 2001 From: Keiran Harcombe Date: Mon, 16 Mar 2026 23:27:50 +0000 Subject: [PATCH 18/28] Update kjharcombe.tid (#9750) I've created my community card for tiddlywiki.com --- community/people/kjharcombe.tid | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 community/people/kjharcombe.tid diff --git a/community/people/kjharcombe.tid b/community/people/kjharcombe.tid new file mode 100644 index 000000000..91ecb39ea --- /dev/null +++ b/community/people/kjharcombe.tid @@ -0,0 +1,7 @@ +title: @kjharcombe +tags: Community/Person +fullname: Keiran harcombe +first-sighting: 2026-03-16 +talk.tiddlywiki.org: kjharcombe +github: kjharcombe +avatar: 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 From ec27a4bf20aa4236a4753f0c2b57cd6d27d25148 Mon Sep 17 00:00:00 2001 From: Himmel Date: Thu, 19 Mar 2026 15:12:33 +0800 Subject: [PATCH 19/28] Add new contributor Himmel to CLA (#9755) --- licenses/cla-individual.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/licenses/cla-individual.md b/licenses/cla-individual.md index e278daf7e..c3f3db9d6 100644 --- a/licenses/cla-individual.md +++ b/licenses/cla-individual.md @@ -646,4 +646,6 @@ Rishu kumar, @rishu-7549, 2025/10/25 @hsteve11, 2025/12/30 -@kjharcombe, 2026/03/16 \ No newline at end of file +@kjharcombe, 2026/03/16 + +Himmel, @NotHimmel, 2026/03/19 From 533414b1df732ea7f8f6cc5aea231a56f68f6ea5 Mon Sep 17 00:00:00 2001 From: Mario Pietsch Date: Thu, 19 Mar 2026 19:47:26 +0100 Subject: [PATCH 20/28] Change codewrapping option to pre-wrap Fixes: #9757 Changed at: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9107/changes#diff-9c7b0f26dc347b90265ea42dd89565ecf1d7b85ac8b5ff3f7dc49eeec42003b5 Talk: https://talk.tiddlywiki.org/t/5-4-0-prerelease-code-lines-pre-wrap-setting-as-default/15066 --- themes/tiddlywiki/vanilla/options.multids | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/themes/tiddlywiki/vanilla/options.multids b/themes/tiddlywiki/vanilla/options.multids index ac64628d4..a671a970f 100644 --- a/themes/tiddlywiki/vanilla/options.multids +++ b/themes/tiddlywiki/vanilla/options.multids @@ -2,4 +2,4 @@ title: $:/themes/tiddlywiki/vanilla/options/ stickytitles: no sidebarlayout: fluid-fixed -codewrapping: pre +codewrapping: pre-wrap From 962692c90ce4f7f1a9a1464215551a9373dc17af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Urban=20Ali=C4=8D?= <44392621+ualich@users.noreply.github.com> Date: Thu, 19 Mar 2026 23:34:56 +0100 Subject: [PATCH 21/28] FIX: restore mhchem require for \ce and \pu macro support (#9756) --- plugins/tiddlywiki/katex/wrapper.js | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/tiddlywiki/katex/wrapper.js b/plugins/tiddlywiki/katex/wrapper.js index cd7d236e6..9770a5ef2 100644 --- a/plugins/tiddlywiki/katex/wrapper.js +++ b/plugins/tiddlywiki/katex/wrapper.js @@ -11,6 +11,7 @@ Wrapper for `katex.min.js` that provides a `<$latex>` widget. It is also availab var katex = require("$:/plugins/tiddlywiki/katex/katex.min.js"), Widget = require("$:/core/modules/widgets/widget.js").widget; +require("$:/plugins/tiddlywiki/katex/mhchem.min.js"); katex.macros = {}; katex.updateMacros = function() { From 65fcded29fd4c354fe45f9cccd14f000cb88285b Mon Sep 17 00:00:00 2001 From: Mario Pietsch Date: Fri, 20 Mar 2026 08:54:03 +0100 Subject: [PATCH 22/28] Fix name clashes - definition tiddlers with images (#9759) --- .../HelloThumbnail - Community Survey.tid | 2 +- .../HelloThumbnail - Intertwingled Innovations.tid | 2 +- ...y 2025.webp => Community Survey 2025 Image.webp} | Bin ...p.meta => Community Survey 2025 Image.webp.meta} | 2 +- ...ns.webp => Intertwingled Innovations Image.webp} | Bin .../Intertwingled Innovations Image.webp.meta | 3 +++ .../images/Intertwingled Innovations.webp.meta | 3 --- 7 files changed, 6 insertions(+), 6 deletions(-) rename editions/tw5.com/tiddlers/images/{Community Survey 2025.webp => Community Survey 2025 Image.webp} (100%) rename editions/tw5.com/tiddlers/images/{Community Survey 2025.webp.meta => Community Survey 2025 Image.webp.meta} (74%) rename editions/tw5.com/tiddlers/images/{Intertwingled Innovations.webp => Intertwingled Innovations Image.webp} (100%) create mode 100644 editions/tw5.com/tiddlers/images/Intertwingled Innovations Image.webp.meta delete mode 100644 editions/tw5.com/tiddlers/images/Intertwingled Innovations.webp.meta diff --git a/editions/tw5.com/tiddlers/hellothere/thumbnails/HelloThumbnail - Community Survey.tid b/editions/tw5.com/tiddlers/hellothere/thumbnails/HelloThumbnail - Community Survey.tid index b540348c9..efbb50e99 100644 --- a/editions/tw5.com/tiddlers/hellothere/thumbnails/HelloThumbnail - Community Survey.tid +++ b/editions/tw5.com/tiddlers/hellothere/thumbnails/HelloThumbnail - Community Survey.tid @@ -1,7 +1,7 @@ title: HelloThumbnail - Community Survey 2025 tags: HelloThumbnail color: rgb(234, 205, 183) -image: Community Survey 2025 +image: Community Survey 2025 Image caption: Community Survey link: Community Survey 2025 ribbon-text: NEW diff --git a/editions/tw5.com/tiddlers/hellothere/thumbnails/HelloThumbnail - Intertwingled Innovations.tid b/editions/tw5.com/tiddlers/hellothere/thumbnails/HelloThumbnail - Intertwingled Innovations.tid index 91678367b..9c5f6c6f6 100644 --- a/editions/tw5.com/tiddlers/hellothere/thumbnails/HelloThumbnail - Intertwingled Innovations.tid +++ b/editions/tw5.com/tiddlers/hellothere/thumbnails/HelloThumbnail - Intertwingled Innovations.tid @@ -1,7 +1,7 @@ background-color: #EDB431 caption: Intertwingled Innovations color: #ff0 -image: Intertwingled Innovations +image: Intertwingled Innovations Image link: Intertwingled Innovations tags: HelloThumbnail title: HelloThumbnail - Intertwingled Innovations diff --git a/editions/tw5.com/tiddlers/images/Community Survey 2025.webp b/editions/tw5.com/tiddlers/images/Community Survey 2025 Image.webp similarity index 100% rename from editions/tw5.com/tiddlers/images/Community Survey 2025.webp rename to editions/tw5.com/tiddlers/images/Community Survey 2025 Image.webp diff --git a/editions/tw5.com/tiddlers/images/Community Survey 2025.webp.meta b/editions/tw5.com/tiddlers/images/Community Survey 2025 Image.webp.meta similarity index 74% rename from editions/tw5.com/tiddlers/images/Community Survey 2025.webp.meta rename to editions/tw5.com/tiddlers/images/Community Survey 2025 Image.webp.meta index ca56926a1..8eb66313b 100644 --- a/editions/tw5.com/tiddlers/images/Community Survey 2025.webp.meta +++ b/editions/tw5.com/tiddlers/images/Community Survey 2025 Image.webp.meta @@ -1,4 +1,4 @@ alt-text: Shape the future by taking the TiddlyWiki Community Survey 2025 tags: picture -title: Community Survey 2025 +title: Community Survey 2025 Image type: image/webp \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/images/Intertwingled Innovations.webp b/editions/tw5.com/tiddlers/images/Intertwingled Innovations Image.webp similarity index 100% rename from editions/tw5.com/tiddlers/images/Intertwingled Innovations.webp rename to editions/tw5.com/tiddlers/images/Intertwingled Innovations Image.webp diff --git a/editions/tw5.com/tiddlers/images/Intertwingled Innovations Image.webp.meta b/editions/tw5.com/tiddlers/images/Intertwingled Innovations Image.webp.meta new file mode 100644 index 000000000..20dd660ca --- /dev/null +++ b/editions/tw5.com/tiddlers/images/Intertwingled Innovations Image.webp.meta @@ -0,0 +1,3 @@ +title: Intertwingled Innovations Image +type: image/webp +tags: picture diff --git a/editions/tw5.com/tiddlers/images/Intertwingled Innovations.webp.meta b/editions/tw5.com/tiddlers/images/Intertwingled Innovations.webp.meta deleted file mode 100644 index f074bf96e..000000000 --- a/editions/tw5.com/tiddlers/images/Intertwingled Innovations.webp.meta +++ /dev/null @@ -1,3 +0,0 @@ -title: Intertwingled Innovations -type: image/webp -tags: picture From f5b4b1781e4f0ba3c89ff678a2006653dc060f23 Mon Sep 17 00:00:00 2001 From: Mario Pietsch Date: Sun, 22 Mar 2026 09:31:38 +0100 Subject: [PATCH 23/28] Fix edit toolbar button spaceing (#9763) --- core/ui/EditTemplate/body-toolbar-button.tid | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/ui/EditTemplate/body-toolbar-button.tid b/core/ui/EditTemplate/body-toolbar-button.tid index 8a1fb24a1..63e754216 100644 --- a/core/ui/EditTemplate/body-toolbar-button.tid +++ b/core/ui/EditTemplate/body-toolbar-button.tid @@ -1,5 +1,7 @@ title: $:/core/ui/EditTemplate/body/toolbar/button +\whitespace trim + \define toolbar-button-icon() <$list From 1d915389d9d8075e573cf9a7979a49748a51767a Mon Sep 17 00:00:00 2001 From: Mario Pietsch Date: Sun, 22 Mar 2026 09:42:13 +0100 Subject: [PATCH 24/28] Gitignore for automated tests (#9761) * Add elements to gitignore, that are used for automated future test * Add playwright configuration for Edge users that have no Chrome installed * update tw-mcp setting --- .gitignore | 11 ++++++++++- playwright.config.js | 5 +++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 74f14dd11..31e35dff1 100644 --- a/.gitignore +++ b/.gitignore @@ -3,10 +3,19 @@ .vs/ .vscode/ .claude/ +# TiddlyWiki tmp/ output/ node_modules/ +$__StoryList.tid +# Playwright /test-results/ /playwright-report/ +/blob-report/ /playwright/.cache/ -$__StoryList.tid +/playwright/.auth/ +test-screenshots/ +test-output.txt +.playwright-mcp +# TiddlyWiki MPC +.tw-mcp \ No newline at end of file diff --git a/playwright.config.js b/playwright.config.js index 491679a6f..b48de06dc 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -40,6 +40,11 @@ module.exports = defineConfig({ { name: 'firefox', use: { ...devices['Desktop Firefox'] }, + }, + + { + name: 'edge', + use: { ...devices['Desktop Chrome'], channel: 'msedge' }, } ], }); From c4991a54a01da45e1194f462a91d589140acdd96 Mon Sep 17 00:00:00 2001 From: Mario Pietsch Date: Tue, 24 Mar 2026 09:46:03 +0100 Subject: [PATCH 25/28] Some minor changes to the filter docs (#9709) * Some minor changes to the filter docs * Fix wording in Intersection Filter Run Prefix documentation --- .../tiddlers/filters/syntax/Filter Expression.tid | 2 ++ .../tiddlers/filters/syntax/Filter Parameter.tid | 12 +++++++++++- .../tw5.com/tiddlers/filters/syntax/Filter Step.tid | 8 ++++---- .../syntax/Interchangeable Filter Run Prefixes.tid | 2 ++ .../syntax/Intersection Filter Run Prefix.tid | 11 ++++++++++- .../filters/syntax/Named Filter Run Prefix.tid | 10 ++++++++++ .../filters/syntax/Shortcut Filter Run Prefixes.tid | 6 +++--- .../filters/syntax/then Filter Run Prefix.tid | 2 +- 8 files changed, 43 insertions(+), 10 deletions(-) diff --git a/editions/tw5.com/tiddlers/filters/syntax/Filter Expression.tid b/editions/tw5.com/tiddlers/filters/syntax/Filter Expression.tid index 739be62bf..3e0c75e9f 100644 --- a/editions/tw5.com/tiddlers/filters/syntax/Filter Expression.tid +++ b/editions/tw5.com/tiddlers/filters/syntax/Filter Expression.tid @@ -19,3 +19,5 @@ A <<.def "filter expression">> is the outermost level of the [[filter syntax|Fil <<.tip """If the diagram has a single start and end line, as shown above, it means there is more info in the linked level above. The breadcrumbs can be used to navigate""">> <<.tip """If the diagram has no start and no end, as used in lower levels, it means that higher level syntax elements have been removed, to increase readability and simplicity. The breadcrumbs can be used to navigate""">> + +<<.note """Filter expressions have a maximum recursion depth of 300. Filters that recursively invoke themselves (e.g. via the <<.olink subfilter>> or <<.olink filter>> operators) beyond this limit will return the error message `/**-- Excessive filter recursion --**/`. This protects against infinite loops.""">> diff --git a/editions/tw5.com/tiddlers/filters/syntax/Filter Parameter.tid b/editions/tw5.com/tiddlers/filters/syntax/Filter Parameter.tid index 0cfb83d5c..ab9840146 100644 --- a/editions/tw5.com/tiddlers/filters/syntax/Filter Parameter.tid +++ b/editions/tw5.com/tiddlers/filters/syntax/Filter Parameter.tid @@ -12,6 +12,8 @@ type: text/vnd.tiddlywiki "{" [: <-"indirect"-> /"anything but }"/] "}" | "<" [: <-"variable"-> /"anything but >"/] ">" + | + "(" [: <-"multi-valued variable"-> /"anything but )"/ ] ")" /"v5.4.0"/ ) """/> @@ -20,6 +22,7 @@ The parameter to a [[filter operator|Filter Operators]] can be: ;<<.def hard>> : `[like this]` : The parameter is the exact text that appears between the square brackets. + ;<<.def soft>> : <<.def indirect>> :: `{like this}` @@ -28,6 +31,9 @@ The parameter to a [[filter operator|Filter Operators]] can be: :: `` :: The parameter is the current value of the [[variable|Variables]] whose name appears between the angle brackets. Macro parameters are <<.em not>> supported up to v5.2.0 ::<<.from-version "5.2.0">> Literal macro parameters are supported. For example: `[]`. +: <<.def "multi-valued variable">> +:: `(like this)` +:: <<.from-version "5.4.0">> The parameter expands to the complete list of values stored in the [[multi-valued variable|Multi-Valued Variables]] whose name appears between the round brackets. When accessed this way, the filter operator receives all values, not just the first. See [[Multi-Valued Variables]] for details. <<.note """Every [[filter Operator]] must be followed by a parameter expression. In the case of [[Operators without parameters]], that expression is empty, as with the filter Operator <<.olink links>> in `[links[]]`.""">> @@ -35,4 +41,8 @@ The parameter to a [[filter operator|Filter Operators]] can be: <<.from-version "5.1.23">> [[Filter Step]]s support multiple parameters which are separated by a `,` character. -For example: `[param1],[param2]` or `,{param2}` +For example: `[param1],[param2]` or `,{param2}` or `[param1],(param2)` + +--- + +<<.warning """The `/regexp/(flags)` operand syntax is deprecated and will log a warning to the browser console. Use the <<.olink regexp>> operator instead.""">> diff --git a/editions/tw5.com/tiddlers/filters/syntax/Filter Step.tid b/editions/tw5.com/tiddlers/filters/syntax/Filter Step.tid index 12a4f4415..a81005343 100644 --- a/editions/tw5.com/tiddlers/filters/syntax/Filter Step.tid +++ b/editions/tw5.com/tiddlers/filters/syntax/Filter Step.tid @@ -18,17 +18,17 @@ In programming terms, it is akin to a function call to which the step's input is { [[parameter|"Filter Parameter"]] + "," } """/> -The step's <<.def operator>> is drawn from a list of predefined keywoards which are known as [[filter operators|Filter Operators]]. +The step's <<.def operator>> is drawn from a list of predefined keywords which are known as [[filter operators|Filter Operators]]. Many steps require an explicit <<.def parameter>>, that further defines what the step is to do. -The <<.def suffix>> is additional text, often the name of a [[field|TiddlerFields]], that extends the meaning of certain operators. +The <<.def suffix>> is additional text, often the name of a [[field|TiddlerFields]], that extends the meaning of certain operators. Suffixes are separated by `:` characters, and each suffix group can contain multiple comma-separated values. For example, in `compare:number:gteq`, the first suffix is `number` and the second is `gteq`. In `:sort:string:reverse,casesensitive`, the first suffix is `string` and the second suffix group contains both `reverse` and `casesensitive`. -If a step's <<.def operator>> and <<.def suffix>> are //omitted// altogether, it defaults to the [[title|title Operator]] operator. +If a step's <<.def operator>> and <<.def suffix>> are //omitted// altogether, it defaults to the [[title|title Operator]] operator. If a suffix is present but the operator name before the colon is empty (e.g. `[:fieldname[value]]`), the operator defaults to <<.olink field>>. <<.from-version "5.1.23">> Some steps accept multiple <<.def parameter>>s which are separated by a `,` character. -Any unrecognised operator is treated as if it was the suffix to the <<.olink field>> operator. +Any unrecognised operator is treated as if it was the suffix to the <<.olink field>> operator. <<.from-version "5.3.0">> However, unrecognised operators whose name contains a `.` (dot) character are treated as user-defined filter operators, resolved by looking up a variable or function with that name. Filter operators can be extended by plugins. diff --git a/editions/tw5.com/tiddlers/filters/syntax/Interchangeable Filter Run Prefixes.tid b/editions/tw5.com/tiddlers/filters/syntax/Interchangeable Filter Run Prefixes.tid index 8eb9c7330..e0504a342 100644 --- a/editions/tw5.com/tiddlers/filters/syntax/Interchangeable Filter Run Prefixes.tid +++ b/editions/tw5.com/tiddlers/filters/syntax/Interchangeable Filter Run Prefixes.tid @@ -43,3 +43,5 @@ For the difference between `+` and `:intersection`, see [[Intersection Filter Ru !! For Developers To create a new filter run prefix, create a [[Javascript module|Modules]] with a [[module-type|ModuleType]] of `filterrunprefix`. + +Compiled filter expressions are cached for performance. The cache holds up to 2000 entries and is reset in its entirety when this limit is exceeded. diff --git a/editions/tw5.com/tiddlers/filters/syntax/Intersection Filter Run Prefix.tid b/editions/tw5.com/tiddlers/filters/syntax/Intersection Filter Run Prefix.tid index 2f2f718b3..c2adcef42 100644 --- a/editions/tw5.com/tiddlers/filters/syntax/Intersection Filter Run Prefix.tid +++ b/editions/tw5.com/tiddlers/filters/syntax/Intersection Filter Run Prefix.tid @@ -15,6 +15,15 @@ type: text/vnd.tiddlywiki [[run|"Filter Run"]] """/> -The filter output from previous runs is set aside. The `:intersection` filter run is started with all tiddler titles as input. Once this latest filter run has completed, the latest output is compared to the set-aside output. A new output is produced that contains only titles that appeared in both the set-aside output and the latest output. +The filter output from previous runs is set aside. The `:intersection` filter run is started with all tiddler titles as input. Once this filter run has completed, the output is compared to the set-aside output. A new output is produced that contains only titles that appeared in both the set-aside output and the latest output. The order of titles in the output is preserved from the accumulated results. + +!! Difference from `:and` / `+` + +The key difference between `:intersection` and `:and` (or `+`) is what they receive as ''input'': + +* `:and` / `+` feeds the ''accumulated results so far'' as input to the filter run. Operators in the run can only see and work with titles already in the results. +* `:intersection` feeds ''all tiddler titles'' as input to the filter run (just like an unprefixed run), then keeps only titles that also appear in the accumulated results. + +This means `:intersection` can match titles using operators that need to start from the full tiddler pool. See [[Interchangeable Filter Run Prefixes]] for more details. [[Intersection Filter Run Prefix (Examples)]] diff --git a/editions/tw5.com/tiddlers/filters/syntax/Named Filter Run Prefix.tid b/editions/tw5.com/tiddlers/filters/syntax/Named Filter Run Prefix.tid index 0706e4cff..d631bcbf9 100644 --- a/editions/tw5.com/tiddlers/filters/syntax/Named Filter Run Prefix.tid +++ b/editions/tw5.com/tiddlers/filters/syntax/Named Filter Run Prefix.tid @@ -34,4 +34,14 @@ A named filter run prefix can precede any [[run|Filter Run]] of a [[filter expre <<.tip """Within the filter runs prefixed with `:reduce`, `:sort`, `:map` and `:filter`, the <<.var currentTiddler>> variable is set to the title of the tiddler being processed.
    The value of currentTiddler outside the subfilter is available in the variable <<.var "..currentTiddler">> <<.from-version "5.2.0">>""" >> +!! Suffixes + +Named filter run prefixes can accept suffixes separated by `:` characters, each optionally containing comma-separated values. The general syntax is `:prefixname:suffix1:suffix2,...`. Currently the following prefixes accept suffixes: + +|!Prefix |!Suffixes |!Example | +|`:map` |`flat` — return all results instead of only the first per item |`:map:flat[...]` | +|`:sort` |type (''string'', ''alphanumeric'', ''number'', ''integer'', ''version'', ''date'') and flags (''reverse'', ''casesensitive'', ''caseinsensitive'') |`:sort:number:reverse[...]` | + +All other named prefixes (`:all`, `:and`, `:cascade`, `:else`, `:except`, `:filter`, `:intersection`, `:let`, `:or`, `:reduce`, `:then`) do not currently accept suffixes. + Also see: [[Interchangeable Filter Run Prefixes]] diff --git a/editions/tw5.com/tiddlers/filters/syntax/Shortcut Filter Run Prefixes.tid b/editions/tw5.com/tiddlers/filters/syntax/Shortcut Filter Run Prefixes.tid index bf4326c41..a1c6adb29 100644 --- a/editions/tw5.com/tiddlers/filters/syntax/Shortcut Filter Run Prefixes.tid +++ b/editions/tw5.com/tiddlers/filters/syntax/Shortcut Filter Run Prefixes.tid @@ -21,10 +21,10 @@ If a run has: * the prefix `-`, output titles are <<.em removed>> from the filter's output (if such tiddlers exist) -* the prefix `~`, if the filter output so far is an empty list then the output titles of the run are [[dominantly appended|Dominant Append]] to the filter's output. If the filter output so far is not an empty list then the run is ignored. <<.from-version "5.1.18">> +* the prefix `~`, <<.from-version "5.1.18">> if the filter output so far is an empty list then the output titles of the run are [[dominantly appended|Dominant Append]] to the filter's output. If the filter output so far is not an empty list then the run is ignored. -* the prefix `=`, output titles are appended to the filter's output without de-duplication. <<.from-version "5.1.20">> +* the prefix `=`, <<.from-version "5.1.20">> output titles are appended to the filter's output without de-duplication. -* the prefix `=>`, the input is assigned to the variable named with the output title. <<.from-version "5.4.0">> +* the prefix `=>`, <<.from-version "5.4.0">> the accumulated results from all previous runs are assigned as a [[multi-valued variable|Multi-Valued Variables]] whose name is the first result of this run. The accumulated results are then cleared. {{Interchangeable Filter Run Prefixes}} \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/filters/syntax/then Filter Run Prefix.tid b/editions/tw5.com/tiddlers/filters/syntax/then Filter Run Prefix.tid index 02d8de0bd..57647a72c 100644 --- a/editions/tw5.com/tiddlers/filters/syntax/then Filter Run Prefix.tid +++ b/editions/tw5.com/tiddlers/filters/syntax/then Filter Run Prefix.tid @@ -1,7 +1,7 @@ created: 20210618133745003 from-version: 5.3.0 modified: 20230710074225410 -rp-input: <<.olink all>> tiddler titles +rp-input: <<.olink all>> tiddler titles (only evaluated when the output from previous runs is non-empty) rp-output: the output of this filter run replaces the output of previous runs unless it is an empty list (see below) rp-purpose: replace any input to this filter run with its output, only evaluating the run when there is any input search: From aff5be71953b2dbe869becb1b1427e443758537a Mon Sep 17 00:00:00 2001 From: Mario Pietsch Date: Tue, 24 Mar 2026 09:48:32 +0100 Subject: [PATCH 26/28] fix flaky test runner (#9718) --- .../test/tiddlers/tests/test-backlinks.js | 2 +- .../tiddlers/tests/test-backtranscludes.js | 26 ++++---- .../tiddlers/tests/test-checkbox-widget.js | 2 +- .../tiddlers/tests/test-compare-filter.js | 2 +- editions/test/tiddlers/tests/test-filters.js | 2 +- .../test/tiddlers/tests/test-json-filters.js | 2 +- .../tiddlers/tests/test-parsetextreference.js | 2 +- .../tiddlers/tests/test-prefixes-filter.js | 4 +- editions/test/tiddlers/tests/test-utils.js | 2 +- .../test/tiddlers/tests/test-widget-event.js | 18 +++--- editions/test/tiddlers/tests/test-widget.js | 62 +++++++++---------- .../tiddlers/tests/test-wikitext-parser.js | 2 +- editions/test/tiddlers/tests/test-wikitext.js | 2 +- plugins/tiddlywiki/jasmine/jasmine-plugin.js | 10 +++ .../jasmine/run-wiki-based-tests.js | 2 +- 15 files changed, 75 insertions(+), 65 deletions(-) diff --git a/editions/test/tiddlers/tests/test-backlinks.js b/editions/test/tiddlers/tests/test-backlinks.js index eac003926..c896ff903 100644 --- a/editions/test/tiddlers/tests/test-backlinks.js +++ b/editions/test/tiddlers/tests/test-backlinks.js @@ -28,7 +28,7 @@ describe("Backlinks tests", function() { } describe("a tiddler with no links to it", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); wiki.addTiddler({ title: "TestIncoming", diff --git a/editions/test/tiddlers/tests/test-backtranscludes.js b/editions/test/tiddlers/tests/test-backtranscludes.js index 3f9873f90..4559c5dd0 100644 --- a/editions/test/tiddlers/tests/test-backtranscludes.js +++ b/editions/test/tiddlers/tests/test-backtranscludes.js @@ -10,7 +10,7 @@ Tests the backtranscludes mechanism. describe("Backtranscludes and transclude filter tests", function() { describe("a tiddler with no transcludes to it", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); wiki.addTiddler({ title: "TestIncoming", @@ -25,7 +25,7 @@ describe("Backtranscludes and transclude filter tests", function() { }); describe("A tiddler added to the wiki with a transclude to it", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); wiki.addTiddler({ title: "TestIncoming", @@ -44,7 +44,7 @@ describe("Backtranscludes and transclude filter tests", function() { }); describe("A tiddler transclude with template will still use the tiddler as result.", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); wiki.addTiddler({ title: "TestIncoming", @@ -60,7 +60,7 @@ describe("Backtranscludes and transclude filter tests", function() { }); describe("A data tiddler transclude will still use the tiddler as result.", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); wiki.addTiddler({ title: "TestIncoming", @@ -81,7 +81,7 @@ describe("Backtranscludes and transclude filter tests", function() { describe("A tiddler that has a transclude added to it later", function() { it("should have an additional backtransclude", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); wiki.addTiddler({ title: "TestIncoming", @@ -106,7 +106,7 @@ describe("Backtranscludes and transclude filter tests", function() { }); describe("A tiddler that has a transclude remove from it later", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); wiki.addTiddler({ title: "TestIncoming", @@ -128,7 +128,7 @@ describe("Backtranscludes and transclude filter tests", function() { }); describe("A tiddler transcludeing to another that gets renamed", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); wiki.addTiddler({ title: "TestIncoming", @@ -148,7 +148,7 @@ describe("Backtranscludes and transclude filter tests", function() { }); describe("A tiddler transcludeing to another that gets deleted", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); wiki.addTiddler({ title: "TestIncoming", @@ -168,7 +168,7 @@ describe("Backtranscludes and transclude filter tests", function() { }); describe("a tiddler with some transcludes on it in order", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); wiki.addTiddler({ title: "TestOutgoing", @@ -186,7 +186,7 @@ describe("Backtranscludes and transclude filter tests", function() { }); describe("include implicit self transclusion", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); wiki.addTiddler({ title: "TestOutgoing", @@ -202,7 +202,7 @@ describe("Backtranscludes and transclude filter tests", function() { }); describe("include explicit self transclusion", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); wiki.addTiddler({ title: "TestOutgoing", @@ -218,7 +218,7 @@ describe("Backtranscludes and transclude filter tests", function() { }); describe("exclude self when target tiddler is not string", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); wiki.addTiddler({ title: "TestOutgoing", @@ -234,7 +234,7 @@ describe("Backtranscludes and transclude filter tests", function() { }); describe("recognize transclusion defined by widget", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); wiki.addTiddler({ title: "TestOutgoing", diff --git a/editions/test/tiddlers/tests/test-checkbox-widget.js b/editions/test/tiddlers/tests/test-checkbox-widget.js index 8ecfdd667..719641295 100644 --- a/editions/test/tiddlers/tests/test-checkbox-widget.js +++ b/editions/test/tiddlers/tests/test-checkbox-widget.js @@ -534,7 +534,7 @@ describe("Checkbox widget", function() { it("checkbox widget test: " + data.testName, function() { // Setup - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); wiki.addTiddlers(data.tiddlers); var widgetNode = createWidgetNode(parseText(data.widgetText,wiki),wiki); renderWidgetNode(widgetNode); diff --git a/editions/test/tiddlers/tests/test-compare-filter.js b/editions/test/tiddlers/tests/test-compare-filter.js index 64f6915f4..055ef7710 100644 --- a/editions/test/tiddlers/tests/test-compare-filter.js +++ b/editions/test/tiddlers/tests/test-compare-filter.js @@ -13,7 +13,7 @@ Tests the compare filter. describe("'compare' filter tests", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); it("should compare numerical equality", function() { expect(wiki.filterTiddlers("[[2]compare:number:eq[0003]]").join(",")).toBe(""); diff --git a/editions/test/tiddlers/tests/test-filters.js b/editions/test/tiddlers/tests/test-filters.js index 6587e33e2..9834090c7 100644 --- a/editions/test/tiddlers/tests/test-filters.js +++ b/editions/test/tiddlers/tests/test-filters.js @@ -583,7 +583,7 @@ describe("Filter tests", function() { }); it("should handle the '[is[draft]]' operator", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); wiki.addTiddlers([ {title: "A"}, {title: "Draft of 'A'", "draft.of": "A", "draft.title": "A"}, diff --git a/editions/test/tiddlers/tests/test-json-filters.js b/editions/test/tiddlers/tests/test-json-filters.js index 5f7ae5756..1232e5cf8 100644 --- a/editions/test/tiddlers/tests/test-json-filters.js +++ b/editions/test/tiddlers/tests/test-json-filters.js @@ -13,7 +13,7 @@ Tests the JSON filters and the format:json operator describe("json filter tests", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); var tiddlers = [{ title: "First", text: '{"a":"one","b":"","c":1.618,"d": {"e": "four","f": ["five","six",true,false,null]}}', diff --git a/editions/test/tiddlers/tests/test-parsetextreference.js b/editions/test/tiddlers/tests/test-parsetextreference.js index 3c18b90fb..c767e3dd3 100644 --- a/editions/test/tiddlers/tests/test-parsetextreference.js +++ b/editions/test/tiddlers/tests/test-parsetextreference.js @@ -12,7 +12,7 @@ Tests for source attribute in parser returned from wiki.parseTextReference describe("Wiki.parseTextReference tests", function() { // Create a wiki - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); wiki.addTiddler({ title: "TiddlerOne", text: "The quick brown fox in $:/TiddlerTwo", diff --git a/editions/test/tiddlers/tests/test-prefixes-filter.js b/editions/test/tiddlers/tests/test-prefixes-filter.js index 5faf96d51..147fb4e5b 100644 --- a/editions/test/tiddlers/tests/test-prefixes-filter.js +++ b/editions/test/tiddlers/tests/test-prefixes-filter.js @@ -14,7 +14,7 @@ Tests the reduce prefix and filter. describe("general filter prefix tests", function() { it("should handle nonexistent prefixes gracefully", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); var results = wiki.filterTiddlers("[tag[A]] :nonexistent[tag[B]]"); expect(results).toEqual(["Filter Error: Unknown prefix for filter run"]); }); @@ -215,7 +215,7 @@ describe("general filter prefix tests", function() { describe("'reduce' and 'intersection' filter prefix tests", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); wiki.addTiddler({ title: "Brownies", diff --git a/editions/test/tiddlers/tests/test-utils.js b/editions/test/tiddlers/tests/test-utils.js index 0b3c05ddd..d0b7d96fe 100644 --- a/editions/test/tiddlers/tests/test-utils.js +++ b/editions/test/tiddlers/tests/test-utils.js @@ -82,7 +82,7 @@ describe("Utility tests", function() { }); it("stringifyList shouldn't interfere with setting variables to negative numbers", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); wiki.addTiddler({title: "test", text: "<$set name=X filter='\"-7\"'>{{{ [add[2]] }}}"}); // X shouldn't be wrapped in brackets. If it is, math filters will treat it as zero. expect(wiki.renderTiddler("text/plain","test")).toBe("-5"); diff --git a/editions/test/tiddlers/tests/test-widget-event.js b/editions/test/tiddlers/tests/test-widget-event.js index 9aeb9a838..7d7afd7f8 100644 --- a/editions/test/tiddlers/tests/test-widget-event.js +++ b/editions/test/tiddlers/tests/test-widget-event.js @@ -19,7 +19,7 @@ describe("Widget Event Listeners", function() { it("should call all added event listeners on dispatchEvent", function() { var calls = []; - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); var widget = createWidgetNode({type:"widget", text:"text"}, wiki); // Add a function listener. @@ -44,7 +44,7 @@ describe("Widget Event Listeners", function() { it("should remove an event listener correctly", function() { var calls = []; - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); var widget = createWidgetNode({type:"widget", text:"text"}, wiki); function listener(e) { @@ -70,7 +70,7 @@ describe("Widget Event Listeners", function() { it("stop further propagation by returns false won't block other listeners on the same level.", function() { var calls = []; - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); var widget = createWidgetNode({type:"widget", text:"text"}, wiki); widget.addEventListener("stopEvent", function(e) { @@ -92,7 +92,7 @@ describe("Widget Event Listeners", function() { it("should dispatch event to parent widget if not handled on child", function() { var parentCalls = []; - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); var parentWidget = createWidgetNode({type:"widget", text:"text"}, wiki); parentWidget.addEventListener("parentEvent", function(e) { parentCalls.push("parentListener"); @@ -110,7 +110,7 @@ describe("Widget Event Listeners", function() { it("should not dispatch event to parent if child's listener stops propagation", function() { var parentCalls = []; - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); var parentWidget = createWidgetNode({type:"widget", text:"text"}, wiki); parentWidget.addEventListener("bubbleTest", function(e) { parentCalls.push("parentListener"); @@ -128,7 +128,7 @@ describe("Widget Event Listeners", function() { it("should call multiple listeners in proper order across child and parent", function() { var calls = []; - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); var parentWidget = createWidgetNode({type:"widget", text:"text"}, wiki); parentWidget.addEventListener("chainEvent", function(e) { calls.push("parentListener"); @@ -152,7 +152,7 @@ describe("Widget Event Listeners", function() { it("should handle events of different types separately", function() { var callsA = []; var callsB = []; - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); var widget = createWidgetNode({type:"widget", text:"text"}, wiki); widget.addEventListener("eventA", function(e) { callsA.push("A1"); @@ -171,7 +171,7 @@ describe("Widget Event Listeners", function() { // Test using $tw.utils.each in removeEventListener internally (behavior verified via dispatch) it("should remove listeners using $tw.utils.each without affecting other listeners", function() { var calls = []; - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); var widget = createWidgetNode({type:"widget", text:"text"}, wiki); function listener1(e) { calls.push("listener1"); @@ -192,7 +192,7 @@ describe("Widget Event Listeners", function() { it("should prevent adding the same event listener multiple times", function() { var calls = 0; - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); var widget = createWidgetNode({type:"widget", text:"text"}, wiki); function listener(e) { diff --git a/editions/test/tiddlers/tests/test-widget.js b/editions/test/tiddlers/tests/test-widget.js index 54e8fc1a9..444672d9b 100755 --- a/editions/test/tiddlers/tests/test-widget.js +++ b/editions/test/tiddlers/tests/test-widget.js @@ -45,7 +45,7 @@ describe("Widget module", function() { } it("should deal with text nodes and HTML elements", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Test parse tree var parseTreeNode = {type: "widget", children: [ {type: "text", text: "A text node"}, @@ -77,7 +77,7 @@ describe("Widget module", function() { }); it("should deal with transclude widgets and indirect attributes", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Add a tiddler wiki.addTiddlers([ {title: "TiddlerOne", text: "the quick brown fox"} @@ -137,7 +137,7 @@ describe("Widget module", function() { }); it("should detect recursion of the transclude macro", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Add a tiddler wiki.addTiddlers([ {title: "TiddlerOne", text: "<$transclude tiddler='TiddlerTwo'/>"}, @@ -158,7 +158,7 @@ describe("Widget module", function() { }); it("should handle single-tiddler recursion with branching nodes", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Add a tiddler wiki.addTiddlers([ {title: "TiddlerOne", text: "<$tiddler tiddler='TiddlerOne'><$transclude /> <$transclude />"}, @@ -182,7 +182,7 @@ describe("Widget module", function() { // end up being the same value for all iterations of the test. $tw.utils.each(["div","$button","$checkbox","$diff-text","$draggable","$droppable","dropzone","$eventcatcher","$keyboard","$link","$list filter=x variable=x","$radio","$reveal type=nomatch","$scrollable","$select","$view field=x"],function(tag) { it(`${tag} cleans itself up if children rendering fails`, function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); wiki.addTiddler({title: "TiddlerOne", text: `<$tiddler tiddler='TiddlerOne'><${tag}><$transclude />`}); var parseTreeNode = {type: "widget", children: [ {type: "transclude", attributes: { @@ -204,7 +204,7 @@ describe("Widget module", function() { }); it("should handle many-tiddler recursion with branching nodes", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Add a tiddler wiki.addTiddlers([ {title: "TiddlerOne", text: "<$transclude tiddler='TiddlerTwo'/> <$transclude tiddler='TiddlerTwo'/>"}, @@ -225,7 +225,7 @@ describe("Widget module", function() { }); it("should deal with SVG elements", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Construct the widget node var text = "\n"; var widgetNode = createWidgetNode(parseText(text,wiki,{parseAsInline:true}),wiki); @@ -237,7 +237,7 @@ describe("Widget module", function() { }); it("should parse and render transclusions", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Add a tiddler wiki.addTiddlers([ {title: "TiddlerOne", text: "Jolly Old World"}, @@ -254,7 +254,7 @@ describe("Widget module", function() { }); it("should render the view widget", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Add a tiddler wiki.addTiddlers([ {title: "TiddlerOne", text: "Jolly Old World"} @@ -283,7 +283,7 @@ describe("Widget module", function() { }); it("should deal with the set widget", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Add some tiddlers wiki.addTiddlers([ {title: "TiddlerOne", text: "Jolly Old World"}, @@ -313,7 +313,7 @@ describe("Widget module", function() { }); it("should deal with the let widget", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); wiki.addTiddlers([ {title: "TiddlerOne", text: "lookup"}, {title: "TiddlerTwo", lookup: "value", newlookup: "value", wrong: "wrong"}, @@ -347,7 +347,7 @@ describe("Widget module", function() { }); it("should deal with attributes specified as macro invocations", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Construct the widget node var text = "\\define myMacro(one:\"paramOne\",two,three:\"paramTwo\")\nMy something $one$, $two$ or other $three$\n\\end\n
    >>Content
    "; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); @@ -358,7 +358,7 @@ describe("Widget module", function() { }); it("should deal with built-in macros", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Add some tiddlers wiki.addTiddlers([ {title: "TiddlerOne", text: "Jolly Old World", type: "text/vnd.tiddlywiki"} @@ -374,7 +374,7 @@ describe("Widget module", function() { /* This test reproduces issue #4693. */ it("should render the entity widget", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Construct the widget node var text = "\n\n<$entity entity=' ' />\n\n<$entity entity='✓' />\n"; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); @@ -391,7 +391,7 @@ describe("Widget module", function() { }); it("should deal with the list widget", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Add some tiddlers wiki.addTiddlers([ {title: "TiddlerOne", text: "Jolly Old World"}, @@ -451,7 +451,7 @@ describe("Widget module", function() { it("should deal with the list widget using a counter variable", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Add some tiddlers wiki.addTiddlers([ {title: "TiddlerOne", text: "Jolly Old World"}, @@ -593,7 +593,7 @@ describe("Widget module", function() { var testListJoin = function(oldList, newList) { return function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Add some tiddlers wiki.addTiddler({title: "Numbers", text: "", list: oldList}); var text = "<$list filter='[list[Numbers]]' variable='item' join=', '><>"; @@ -632,7 +632,7 @@ describe("Widget module", function() { var testCounterLast = function(oldList, newList) { return function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Add some tiddlers wiki.addTiddler({title: "Numbers", text: "", list: oldList}); var text = "<$list filter='[list[Numbers]]' variable='item' counter='c'><><$text text={{{ [match[no]then[, ]] }}} />"; @@ -654,7 +654,7 @@ describe("Widget module", function() { it("the list widget with counter-last should update correctly when first item is removed", testCounterLast("1 2 3 4", "2 3 4")); it("should deal with the list widget followed by other widgets", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Add some tiddlers wiki.addTiddlers([ {title: "TiddlerOne", text: "Jolly Old World"}, @@ -727,7 +727,7 @@ describe("Widget module", function() { }); it("should deal with the list widget and external templates", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Add some tiddlers wiki.addTiddlers([ {title: "$:/myTemplate", text: "(<$view field='title'/>)"}, @@ -747,7 +747,7 @@ describe("Widget module", function() { }); it("should deal with the list widget and empty lists", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Construct the widget node var text = "<$list emptyMessage='nothing'><$view field='title'/>"; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); @@ -758,7 +758,7 @@ describe("Widget module", function() { }); it("should refresh lists that become empty", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Add some tiddlers wiki.addTiddlers([ {title: "TiddlerOne", text: "Jolly Old World"}, @@ -788,7 +788,7 @@ describe("Widget module", function() { * if they use transclusion for their value. This relates to PR #4108. */ it("should refresh imported <$set> widgets", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Add some tiddlers wiki.addTiddlers([ {title: "Raw", text: "Initial value"}, @@ -808,7 +808,7 @@ describe("Widget module", function() { }); it("should support mixed setWidgets and macros when importing", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Add some tiddlers wiki.addTiddlers([ {title: "A", text: "\\define A() Aval"}, @@ -824,7 +824,7 @@ describe("Widget module", function() { }); it("should skip parameters widgets when importing", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Add some tiddlers wiki.addTiddlers([ {title: "B", text: "<$parameters bee=nothing><$set name='B' value='Bval'>\n\ndummy text"}, @@ -838,7 +838,7 @@ describe("Widget module", function() { }); it("should use default $parameters if directly rendered", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); var text = "<$parameters bee=default $$dollar=bill nothing empty=''>bee=<>, $dollar=<<$dollar>>, nothing=<>, empty=<>"; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); // Render the widget node to the DOM @@ -848,7 +848,7 @@ describe("Widget module", function() { }); it("should use default \\parameters if directly rendered", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); var text = "\\parameters(bee:default $$dollar:bill nothing)\nbee=<>, $$dollar=<<$$dollar>>, nothing=<>"; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); // Render the widget node to the DOM @@ -858,7 +858,7 @@ describe("Widget module", function() { }); it("can have more than one macroDef variable imported", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); wiki.addTiddlers([ {title: "ABC", text: "<$set name=A value=A>\n\n<$set name=B value=B>\n\n<$set name=C value=C>\n\ndummy text"}, {title: "D", text: "\\define D() D"}]); @@ -911,7 +911,7 @@ describe("Widget module", function() { * doesn't forget its childrenNodes. */ it("should work when import widget imports nothing", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); var text = "\\import [prefix[XXX]]\nDon't forget me."; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); // Render the widget node to the DOM @@ -925,7 +925,7 @@ describe("Widget module", function() { * visual difference, but may affect plugins if it doesn't. */ it("should work when import pragma is standalone", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); var text = "\\import [prefix[XXX]]"; var parseTreeNode = parseText(text,wiki); // Test the resulting parse tree node, since there is no @@ -944,7 +944,7 @@ describe("Widget module", function() { * at least ONE variable. */ it("adding imported variables doesn't change qualifyers", function() { - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); function wikiparse(text) { var tree = parseText(text,wiki); var widgetNode = createWidgetNode(tree,wiki); diff --git a/editions/test/tiddlers/tests/test-wikitext-parser.js b/editions/test/tiddlers/tests/test-wikitext-parser.js index a150047d0..2808c91de 100644 --- a/editions/test/tiddlers/tests/test-wikitext-parser.js +++ b/editions/test/tiddlers/tests/test-wikitext-parser.js @@ -12,7 +12,7 @@ Tests for wikitext parser describe("WikiText parser tests", function() { // Create a wiki - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Define a parsing shortcut var parse = function(text) { diff --git a/editions/test/tiddlers/tests/test-wikitext.js b/editions/test/tiddlers/tests/test-wikitext.js index c86cb90d7..d96a6fe93 100644 --- a/editions/test/tiddlers/tests/test-wikitext.js +++ b/editions/test/tiddlers/tests/test-wikitext.js @@ -12,7 +12,7 @@ Tests the wikitext rendering pipeline end-to-end. We also need tests that indivi describe("WikiText tests", function() { // Create a wiki - var wiki = new $tw.Wiki(); + var wiki = $tw.test.wiki(); // Add a couple of tiddlers wiki.addTiddler({title: "TiddlerOne", text: "The quick brown fox"}); wiki.addTiddler({title: "TiddlerTwo", text: "The rain in Spain\nfalls mainly on the plain"}); diff --git a/plugins/tiddlywiki/jasmine/jasmine-plugin.js b/plugins/tiddlywiki/jasmine/jasmine-plugin.js index af3e75300..bb415231f 100644 --- a/plugins/tiddlywiki/jasmine/jasmine-plugin.js +++ b/plugins/tiddlywiki/jasmine/jasmine-plugin.js @@ -143,6 +143,16 @@ exports.runTests = function(callback,specFilter) { var env = jasmine.getEnv(); var jasmineInterface = jasmineCore.interface(jasmine,env); context = $tw.utils.extend({},jasmineInterface,context); + // Initialise the WikiParser prototype with the correct rule config + // from the main wiki, before any test can trigger it from an empty wiki + $tw.wiki.parseText("text/vnd.tiddlywiki",""); + // Set up test utilities available to all test specs + $tw.test = { + /** Create a test wiki instance, pre-configured with core settings */ + wiki: function() { + return new $tw.Wiki(); + } + }; // Iterate through all the test modules var tests = $tw.wiki.filterTiddlers(TEST_TIDDLER_FILTER); $tw.utils.each(tests,evalInContext); diff --git a/plugins/tiddlywiki/jasmine/run-wiki-based-tests.js b/plugins/tiddlywiki/jasmine/run-wiki-based-tests.js index 6a067a744..8ad1f057e 100644 --- a/plugins/tiddlywiki/jasmine/run-wiki-based-tests.js +++ b/plugins/tiddlywiki/jasmine/run-wiki-based-tests.js @@ -19,7 +19,7 @@ describe("Wiki-based tests", function() { var tiddler = $tw.wiki.getTiddler(title); it(tiddler.fields.title + ": " + tiddler.fields.description, function() { // Add our tiddlers - var wiki = new $tw.Wiki(), + var wiki = $tw.test.wiki(), coreTiddler = $tw.wiki.getTiddler("$:/core"); if(coreTiddler) { wiki.addTiddler(coreTiddler); From 3a4a8a206ffac23f95f698a8896a21e4df806467 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Tue, 24 Mar 2026 11:19:17 +0000 Subject: [PATCH 27/28] Hotfix for broken test for dates Fixes #9726 --- boot/boot.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/boot/boot.js b/boot/boot.js index c726cd83c..421b93626 100644 --- a/boot/boot.js +++ b/boot/boot.js @@ -47,6 +47,13 @@ $tw.utils.hop = function(object,property) { /** @deprecated Use Array.isArray instead */ $tw.utils.isArray = (value) => Array.isArray(value); +/* +Determine if a value is a date, even across VM boundaries +*/ +$tw.utils.isDate = function(value) { + return Object.prototype.toString.call(value) === "[object Date]"; +}; + /* Check if an array is equal by value and by reference. */ From 78b6f6f4429afc8e99f86d4ae386a311e095e78a Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Tue, 24 Mar 2026 12:23:58 +0000 Subject: [PATCH 28/28] Docs: clarify parameters within calls in filters --- editions/tw5.com/tiddlers/procedures/calls/Calls.tid | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/editions/tw5.com/tiddlers/procedures/calls/Calls.tid b/editions/tw5.com/tiddlers/procedures/calls/Calls.tid index 5e07f861e..e1fd9d1fa 100644 --- a/editions/tw5.com/tiddlers/procedures/calls/Calls.tid +++ b/editions/tw5.com/tiddlers/procedures/calls/Calls.tid @@ -55,8 +55,10 @@ The text returned from a call can be directly assigned to an attribute of a widg Calls can be used in filters. The text is not wikified which again means that the parameters will be ignored. +//Note that currently only literal string parameters are supported// + ``` <$list filter="[]"> ... -``` \ No newline at end of file +```