1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2026-01-24 03:44:41 +00:00

Compare commits

...

78 Commits

Author SHA1 Message Date
Jeremy Ruston
ae68650e93 Docs updates 2025-11-22 12:25:59 +00:00
Jeremy Ruston
91fc7bb6cd Update let.js 2025-11-22 12:03:53 +00:00
Jeremy Ruston
5d8f613df8 Typo 2025-11-22 12:03:46 +00:00
Jeremy Ruston
5cce338206 Fix filenames of tests 2025-11-22 12:03:34 +00:00
Jeremy Ruston
1c1547fe59 Add change note 2025-11-22 11:37:54 +00:00
Jeremy Ruston
a5648f829d Merge branch 'master' into let-filter-run-prefix 2025-11-22 11:31:37 +00:00
Jeremy Ruston
5209f212f4 Another incorrect version number 2025-11-22 11:31:31 +00:00
Jeremy Ruston
f2d5c2d83d Fix version numbers of from-version procedures 2025-11-22 11:20:15 +00:00
Bram Chen
ed4a186c9c Add missing changelog for #9375 (#9456) 2025-11-21 09:50:21 +00:00
Maurycy Zarzycki
f16c0d769b Add support for text/tab-separated-values mimetype (#9445)
* Add support for `text/tab-separated-values` mimetype

* Fix eslint issues

* Add the release note for CSV parser change
2025-11-19 16:59:15 +00:00
Andrew Gregory
298508c104 Updates pluginmaker.js to add modification fields (#9401)
* Update pluginmaker.js

Update plugin modified timestamp unless disabled.

* Update pluginmaker.js

Use getModificationFields()

* Update pluginmaker.js

Inline Tiddler creation parameters

* Create #9401.tid

Create release notes tiddler

* Apply suggestion from @saqimtiaz

---------

Co-authored-by: Saq Imtiaz <saq.imtiaz@gmail.com>
2025-11-17 14:02:07 +01:00
XLBilly
f1e1532949 Updates change notes (#9413)
* Update missing change notes

* Update change notes

* Add date fields

* Update change notes

* Update change notes

* Add 9253 impact note

* Replace deprecation with performance

* Add change note for 9350
2025-11-17 12:31:08 +01:00
lin onetwo
3e1078eff1 Fix/serialize close html tag (#9437)
* fix: should use tree.isSelfClosing || isVoidElement

* docs: about html

* fix: Void element without self-closing slash (e.g., <br> instead of <br/>)

* Update editions/test/tiddlers/tests/data/serialize/VoidElements.tid

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-14 21:33:45 +00:00
XLBilly
cc348fee96 Further fix markup not included in external core edition (#9439)
* Further fix markup not included in external core edition

* Update change note

* Typo
2025-11-14 15:57:35 +00:00
XLBilly
24d45fd318 Replace palette category with theme (#9438) 2025-11-14 09:46:14 +00:00
Jeremy Ruston
9fd345ec06 Fixes for #9432 2025-11-13 12:00:21 +00:00
Arlen Beiler
30e9b4f3b7 Add change notes as requested in #9406 (#9435)
* Add change notes as requested in #9406

* fix username casing
2025-11-12 21:41:29 +00:00
lin onetwo
c6556d5207 Update PR validator to v6 (#9434)
* Update pr-validation.yml

* v6

* fix missing filter

* Revert "fix missing filter"

This reverts commit 9f132d8819.
2025-11-12 17:47:50 +00:00
yaisog
a8da7e0207 Add changenotes for PRs #9305 and #9337 2025-11-12 17:25:40 +00:00
Jeremy Ruston
d5762b1fbb Add "filters" to change note categories
https://github.com/TiddlyWiki/TiddlyWiki5/pull/9390#issuecomment-3523005885
2025-11-12 17:20:26 +00:00
aka James4u
2b0739f06e Adds jsondelete operator to fix #9371 (#9390)
* Added jsondelter to fix 9371

* Replacced deprecated utils.isArray with Array.isArray, Refactored to remove duplication between setDataItem() and getDataItem()

* added changenotes for #9371

* Update #9371.tid

Fix key word: links-> github-links

* changed change-category

* updated github-links

* Apply suggestion from @saqimtiaz

---------

Co-authored-by: Saq Imtiaz <saq.imtiaz@gmail.com>
2025-11-12 16:33:28 +01:00
lin onetwo
e339f112a4 Fix/Widget css leading space (#9427)
* fix: trim tc-reveal class

* test: " tc-reveal" -> "tc-reveal"

* Create #9427.tid

* fix: more widgets

* Update #9427.tid
2025-11-12 11:57:02 +00:00
Saq Imtiaz
7a9235e9d1 Revert "Create LinOnetwo.tid (#9425)" (#9428)
This reverts commit bbbc8c2c03.
2025-11-12 12:23:05 +01:00
lin onetwo
bbbc8c2c03 Create LinOnetwo.tid (#9425) 2025-11-11 23:20:25 +00:00
Jeremy Ruston
538482e9a9 Release note fixes 2025-11-11 16:53:02 +00:00
Jeremy Ruston
df7973fc3e Merge branch 'tiddlywiki-com' 2025-11-11 16:47:36 +00:00
XLBilly
ae79736e82 Deprecate some CSS property macros (#9242)
* Deprecate some CSS property macros

* Update docs

* Remove -ms and -o prefix

* Revert "Update docs"

This reverts commit cdf535054a.

* Update docs

* Update docs

* Update change note

* Update comment
2025-11-11 16:46:02 +00:00
Mario Pietsch
102c236267 Create PMario community card (#9417) 2025-11-11 16:43:59 +00:00
Saq Imtiaz
06a2923adf Adds edit.tiddlywiki.com to infrastructure team responsibilities (#9419) 2025-11-11 16:37:45 +00:00
cdruan
244251ed44 Submit @cdruan change notes (#9421)
For PR #9295 #9341 #9358.
2025-11-11 16:29:26 +00:00
XLBilly
9164f305e0 Remove Opera & Microsoft prefix in browser.js (#9422)
* Remove Opera & Microsoft prefix in browser.js

* Add change notes
2025-11-11 16:28:27 +00:00
lin onetwo
64f86c2187 ci: Uses TiddlyWiki/cerebrus@v5, and replaces obsolete workflow (#9423)
* refactor: use TiddlyWiki/cerebrus@v5, and rename yml

* continue-on-error: false

* Update .github/workflows/pr-validation.yml to restrict contents permissions

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Saq Imtiaz <saq.imtiaz@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-11 16:13:41 +01:00
Jeremy Ruston
54e5ef7489 Fix link 2025-11-11 11:41:27 +00:00
Jeremy Ruston
2d2ba61949 Revert "Bot to remind of change note (#9407)"
This reverts commit 845f988ab0.
2025-11-11 09:19:57 +00:00
Jeremy Ruston
0bbe170cf0 Merge branch 'tiddlywiki-com' 2025-11-10 10:22:16 +00:00
Jeremy Ruston
6f306d1ed6 Community Team Card for the Developer Experience Team
@pmario this is a starter list of, please do suggest improvements
2025-11-10 10:21:58 +00:00
lin onetwo
845f988ab0 Bot to remind of change note (#9407)
* let claude write this

* Update validate-changenotes.yml

* Update validate-changenotes.yml

* Update validate-changenotes.yml

* Update validate-changenotes.yml

* refactor: move logic to sh file
2025-11-10 09:08:29 +00:00
lin onetwo
234667cc31 Add @linonetwo’s missing change notes (#9409)
* docs

(cherry picked from commit eb5d2c50c0e45712eafda0b0e79bd1816b5f8a5b)
(cherry picked from commit eecfd35336964c756547f9ea622566f8bab02709)

* typo

(cherry picked from commit d9129bcaf3f4face613b3f6ccfe06ce6ed45b2d5)
2025-11-09 21:17:29 +00:00
Jeremy Ruston
f1ce35036e Release Notes: Introduce impact notes attached to change notes (#9385)
* Introduce impact notes attached to change notes

No styles yet

* Add more tickets for demo and testing purposes

* Slightly better description for v5.4.0

* Formatting. More to do.

* Embarrassing typo

* Update field names

* Styling refinements

* Disable the #8702 changenote as it is not yet merged

* Add incompleteness warning
2025-11-09 16:05:00 +00:00
Mario Pietsch
5dfdbc8ea0 [DOCS] Slightly improve the substitution documentation (#9357) 2025-11-07 19:27:20 +00:00
Jeremy Ruston
ac7837a98f Happy linter happy life 2025-11-06 22:45:26 +00:00
Jeremy Ruston
eaaa36fb41 Fix typos 2025-11-06 22:43:22 +00:00
Jeremy Ruston
d4684c2811 Merge branch 'master' into let-filter-run-prefix 2025-11-06 22:43:08 +00:00
XLBilly
4d4d9d9995 [DOCS] Add docs about deprecated classes (#9345)
* Add docs about deprecated classes

* Add Core classes tag

* Deprecate `tc-language-*` class

* Update deprecation warning

* 更新 Deprecated Core Classes.tid

Co-authored-by: Saq Imtiaz <saq.imtiaz@gmail.com>

* Apply suggestion from @saqimtiaz

---------

Co-authored-by: Saq Imtiaz <saq.imtiaz@gmail.com>
2025-11-04 13:03:38 +01:00
Jeremy Ruston
f2b60d4803 Docs updates 2025-04-30 17:03:40 +01:00
Jeremy Ruston
f371a59c75 Merge branch 'master' into let-filter-run-prefix 2025-04-30 17:03:00 +01:00
Jeremy Ruston
c6b499af98 Update action-log widget to log multi-valued attributes 2025-03-30 21:44:43 +01:00
Jeremy Ruston
f3f232e634 Remove duplicate test 2025-03-30 19:42:39 +01:00
Jeremy Ruston
2c50345653 Extend title operator to allow negated form to use multi-valued variables 2025-03-29 15:35:31 +00:00
Jeremy Ruston
9453e3e2e2 Simplify title operator 2025-03-29 14:45:35 +00:00
Jeremy Ruston
d20b02e606 Allow functions to take multivalued parameters 2025-03-29 14:13:23 +00:00
Jeremy Ruston
22eea56b53 Fix bug with round brackets for 2nd parameter onwards 2025-03-29 12:50:43 +00:00
Jeremy Ruston
e8a2b3eefa Merge branch 'master' into let-filter-run-prefix 2025-03-29 12:32:27 +00:00
Jeremy Ruston
b6a171cf5e Fix bug with missing variable attributes
See https://github.com/TiddlyWiki/TiddlyWiki5/pull/8972#issuecomment-2752792329
2025-03-26 20:32:35 +00:00
Jeremy Ruston
f15e9e309e Merge branch 'master' into let-filter-run-prefix 2025-03-21 17:23:27 +00:00
Jeremy Ruston
f914aa6605 Fix bug exposed in "Filter Operators" tiddler
See https://github.com/TiddlyWiki/TiddlyWiki5/pull/8972#issuecomment-2740003414
2025-03-20 17:46:12 +00:00
Jeremy Ruston
bb5061c6ff Introduce => as a shortcut syntax for the let filter run prefix
Also relax the requirement for a filter run prefix to be followed by an opening square bracket
2025-03-20 09:29:15 +00:00
Jeremy Ruston
0c24e2f5f1 Introduce round brackets for multi-valued filter operands
Allowing us to drop the varlist operator
2025-03-19 16:14:54 +00:00
Jeremy Ruston
938e72dc34 Bug fix 2025-03-19 11:00:03 +00:00
Jeremy Ruston
7640135a7e Docs update 2025-03-18 16:29:11 +00:00
Jeremy Ruston
ac134ac549 Missed off 211b135265 2025-03-18 15:54:51 +00:00
Jeremy Ruston
211b135265 Refactor the let filter run prefix to assign the input list to the variable named by the filter run
These semantics are much simpler, and allow the variable name to be computed.
2025-03-18 15:53:28 +00:00
Jeremy Ruston
0db188f365 Dispense with the letlist widget
What this PR actually does is rename the letlist widget to "let". The result is the same as using the letlist widget, but it is backwards compatible by virtue of the fact that all existing ways to access variables will only see the single value for the variable.
2025-03-18 12:46:31 +00:00
Jeremy Ruston
a31df85894 varlist operator: fixed crash accessing non-existent variable
See https://github.com/TiddlyWiki/TiddlyWiki5/pull/8972#issuecomment-2712068743
2025-03-11 13:59:19 +00:00
Jeremy Ruston
d69ecf4639 Test for multivalued functions 2025-03-11 13:30:17 +00:00
Jeremy Ruston
efce6731e1 Docs update 2025-03-11 12:49:40 +00:00
Jeremy Ruston
a9a9a745d4 Switch to triple brace syntax for assigning filtered lists 2025-03-11 12:47:01 +00:00
Jeremy Ruston
4cfa758d51 Fixed varlist crash with empty parameter 2025-03-10 15:31:28 +00:00
Jeremy Ruston
c5ad429493 Update DefaultTiddlers to highlight the new docs 2025-03-10 12:45:10 +00:00
Jeremy Ruston
76e1c2124b Docs update 2025-03-09 18:21:36 +00:00
Jeremy Ruston
78a7aedd4b Introduce letlist widget for assigning lists to variables
Terrible name. Annoyingly, we can't overload the existing let or set widgets.
2025-03-09 17:50:42 +00:00
Jeremy Ruston
2a1542c4e5 Docs update 2025-03-09 17:01:31 +00:00
Jeremy Ruston
7397f4fa3a Support for saving result lists in a variable
Extend let filter run prefix to store list of results, and add varlist operator for accessing variables as a list.

We already had partial support for variables returning a list of values in order for functions to work, now we extend it so that any variable can be used to store a list

We should extend the set widget so that it returns a result list that can be accessed with the varlist operator
2025-03-09 16:50:48 +00:00
Jeremy Ruston
e7b713c277 Cleanup 2025-03-09 14:56:15 +00:00
Jeremy Ruston
8e9b30fec8 Fix tests 2025-03-08 09:05:59 +00:00
Jeremy Ruston
db1ed0e66b Fix tests 2025-03-08 09:00:32 +00:00
Jeremy Ruston
1acef48a10 Get rid of the special behaviour for all[]
Not needed because the input to the filter run is available
2025-03-08 08:56:12 +00:00
Jeremy Ruston
b8bdb0aeeb Introduce let filter run prefix for assigning filter run result to a variable 2025-03-07 21:48:36 +00:00
133 changed files with 1828 additions and 369 deletions

View File

@@ -20,7 +20,7 @@ jobs:
steps:
- name: build-size-check
id: get_sizes
uses: TiddlyWiki/cerebrus@v4
uses: TiddlyWiki/cerebrus@v6
with:
pr_number: ${{ github.event.pull_request.number }}
repo: ${{ github.repository }}

View File

@@ -25,7 +25,7 @@ jobs:
steps:
- name: Build and check size
uses: TiddlyWiki/cerebrus@v4
uses: TiddlyWiki/cerebrus@v6
with:
pr_number: ${{ inputs.pr_number }}
repo: ${{ github.repository }}

View File

@@ -1,18 +0,0 @@
name: Validate PR Paths
on:
pull_request_target:
types: [opened, reopened, synchronize]
jobs:
validate-pr:
runs-on: ubuntu-latest
steps:
- name: Validate PR
uses: TiddlyWiki/cerebrus@v4
with:
pr_number: ${{ github.event.pull_request.number }}
repo: ${{ github.repository }}
base_ref: ${{ github.base_ref }}
github_token: ${{ secrets.GITHUB_TOKEN }}

37
.github/workflows/pr-validation.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: PR Validation
on:
pull_request_target:
types: [opened, reopened, synchronize]
permissions:
contents: read
pull-requests: write
issues: write
jobs:
validate-pr:
runs-on: ubuntu-latest
steps:
# Step 1: Validate PR paths
- name: Validate PR Paths
uses: TiddlyWiki/cerebrus@v6
with:
pr_number: ${{ github.event.pull_request.number }}
repo: ${{ github.repository }}
base_ref: ${{ github.event.pull_request.base.ref }}
github_token: ${{ secrets.GITHUB_TOKEN }}
mode: rules
continue-on-error: true
# Step 2: Validate change notes
- name: Validate Change Notes
uses: TiddlyWiki/cerebrus@v6
with:
pr_number: ${{ github.event.pull_request.number }}
repo: ${{ github.repository }}
base_ref: ${{ github.event.pull_request.base.ref }}
github_token: ${{ secrets.GITHUB_TOKEN }}
mode: changenotes
continue-on-error: false

View File

@@ -0,0 +1,25 @@
avatar: UklGRiwIAABXRUJQVlA4WAoAAAAwAAAAPwAAPwAASUNDUCACAAAAAAIgbGNtcwRAAABtbnRyR1JBWVhZWiAH6QALAAoACwADAAZhY3NwTVNGVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWxjbXMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZkZXNjAAAAzAAAAG5jcHJ0AAABPAAAADZ3dHB0AAABdAAAABRrVFJDAAABiAAAACBkbW5kAAABqAAAACRkbWRkAAABzAAAAFJtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAFIAAAAcAEcASQBNAFAAIABiAHUAaQBsAHQALQBpAG4AIABEADYANQAgAEcAcgBhAHkAcwBjAGEAbABlACAAdwBpAHQAaAAgAHMAUgBHAEIAIABUAFIAQwAAbWx1YwAAAAAAAAABAAAADGVuVVMAAAAaAAAAHABQAHUAYgBsAGkAYwAgAEQAbwBtAGEAaQBuAABYWVogAAAAAAAA81EAAQAAAAEWzHBhcmEAAAAAAAMAAAACZmYAAPKnAAANWQAAE9AAAApbbWx1YwAAAAAAAAABAAAADGVuVVMAAAAIAAAAHABHAEkATQBQbWx1YwAAAAAAAAABAAAADGVuVVMAAAA2AAAAHABEADYANQAgAEcAcgBhAHkAcwBjAGEAbABlACAAdwBpAHQAaAAgAHMAUgBHAEIAIABUAFIAQwAAVlA4TOYFAAAvP8APEDWGgbRtWv+yt/0WImICOBvWn1C4dFi1bStbvpY8Qg2ePANNNAMh3N2db/7A91/7CHBvBBRr25ZFH+4k98ihkqi2CP4tsANvX8a+8y8Ct04dn0nuUt39ZiBJkowqt911M+MJ1G3bNiZJr1iP0DZ+2bbdadsqprOjAqmoUIX9hf3Fl5/uPYV7I3OMeoFzIvrvwG0kRUr3zPLdYMMXaqrMMsp0K4fufKO6c2hFV5Zh7kRROZX0PSCmB/3KWQwpuiekWelSRZDW94d0q750NrxavpFn1eLNQ9EV8nWlmAET6Q8lrCRTcjFLlLImluK3iXJW/hT47KGklS8OlzWUtXLFYDRCSS74ojUjxggqKMoxd6A1lTCyvsvyzC5/d7BsCHb7yIcHyrX2yR/NPnsAdRT2i0Pwp/o0Il6ix8hsRAuJmQgcr4KREfAiMgUVm9KqmfSxL5pOJspVwwTiV6jiIAg1RMhHpERhbvwgGI34Hc49T7UeKZtXwEqJ+BAaoBneperJH0POs1u4dufwv8Gf+qcOfjyvX6ZIVgxE0Rw87YF3BSc9c7jsXfdjOBG7FwmSb39pfGRwu8IuvUjJNoTpFzkEvDg6W3Qt/9nf99ZXPy8HM43IweTKyNR+WVatXcWWyakBksj9cqW+QetplcjsKElvZH/zuOO/PrCx//tL3/6x/O/C1PZZvSKuulLcS4l8M1ewGPR6ef5sllXW2eGQZ7hVSEZiPmcqrSS8e2ElX8o7t1fvB9LFetmEx5hx1Xuye2PpfjZnSjj7QfKTB3bZZo05Zvh6YuivX24cpc8+ddvADWG9odrSwFalVurxUiidDHmTiaoNkkh2gjbcpxMiAbd39aVP119/N9k4+euNKfcNjwaPhZEuUupUsJrHchw1LkPrRC9bQKa3M8Mj/xx903drdnHMpbirj1ENsUre0oo3N+7gat+2ZctKdsIUYc21sRu+Ucdhn+P7DyarftW00iu3Tmbv+hTfdCTmyaIPT4PrYZDFtBN2W8S9m4oTB5Z2P3Oe7weKjVBq86kXX/r0+WuvTAzfjqm1hsYRPWlbxm4n3IaeGOJEizv8orH9w5ejjmSrfOuEq/HxT6eDemtsZ/HTvvG1/8iVspxZILrlkz/cdsIbIroOgJileFSty2xiHNW5t9fbHJ3ze87bp5T9vc8RuqMB0ReDSt464R/BJxspvgpEsrVAJMTsYg2QovPTOHrvQ9et/S2Xx+40z7dY4JBX0Pz/ElH/T73U2DkK8EiqC9hM/zV3frQfzjaAqO16s1l6xCUXnBFlYxyIer3eEdth7u5xsHKxWoGLqzY3wIULt9G3K6soei9jZ+UcF+Ka3M/II9EUWrJ/LLxy+Q9xIh0vOl3NZCrVnBsuFUTOSnJnSioRWZ9q4g+ZDk5XVORoW2qX2hbIkna3JOrdR3jmpHVLovUkLES6grRO010u0GkDlX7SpH1DQ64Wl2zaSUJv1Mtti2G7kx5IyftWMhfDlGClcxvIUhP5crhp9LIb1Vne187oSAWxelcR/kXjYQTZboW+Oj1pqF0gmfZhSDD6bSgzGWrw3s7QLNtCV+2uatYrd/aFtjDI8R52e/DdyKgRKXBhEak3Ev50+GCUA9EFUor39htVMxmWvW8AM6ptG416rZvdWn+MarIEyH5r6ruZSrx8XrWDP370vbfTjqpmZGIbiFPFoihc4jcrlYi9p3ndSuymZ+XLaKza/P/HUWHn5Axdkd9OjBskY0+pIlz4AlFPFs+aStK5PBIRR4MVVJDihsy4JdEA4pVcrVqMZDyL2/8aYocikEAR9Xjc1BNG9zEiJG7n/cGyrtnblkClBhEgMW4Kx21BEBGJjLa0hcOGmTK64KsKLfKr9QyQELclxY3hqowTIZKdZNTSS5BWiBPlKxDWBVSS41bOepkhTkhGDajLfLyUBOKlkMHPgOhx3JoRN/cEiRgSWdgF2yCyDQu4IcbNo8ftTzxveOJ5y+h509h52+h549h569h587/M20f/b1AB
created: 20251110102157310
first-sighting: 2009-11-14
fullname: Mario Pietsch
github: pmario
homepage: https://wikilabs.github.io/
modified: 20251110124935183
tags: Community/Person Community/Team/Contributors
talk.tiddlywiki.org: pmario
title: @pmario
type: text/vnd.tiddlywiki
youtube: https://www.youtube.com/@pmario
''Hi, My name is Mario Pietsch''. Back in 2009 I was ''searching'' for ''a simple presentation tool'' and discovered ~TiddlyWiki Classic, Monkey Pirate ~TiddlyWiki ([[MPTW|https://mptw.tiddlyspot.com/]]) with ~TagglyTagging, Eric Shulman's ~TiddlyTools, Saq Imtiaz's navigation macros, and more. --- ''I was captivated''.
After a deep dive, I combined these elements into my own "Presentation Manager", along [[3 step by step tutorials|https://groups.google.com/g/tiddlywiki/c/qG_tZ1x0MEU/m/-vLA0luMicYJ]] to help others build it.
Thanks to ''the positive spirit'' of the ~TiddlyWiki community, I am proud to be part of it since 2009.
When Jeremy started developing ~TiddlyWiki 5 on ~GitHub, I joined in—opening [[issue no. 1|https://github.com/TiddlyWiki/TiddlyWiki5/issues/1]] all the way up to 13. For what thats good ;) Since then, I have submitted nearly 600 pull requests and more than 500 issues, many of which have been merged or resolved.
My ~TiddlyWiki 5 "laboratory" is at https://wikilabs.github.io, and I also share content on my ''~YouTube'' channel: https://www.youtube.com/@pmario
Have fun!<br>
Mario

View File

@@ -0,0 +1,19 @@
title: Developer Experience Team
tags: Community/Team
modified: 20251109200632671
created: 20251109200632671
leader: @pmario
team: @saqimtiaz
The Developer Experience Team improves the experience of software contributors to the TiddlyWiki project. This includes enhancing documentation, streamlining contribution processes, and providing tools and resources to help developers effectively contribute to TiddlyWiki.
Tools and resources managed by the Developer Experience Team include:
* Advising and assisting contributors, particularly new developers
* Maintenance of developer-focused documentation on the https://tiddlywiki.com/dev/ site, including:
** Development environment setup guides
** Code review processes and best practices
** Contribution guidelines and documentation
* Continuous integration and deployment scripts providing feedback on pull requests
* Devising and implementing labelling systems for issues and pull requests
* Automation scripts to simplify common development tasks

View File

@@ -1,8 +1,8 @@
title: Infrastructure Team
tags: Community/Team
modified: 20250909171928024
created: 20250909171928024
modified: 20251110133437795
tags: Community/Team
team: @MotovunJack
title: Infrastructure Team
The Infrastructure Team is responsible for maintaining and improving the infrastructure that supports the TiddlyWiki project. This includes the hosting, deployment, and management of the TiddlyWiki websites and services, as well as the tools and systems used by the TiddlyWiki community.
@@ -12,3 +12,4 @@ The infrastructure includes:
* github.com/TiddlyWiki
* tiddlywiki.com DNS
* Netlify account for PR previews
* edit.tiddlywiki.com

View File

@@ -0,0 +1,41 @@
/*\
title: $:/core/modules/filterrunprefixes/let.js
type: application/javascript
module-type: filterrunprefix
Assign a value to a variable
\*/
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Export our filter prefix function
*/
exports.let = function(operationSubFunction,options) {
// Return the filter run prefix function
return function(results,source,widget) {
// Save the result list
var resultList = results.toArray();
// Clear the results
results.clear();
// Evaluate the subfunction to get the variable name
var subFunctionResults = operationSubFunction(source,widget);
if(subFunctionResults.length === 0) {
return;
}
var name = subFunctionResults[0];
if(typeof name !== "string" || name.length === 0) {
return;
}
// Assign the result of the subfunction to the variable
var variables = {};
variables[name] = resultList;
// Return the variables
return {
variables: variables
};
};
};

View File

@@ -35,7 +35,7 @@ function parseFilterOperation(operators,filterString,p) {
operator.prefix = filterString.charAt(p++);
}
// Get the operator name
nextBracketPos = filterString.substring(p).search(/[\[\{<\/]/);
nextBracketPos = filterString.substring(p).search(/[\[\{<\/\(]/);
if(nextBracketPos === -1) {
throw "Missing [ in filter expression";
}
@@ -79,6 +79,10 @@ function parseFilterOperation(operators,filterString,p) {
operand.variable = true;
nextBracketPos = filterString.indexOf(">",p);
break;
case "(": // Round brackets
operand.multiValuedVariable = true;
nextBracketPos = filterString.indexOf(")",p);
break;
case "/": // regexp brackets
var rex = /^((?:[^\\\/]|\\.)*)\/(?:\(([mygi]+)\))?/g,
rexMatch = rex.exec(filterString.substring(p));
@@ -112,7 +116,7 @@ function parseFilterOperation(operators,filterString,p) {
// Check for multiple operands
while(filterString.charAt(p) === ",") {
p++;
if(/^[\[\{<\/]/.test(filterString.substring(p))) {
if(/^[\[\{<\/\(]/.test(filterString.substring(p))) {
nextBracketPos = p;
p++;
parseOperand(filterString.charAt(nextBracketPos));
@@ -141,7 +145,15 @@ exports.parseFilter = function(filterString) {
p = 0, // Current position in the filter string
match;
var whitespaceRegExp = /(\s+)/mg,
operandRegExp = /((?:\+|\-|~|=|\:(\w+)(?:\:([\w\:, ]*))?)?)(?:(\[)|(?:"([^"]*)")|(?:'([^']*)')|([^\s\[\]]+))/mg;
// Groups:
// 1 - entire filter run prefix
// 2 - filter run prefix itself
// 3 - filter run prefix suffixes
// 4 - opening square bracket following filter run prefix
// 5 - double quoted string following filter run prefix
// 6 - single quoted string following filter run prefix
// 7 - anything except for whitespace and square brackets
operandRegExp = /((?:\+|\-|~|(?:=>?)|\:(\w+)(?:\:([\w\:, ]*))?)?)(?:(\[)|(?:"([^"]*)")|(?:'([^']*)')|([^\s\[\]]+))/mg;
while(p < filterString.length) {
// Skip any whitespace
whitespaceRegExp.lastIndex = p;
@@ -152,38 +164,45 @@ exports.parseFilter = function(filterString) {
// Match the start of the operation
if(p < filterString.length) {
operandRegExp.lastIndex = p;
match = operandRegExp.exec(filterString);
if(!match || match.index !== p) {
throw $tw.language.getString("Error/FilterSyntax");
}
var operation = {
prefix: "",
operators: []
};
if(match[1]) {
operation.prefix = match[1];
p = p + operation.prefix.length;
if(match[2]) {
operation.namedPrefix = match[2];
}
if(match[3]) {
operation.suffixes = [];
$tw.utils.each(match[3].split(":"),function(subsuffix) {
operation.suffixes.push([]);
$tw.utils.each(subsuffix.split(","),function(entry) {
entry = $tw.utils.trim(entry);
if(entry) {
operation.suffixes[operation.suffixes.length -1].push(entry);
}
match = operandRegExp.exec(filterString);
if(match && match.index === p) {
// If there is a filter run prefix
if(match[1]) {
operation.prefix = match[1];
p = p + operation.prefix.length;
// Name for named prefixes
if(match[2]) {
operation.namedPrefix = match[2];
}
// Suffixes for filter run prefix
if(match[3]) {
operation.suffixes = [];
$tw.utils.each(match[3].split(":"),function(subsuffix) {
operation.suffixes.push([]);
$tw.utils.each(subsuffix.split(","),function(entry) {
entry = $tw.utils.trim(entry);
if(entry) {
operation.suffixes[operation.suffixes.length -1].push(entry);
}
});
});
});
}
}
// Opening square bracket
if(match[4]) {
p = parseFilterOperation(operation.operators,filterString,p);
} else {
p = match.index + match[0].length;
}
}
if(match[4]) { // Opening square bracket
p = parseFilterOperation(operation.operators,filterString,p);
} else {
p = match.index + match[0].length;
// No filter run prefix
p = parseFilterOperation(operation.operators,filterString,p);
}
// Quoted strings and unquoted title
if(match[5] || match[6] || match[7]) { // Double quoted string, single quoted string or unquoted title
operation.operators.push(
{operator: "title", operands: [{text: match[5] || match[6] || match[7]}]}
@@ -251,6 +270,8 @@ exports.compileFilter = function(filterString) {
results = [];
$tw.utils.each(operation.operators,function(operator) {
var operands = [],
multiValueOperands = [],
isMultiValueOperand = [],
operatorFunction;
if(!operator.operator) {
// Use the "title" operator if no operator is specified
@@ -266,13 +287,29 @@ exports.compileFilter = function(filterString) {
if(operand.indirect) {
var currTiddlerTitle = widget && widget.getVariable("currentTiddler");
operand.value = self.getTextReference(operand.text,"",currTiddlerTitle);
operand.multiValue = [operand.value];
} else if(operand.variable) {
var varTree = $tw.utils.parseFilterVariable(operand.text);
operand.value = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source})[0] || "";
operand.multiValue = [operand.value];
} else if(operand.multiValuedVariable) {
var varTree = $tw.utils.parseFilterVariable(operand.text);
var resultList = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source});
if((resultList.length > 0 && resultList[0] !== undefined) || resultList.length === 0) {
operand.multiValue = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source}) || [];
operand.value = operand.multiValue[0] || "";
} else {
operand.value = "";
operand.multiValue = [];
}
operand.isMultiValueOperand = true;
} else {
operand.value = operand.text;
operand.multiValue = [operand.value];
}
operands.push(operand.value);
multiValueOperands.push(operand.multiValue);
isMultiValueOperand.push(!!operand.isMultiValueOperand);
});
// Invoke the appropriate filteroperator module
@@ -280,6 +317,8 @@ exports.compileFilter = function(filterString) {
operator: operator.operator,
operand: operands.length > 0 ? operands[0] : undefined,
operands: operands,
multiValueOperands: multiValueOperands,
isMultiValueOperand: isMultiValueOperand,
prefix: operator.prefix,
suffix: operator.suffix,
suffixes: operator.suffixes,
@@ -319,6 +358,8 @@ exports.compileFilter = function(filterString) {
return filterRunPrefixes["and"](operationSubFunction, options);
case "~": // This operation is unioned into the result only if the main result so far is empty
return filterRunPrefixes["else"](operationSubFunction, options);
case "=>": // This operation is applied to the main results so far, and the results are assigned to a variable
return filterRunPrefixes["let"](operationSubFunction, options);
default:
if(operation.namedPrefix && filterRunPrefixes[operation.namedPrefix]) {
return filterRunPrefixes[operation.namedPrefix](operationSubFunction, options);
@@ -345,7 +386,13 @@ exports.compileFilter = function(filterString) {
self.filterRecursionCount = (self.filterRecursionCount || 0) + 1;
if(self.filterRecursionCount < MAX_FILTER_DEPTH) {
$tw.utils.each(operationFunctions,function(operationFunction) {
operationFunction(results,source,widget);
var operationResult = operationFunction(results,source,widget);
if(operationResult) {
if(operationResult.variables) {
// If the filter run prefix has returned variables, create a new fake widget with those variables
widget = widget.makeFakeWidgetWithVariables(operationResult.variables);
}
}
});
} else {
results.push("/**-- Excessive filter recursion --**/");

View File

@@ -16,8 +16,8 @@ exports.function = function(source,operator,options) {
var functionName = operator.operands[0],
params = [],
results;
$tw.utils.each(operator.operands.slice(1),function(param) {
params.push({value: param});
$tw.utils.each(operator.multiValueOperands.slice(1),function(paramList) {
params.push({value: paramList[0] || "",multiValue: paramList});
});
// console.log(`Calling ${functionName} with params ${JSON.stringify(params)}`);
var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(functionName,{params: params, source: source});

View File

@@ -113,6 +113,22 @@ exports["jsonset"] = function(source,operator,options) {
return results;
};
exports["jsondelete"] = function(source,operator,options) {
var indexes = operator.operands,
results = [];
source(function(tiddler,title) {
var data = $tw.utils.parseJSONSafe(title,title);
// If parsing failed (data equals original title and is a string), return unchanged
if(data === title && typeof data === "string") {
results.push(title);
} else if(data) {
data = deleteDataItem(data,indexes);
results.push(JSON.stringify(data));
}
});
return results;
};
/*
Given a JSON data structure and an array of index strings, return an array of the string representation of the values at the end of the index chain, or "undefined" if any of the index strings are invalid
*/
@@ -144,7 +160,7 @@ function convertDataItemValueToStrings(item) {
return ["null"]
} else if(typeof item === "object") {
var results = [],i,t;
if($tw.utils.isArray(item)) {
if(Array.isArray(item)) {
// Return all the items in arrays recursively
for(i=0; i<item.length; i++) {
t = convertDataItemValueToStrings(item[i])
@@ -178,7 +194,7 @@ function convertDataItemKeysToStrings(item) {
return [];
}
var results = [];
if($tw.utils.isArray(item)) {
if(Array.isArray(item)) {
for(var i=0; i<item.length; i++) {
results.push(i.toString());
}
@@ -201,7 +217,7 @@ function getDataItemType(data,indexes) {
return item;
} else if(item === null) {
return "null";
} else if($tw.utils.isArray(item)) {
} else if(Array.isArray(item)) {
return "array";
} else if(typeof item === "object") {
return "object";
@@ -213,7 +229,7 @@ function getDataItemType(data,indexes) {
function getItemAtIndex(item,index) {
if($tw.utils.hop(item,index)) {
return item[index];
} else if($tw.utils.isArray(item)) {
} else if(Array.isArray(item)) {
index = $tw.utils.parseInt(index);
if(index < 0) { index = index + item.length };
return item[index]; // Will be undefined if index was out-of-bounds
@@ -223,15 +239,16 @@ function getItemAtIndex(item,index) {
}
/*
Given a JSON data structure and an array of index strings, return the value at the end of the index chain, or "undefined" if any of the index strings are invalid
Traverse the index chain and return the item at the specified depth.
Returns the item at the end of the traversal, or undefined if traversal fails.
*/
function getDataItem(data,indexes) {
function traverseIndexChain(data,indexes,stopBeforeLast) {
if(indexes.length === 0 || (indexes.length === 1 && indexes[0] === "")) {
return data;
}
// Get the item
var item = data;
for(var i=0; i<indexes.length; i++) {
var stopIndex = stopBeforeLast ? indexes.length - 1 : indexes.length;
for(var i = 0; i < stopIndex; i++) {
if(item !== undefined) {
if(item !== null && ["number","string","boolean"].indexOf(typeof item) === -1) {
item = getItemAtIndex(item,indexes[i]);
@@ -243,6 +260,13 @@ function getDataItem(data,indexes) {
return item;
}
/*
Given a JSON data structure and an array of index strings, return the value at the end of the index chain, or "undefined" if any of the index strings are invalid
*/
function getDataItem(data,indexes) {
return traverseIndexChain(data,indexes,false);
}
/*
Given a JSON data structure, an array of index strings and a value, return the data structure with the value added at the end of the index chain. If any of the index strings are invalid then the JSON data structure is returned unmodified. If the root item is targetted then a different data object will be returned
*/
@@ -255,18 +279,15 @@ function setDataItem(data,indexes,value) {
if(indexes.length === 0 || (indexes.length === 1 && indexes[0] === "")) {
return value;
}
// Traverse the JSON data structure using the index chain
var current = data;
for(var i = 0; i < indexes.length - 1; i++) {
current = getItemAtIndex(current,indexes[i]);
if(current === undefined) {
// Return the original JSON data structure if any of the index strings are invalid
return data;
}
// Traverse the JSON data structure using the index chain up to the parent
var current = traverseIndexChain(data,indexes,true);
if(current === undefined) {
// Return the original JSON data structure if any of the index strings are invalid
return data;
}
// Add the value to the end of the index chain
var lastIndex = indexes[indexes.length - 1];
if($tw.utils.isArray(current)) {
if(Array.isArray(current)) {
lastIndex = $tw.utils.parseInt(lastIndex);
if(lastIndex < 0) { lastIndex = lastIndex + current.length };
}
@@ -276,3 +297,32 @@ function setDataItem(data,indexes,value) {
}
return data;
}
/*
Given a JSON data structure and an array of index strings, return the data structure with the item at the end of the index chain deleted. If any of the index strings are invalid then the JSON data structure is returned unmodified. If the root item is targetted then the JSON data structure is returned unmodified.
*/
function deleteDataItem(data,indexes) {
// Check for the root item - don't delete the root
if(indexes.length === 0 || (indexes.length === 1 && indexes[0] === "")) {
return data;
}
// Traverse the JSON data structure using the index chain up to the parent
var current = traverseIndexChain(data,indexes,true);
if(current === undefined || current === null) {
// Return the original JSON data structure if any of the index strings are invalid
return data;
}
// Delete the item at the end of the index chain
var lastIndex = indexes[indexes.length - 1];
if(Array.isArray(current) && current !== null) {
lastIndex = $tw.utils.parseInt(lastIndex);
if(lastIndex < 0) { lastIndex = lastIndex + current.length };
// Check if index is valid before splicing
if(lastIndex >= 0 && lastIndex < current.length) {
current.splice(lastIndex,1);
}
} else if(typeof current === "object" && current !== null) {
delete current[lastIndex];
}
return data;
}

View File

@@ -16,12 +16,13 @@ exports.title = function(source,operator,options) {
var results = [];
if(operator.prefix === "!") {
source(function(tiddler,title) {
if(tiddler && tiddler.fields.title !== operator.operand) {
var titleList = operator.multiValueOperands[0] || [];
if(tiddler && titleList.indexOf(tiddler.fields.title) === -1) {
results.push(title);
}
});
} else {
results.push(operator.operand);
Array.prototype.push.apply(results,operator.multiValueOperands[0]);
}
return results;
};

View File

@@ -20,8 +20,8 @@ exports["[unknown]"] = function(source,operator,options) {
// Check for a user defined filter operator
if(operator.operator.indexOf(".") !== -1) {
var params = [];
$tw.utils.each(operator.operands,function(param) {
params.push({value: param});
$tw.utils.each(operator.multiValueOperands,function(paramList) {
params.push({value: paramList[0] || "",multiValue: paramList});
});
var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(operator.operator,{params: params, source: source});
if(variableInfo && variableInfo.srcVariable) {

View File

@@ -11,10 +11,13 @@ The CSV text parser processes CSV files into a table wrapped in a scrollable wid
var CsvParser = function(type,text,options) {
// Special handler for tab-delimited files
if (type === 'text/tab-delimited-values' && !options.separator) {
if(
!options.separator &&
(type === "text/tab-delimited-values" || type === "text/tab-separated-values")
) {
options.separator = "\t";
}
// Table framework
this.tree = [{
"type": "scrollable", "children": [{
@@ -32,7 +35,7 @@ var CsvParser = function(type,text,options) {
$tw.utils.each(lines, function(columns) {
maxColumns = Math.max(columns.length, maxColumns);
});
for(var line=0; line<lines.length; line++) {
var columns = lines[line];
var row = {
@@ -55,3 +58,4 @@ var CsvParser = function(type,text,options) {
exports["text/csv"] = CsvParser;
exports["text/tab-delimited-values"] = CsvParser;
exports["text/tab-separated-values"] = CsvParser;

View File

@@ -61,7 +61,7 @@ exports.convertStyleNameToPropertyName = function(styleName) {
var propertyName = $tw.utils.unHyphenateCss(styleName);
// Then check if it needs a prefix
if($tw.browser && document.body.style[propertyName] === undefined) {
var prefixes = ["O","MS","Moz","webkit"];
var prefixes = ["Moz","webkit"];
for(var t=0; t<prefixes.length; t++) {
var prefixedName = prefixes[t] + propertyName.substr(0,1).toUpperCase() + propertyName.substr(1);
if(document.body.style[prefixedName] !== undefined) {
@@ -112,8 +112,6 @@ var eventNameMappings = {
correspondingCssProperty: "transition",
mappings: {
transition: "transitionend",
OTransition: "oTransitionEnd",
MSTransition: "msTransitionEnd",
MozTransition: "transitionend",
webkitTransition: "webkitTransitionEnd"
}
@@ -122,8 +120,6 @@ var eventNameMappings = {
correspondingCssProperty: "animation",
mappings: {
animation: "animationend",
OAnimation: "oAnimationEnd",
MSAnimation: "msAnimationEnd",
MozAnimation: "animationend",
webkitAnimation: "webkitAnimationEnd"
}
@@ -156,19 +152,15 @@ exports.getFullScreenApis = function() {
result = {
"_requestFullscreen": db.webkitRequestFullscreen !== undefined ? "webkitRequestFullscreen" :
db.mozRequestFullScreen !== undefined ? "mozRequestFullScreen" :
db.msRequestFullscreen !== undefined ? "msRequestFullscreen" :
db.requestFullscreen !== undefined ? "requestFullscreen" : "",
"_exitFullscreen": d.webkitExitFullscreen !== undefined ? "webkitExitFullscreen" :
d.mozCancelFullScreen !== undefined ? "mozCancelFullScreen" :
d.msExitFullscreen !== undefined ? "msExitFullscreen" :
d.exitFullscreen !== undefined ? "exitFullscreen" : "",
"_fullscreenElement": d.webkitFullscreenElement !== undefined ? "webkitFullscreenElement" :
d.mozFullScreenElement !== undefined ? "mozFullScreenElement" :
d.msFullscreenElement !== undefined ? "msFullscreenElement" :
d.fullscreenElement !== undefined ? "fullscreenElement" : "",
"_fullscreenChange": d.webkitFullscreenElement !== undefined ? "webkitfullscreenchange" :
d.mozFullScreenElement !== undefined ? "mozfullscreenchange" :
d.msFullscreenElement !== undefined ? "MSFullscreenChange" :
d.fullscreenElement !== undefined ? "fullscreenchange" : ""
};
if(!result._requestFullscreen || !result._exitFullscreen || !result._fullscreenElement || !result._fullscreenChange) {

View File

@@ -60,7 +60,7 @@ exports.repackPlugin = function(title,additionalTiddlers,excludeTiddlers) {
version += "+" + pluginVersion.build;
}
// Save the tiddler
$tw.wiki.addTiddler(new $tw.Tiddler(pluginTiddler,{text: JSON.stringify({tiddlers: plugins},null,4), version: version}));
$tw.wiki.addTiddler(new $tw.Tiddler(pluginTiddler,{text: JSON.stringify({tiddlers: plugins},null,4), version: version},$tw.wiki.getModificationFields()));
// Delete any non-shadow constituent tiddlers
$tw.utils.each(tiddlers,function(title) {
if($tw.wiki.tiddlerExists(title)) {

View File

@@ -49,14 +49,26 @@ exports.warning = function(text) {
};
/*
Log a table of name: value pairs
Log a table of name: value or name: [values...] pairs
*/
exports.logTable = function(data) {
if(console.table) {
var hasArrays = false;
$tw.utils.each(data,function(value,name) {
if($tw.utils.isArray(value)) {
hasArrays = true;
}
});
if(console.table && !hasArrays) {
console.table(data);
} else {
$tw.utils.each(data,function(value,name) {
console.log(name + ": " + value);
if($tw.utils.isArray(value)) {
for(var t=0; t<value.length; t++) {
console.log(`${name}[${t}]: ${value[t]}`);
}
} else {
console.log(`${name}: ${value}`);
}
});
}
}

View File

@@ -51,23 +51,29 @@ LogWidget.prototype.invokeAction = function(triggeringWidget,event) {
};
LogWidget.prototype.log = function() {
var data = {},
var self = this,
data = {}, // Hashmap by attribute name with string or array of string values
dataCount,
allVars = {},
allVars = {}, // Hashmap by variable name with string or array of string values
filteredVars;
$tw.utils.each(this.attributes,function(attribute,name) {
// Collect the attributes to be logged
$tw.utils.each(this.parseTreeNode.attributes,function(attribute,name) {
if(name.substring(0,2) !== "$$") {
data[name] = attribute;
var resultList = self.computeAttribute(attribute,{asList: true});
if(resultList.length <= 1) {
data[name] = resultList[0] || "";
} else {
data[name] = resultList;
}
}
});
// Collect values of all variables, using the source text for functions
for(var v in this.variables) {
var variable = this.parentWidget && this.parentWidget.variables[v];
if(variable && variable.isFunctionDefinition) {
allVars[v] = variable.value;
var variableInfo = this.getVariableInfo(v);
if(variableInfo && variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) {
allVars[v] = variableInfo.text;
} else {
allVars[v] = this.getVariable(v,{defaultValue:""});
allVars[v] = variableInfo.resultList.length > 1 ? variableInfo.resultList : variableInfo.text;
}
}
if(this.filter) {

View File

@@ -177,7 +177,7 @@ DroppableWidget.prototype.execute = function() {
DroppableWidget.prototype.assignDomNodeClasses = function() {
var classes = this.getAttribute("class","").split(" ");
classes.push("tc-droppable");
this.domNode.className = classes.join(" ");
this.domNode.className = classes.join(" ").trim();
};
/*

View File

@@ -130,7 +130,7 @@ EventWidget.prototype.execute = function() {
EventWidget.prototype.assignDomNodeClasses = function() {
var classes = this.getAttribute("class","").split(" ");
classes.push("tc-eventcatcher");
this.domNode.className = classes.join(" ");
this.domNode.className = classes.join(" ").trim();
};
/*

View File

@@ -110,7 +110,7 @@ KeyboardWidget.prototype.execute = function() {
KeyboardWidget.prototype.assignDomNodeClasses = function() {
var classes = this.getAttribute("class","").split(" ");
classes.push("tc-keyboard");
this.domNode.className = classes.join(" ");
this.domNode.className = classes.join(" ").trim();
};
/*

View File

@@ -46,7 +46,7 @@ LetWidget.prototype.computeAttributes = function() {
self = this;
this.currentValueFor = Object.create(null);
$tw.utils.each($tw.utils.getOrderedAttributesFromParseTreeNode(this.parseTreeNode),function(attribute) {
var value = self.computeAttribute(attribute),
var value = self.computeAttribute(attribute,{asList: true}),
name = attribute.name;
// Now that it's prepped, we're allowed to look this variable up
// when defining later variables
@@ -56,7 +56,7 @@ LetWidget.prototype.computeAttributes = function() {
});
// Run through again, setting variables and looking for differences
$tw.utils.each(this.currentValueFor,function(value,name) {
if (self.attributes[name] !== value) {
if(!$tw.utils.isArrayEqual(self.attributes[name],value)) {
self.attributes[name] = value;
self.setVariable(name,value);
changedAttributes[name] = true;
@@ -69,8 +69,10 @@ LetWidget.prototype.getVariableInfo = function(name,options) {
// Special handling: If this variable exists in this very $let, we can
// use it, but only if it's been staged.
if ($tw.utils.hop(this.currentValueFor,name)) {
var value = this.currentValueFor[name];
return {
text: this.currentValueFor[name]
text: value[0] || "",
resultList: value
};
}
return Widget.prototype.getVariableInfo.call(this,name,options);

View File

@@ -203,7 +203,7 @@ RevealWidget.prototype.readPopupState = function(state) {
RevealWidget.prototype.assignDomNodeClasses = function() {
var classes = this.getAttribute("class","").split(" ");
classes.push("tc-reveal");
this.domNode.className = classes.join(" ");
this.domNode.className = classes.join(" ").trim();
};
/*

View File

@@ -80,7 +80,7 @@ Widget.prototype.execute = function() {
/*
Set the value of a context variable
name: name of the variable
value: value of the variable
value: value of the variable, can be a string or an array
params: array of {name:, default:} for each parameter
isMacroDefinition: true if the variable is set via a \define macro pragma (and hence should have variable substitution performed)
options includes:
@@ -90,8 +90,10 @@ options includes:
*/
Widget.prototype.setVariable = function(name,value,params,isMacroDefinition,options) {
options = options || {};
var valueIsArray = $tw.utils.isArray(value);
this.variables[name] = {
value: value,
value: valueIsArray ? (value[0] || "") : value,
resultList: valueIsArray ? value : [value],
params: params,
isMacroDefinition: !!isMacroDefinition,
isFunctionDefinition: !!options.isFunctionDefinition,
@@ -114,7 +116,7 @@ allowSelfAssigned: if true, includes the current widget in the context chain ins
Returns an object with the following fields:
params: array of {name:,value:} or {value:} of parameters to be applied
params: array of {name:,value:,multiValue:} of parameters to be applied (name is optional)
text: text of variable, with parameters properly substituted
resultList: result of variable evaluation as an array
srcVariable: reference to the object defining the variable
@@ -140,7 +142,9 @@ Widget.prototype.getVariableInfo = function(name,options) {
params = self.resolveVariableParameters(variable.params,actualParams);
// Substitute any parameters specified in the definition
$tw.utils.each(params,function(param) {
value = $tw.utils.replaceString(value,new RegExp("\\$" + $tw.utils.escapeRegExp(param.name) + "\\$","mg"),param.value);
if("name" in param) {
value = $tw.utils.replaceString(value,new RegExp("\\$" + $tw.utils.escapeRegExp(param.name) + "\\$","mg"),param.value);
}
});
value = self.substituteVariableReferences(value,options);
resultList = [value];
@@ -154,13 +158,20 @@ Widget.prototype.getVariableInfo = function(name,options) {
variables[param.name] = param["default"];
}
});
// Parameters are an array of {value:} or {name:, value:} pairs
// Parameters are an array of {name:, value:, multivalue:} pairs (name and multivalue are optional)
$tw.utils.each(params,function(param) {
variables[param.name] = param.value;
if(param.multiValue) {
variables[param.name] = param.multiValue;
} else {
variables[param.name] = param.value || "";
}
});
resultList = this.wiki.filterTiddlers(value,this.makeFakeWidgetWithVariables(variables),options.source);
value = resultList[0] || "";
} else {
if(variable.resultList) {
resultList = variable.resultList;
}
params = variable.params;
}
return {
@@ -192,22 +203,24 @@ Widget.prototype.getVariable = function(name,options) {
/*
Maps actual parameters onto formal parameters, returning an array of {name:,value:} objects
formalParams - Array of {name:,default:} (default value is optional)
actualParams - Array of string values or {name:,value:} (name is optional)
actualParams - Array of string values or {name:,value:,multiValue} (name and multiValue is optional)
*/
Widget.prototype.resolveVariableParameters = function(formalParams,actualParams) {
formalParams = formalParams || [];
actualParams = actualParams || [];
var nextAnonParameter = 0, // Next candidate anonymous parameter in macro call
paramInfo, paramValue,
paramInfo, paramValue, paramMultiValue,
results = [];
// Step through each of the parameters in the macro definition
for(var p=0; p<formalParams.length; p++) {
// Check if we've got a macro call parameter with the same name
paramInfo = formalParams[p];
paramValue = undefined;
paramMultiValue = undefined;
for(var m=0; m<actualParams.length; m++) {
if(typeof actualParams[m] !== "string" && actualParams[m].name === paramInfo.name) {
paramValue = actualParams[m].value;
paramMultiValue = actualParams[m].multiValue || [paramValue]
}
}
// If not, use the next available anonymous macro call parameter
@@ -217,11 +230,13 @@ Widget.prototype.resolveVariableParameters = function(formalParams,actualParams)
if(paramValue === undefined && nextAnonParameter < actualParams.length) {
var param = actualParams[nextAnonParameter++];
paramValue = typeof param === "string" ? param : param.value;
paramMultiValue = typeof param === "string" ? [param] : (param.multiValue || [paramValue]);
}
// If we've still not got a value, use the default, if any
paramValue = paramValue || paramInfo["default"] || "";
paramMultiValue = paramMultiValue || [paramValue];
// Store the parameter name and value
results.push({name: paramInfo.name, value: paramValue});
results.push({name: paramInfo.name, value: paramValue, multiValue: paramMultiValue});
}
return results;
};
@@ -310,7 +325,7 @@ Widget.prototype.getStateQualifier = function(name) {
};
/*
Make a fake widget with specified variables, suitable for variable lookup in filters
Make a fake widget with specified variables, suitable for variable lookup in filters. Each variable can be a string or an array of strings
*/
Widget.prototype.makeFakeWidgetWithVariables = function(variables) {
var self = this,
@@ -318,7 +333,12 @@ Widget.prototype.makeFakeWidgetWithVariables = function(variables) {
return {
getVariable: function(name,opts) {
if($tw.utils.hop(variables,name)) {
return variables[name];
var value = variables[name];
if($tw.utils.isArray(value)) {
return value[0];
} else {
return value;
}
} else {
opts = opts || {};
opts.variables = variables;
@@ -327,9 +347,18 @@ Widget.prototype.makeFakeWidgetWithVariables = function(variables) {
},
getVariableInfo: function(name,opts) {
if($tw.utils.hop(variables,name)) {
return {
text: variables[name]
};
var value = variables[name];
if($tw.utils.isArray(value)) {
return {
text: value[0],
resultList: value
};
} else {
return {
text: value,
resultList: [value]
};
}
} else {
opts = opts || {};
opts.variables = $tw.utils.extend({},variables,opts.variables);
@@ -366,20 +395,45 @@ Widget.prototype.computeAttributes = function(options) {
return changedAttributes;
};
Widget.prototype.computeAttribute = function(attribute) {
/*
Compute the value of a single attribute. Options include:
asList: boolean if true returns results as an array instead of a single value
*/
Widget.prototype.computeAttribute = function(attribute,options) {
options = options || {};
var self = this,
value;
if(attribute.type === "filtered") {
value = this.wiki.filterTiddlers(attribute.filter,this)[0] || "";
value = this.wiki.filterTiddlers(attribute.filter,this);
if(!options.asList) {
value = value[0] || "";
}
} else if(attribute.type === "indirect") {
value = this.wiki.getTextReference(attribute.textReference,"",this.getVariable("currentTiddler")) || "";
value = this.wiki.getTextReference(attribute.textReference,"",this.getVariable("currentTiddler"));
if(value && options.asList) {
value = [value];
}
} else if(attribute.type === "macro") {
var variableInfo = this.getVariableInfo(attribute.value.name,{params: attribute.value.params});
value = variableInfo.text;
if(options.asList) {
value = variableInfo.resultList;
} else {
value = variableInfo.text;
}
} else if(attribute.type === "substituted") {
value = this.wiki.getSubstitutedText(attribute.rawValue,this) || "";
if(options.asList) {
value = [value];
}
} else { // String attribute
value = attribute.value;
if(options.asList) {
if(value === undefined) {
value = [];
} else {
value = [value];
}
}
}
return value;
};

View File

@@ -1,7 +1,7 @@
title: $:/core/templates/tiddlywiki5-external-js.html
<$set name="saveTiddlerAndShadowsFilter" filter="[subfilter<saveTiddlerFilter>] [subfilter<saveTiddlerFilter>plugintiddlers[]]">
<$set name="rawMarkupFilter" filter="[enlist<saveTiddlerAndShadowsFilter>] +[[$:/core]plugintiddlers[]]">
<$set name="rawMarkupFilter" filter="[enlist<saveTiddlerAndShadowsFilter>] [[$:/core]plugintiddlers[]]">
`<!doctype html>
`{{$:/core/templates/MOTW.html}}`<html lang="`<$text text={{{ [{$:/language}get[name]] }}}/>`">
<head>

View File

@@ -13,56 +13,6 @@ tags: $:/tags/Macro
\define color(name) <<colour $name$>>
\define box-shadow(shadow)
``
-webkit-box-shadow: $shadow$;
-moz-box-shadow: $shadow$;
box-shadow: $shadow$;
``
\end
\define filter(filter)
``
-webkit-filter: $filter$;
-moz-filter: $filter$;
filter: $filter$;
``
\end
\define transition(transition)
``
-webkit-transition: $transition$;
-moz-transition: $transition$;
transition: $transition$;
``
\end
\define transform-origin(origin)
``
-webkit-transform-origin: $origin$;
-moz-transform-origin: $origin$;
transform-origin: $origin$;
``
\end
\define background-linear-gradient(gradient)
``
background-image: linear-gradient($gradient$);
background-image: -o-linear-gradient($gradient$);
background-image: -moz-linear-gradient($gradient$);
background-image: -webkit-linear-gradient($gradient$);
background-image: -ms-linear-gradient($gradient$);
``
\end
\define column-count(columns)
``
-moz-column-count: $columns$;
-webkit-column-count: $columns$;
column-count: $columns$;
``
\end
\procedure datauri(title)
<$macrocall $name="makedatauri" type={{{ [<title>get[type]] }}} text={{{ [<title>get[text]] }}} _canonical_uri={{{ [<title>get[_canonical_uri]] }}}/>
\end

View File

@@ -0,0 +1,43 @@
title: $:/core/macros/deprecated
tags: $:/tags/Macro
<!-- Deprecated Macros -->
<!-- DO NOT USE THESE MACROS. THEY MAY BE REMOVED AT ANY MOMENT -->
\define box-shadow(shadow)
``
box-shadow: $shadow$;
``
\end
\define filter(filter)
``
filter: $filter$;
``
\end
\define transition(transition)
``
transition: $transition$;
``
\end
\define transform-origin(origin)
``
transform-origin: $origin$;
``
\end
\define background-linear-gradient(gradient)
``
background-image: linear-gradient($gradient$);
background-image: -moz-linear-gradient($gradient$);
background-image: -webkit-linear-gradient($gradient$);
``
\end
\define column-count(columns)
``
column-count: $columns$;
``
\end

View File

@@ -2,4 +2,4 @@ title: Using TiddlyWiki as a library in another Node.js application
Node.js applications can include TiddlyWiki as a library so that they can use wikitext rendering.
See the demo at https://github.com/TiddlyWiki/TiddlyWiki5DemoApp
See the demo at https://github.com/Jermolene/TiddlyWiki5DemoApp

View File

@@ -2,12 +2,5 @@ created: 20131127215321439
modified: 20140912135951542
title: $:/DefaultTiddlers
[[Hire the founder of TiddlyWiki]]
[[TiddlyWiki Pre-release]]
HelloThere
[[Quick Start]]
[[Find Out More]]
[[TiddlyWiki on the Web]]
[[Testimonials and Reviews]]
GettingStarted
Community
[[Multi-Valued Variables]]
[[Let Filter Run Prefix]]

View File

@@ -27,4 +27,4 @@ Block forced inline
+
title: ExpectedResult
<div class=" tc-reveal"><p>Block</p></div><div class=" tc-reveal"><p>Block forced block</p></div><span class=" tc-reveal"><p>Block forced inline</p></span><p><span class=" tc-reveal">Inline</span><div class=" tc-reveal">Inline forced block</div><span class=" tc-reveal">Inline forced inline</span></p>
<div class="tc-reveal"><p>Block</p></div><div class="tc-reveal"><p>Block forced block</p></div><span class="tc-reveal"><p>Block forced inline</p></span><p><span class="tc-reveal">Inline</span><div class="tc-reveal">Inline forced block</div><span class="tc-reveal">Inline forced inline</span></p>

View File

@@ -0,0 +1,12 @@
title: LetFilterRunPrefix/ResultList
description: Using the "let" filter run prefix to store result lists, not just single values
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
<$text text={{{ [all[tiddlers]] :let[[varname]] [(varname)sort[]join[,]] }}}/>
+
title: ExpectedResult
<p>$:/core,ExpectedResult,Output</p>

View File

@@ -0,0 +1,12 @@
title: LetFilterRunPrefix/ResultListUnnamedVariable
description: Using the "let" filter run prefix to store result lists, not just single values
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
<$text text={{{ [all[tiddlers]] :let[all[]tag[nothing]] [(varname)sort[]join[,]] }}}/>
+
title: ExpectedResult
<p></p>

View File

@@ -0,0 +1,12 @@
title: LetFilterRunPrefix/ShortcutSyntax
description: Simple usage of "let" filter run prefix
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
<$text text={{{ [[magpie]] =>varname [<varname>] +[join[-]] }}}/>
+
title: ExpectedResult
<p>magpie</p>

View File

@@ -0,0 +1,12 @@
title: LetFilterRunPrefix/Simple
description: Simple usage of "let" filter run prefix
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
<$text text={{{ [[magpie]] :let[[varname]] [<varname>] +[join[-]] }}}/>
+
title: ExpectedResult
<p>magpie</p>

View File

@@ -0,0 +1,18 @@
title: MultiValuedVariables/Function
description: Multi-valued functions
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\function myfunc() [all[tiddlers]sort[]]
<$let varname=<<myfunc>>>
<$text text={{{ [(varname)] +[join[-]] }}}/>
</$let>
+
title: ExpectedResult
<p>
$:/core-ExpectedResult-Output
</p>

View File

@@ -0,0 +1,12 @@
title: MultiValuedVariables/MissingVariable
description: Using multivalued operands with a missing variable
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
<$text text={{{ [(varname)] }}}/>
+
title: ExpectedResult
<p></p>

View File

@@ -0,0 +1,18 @@
title: MultiValuedVariables/NegatedTitle
description: Multi-valued operands
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
<$let
exclude={{{ $:/core Output }}}
>
<$text text={{{ [all[tiddlers]!title(exclude)] +[join[-]] }}}/>
</$let>
+
title: ExpectedResult
<p>
ExpectedResult
</p>

View File

@@ -0,0 +1,18 @@
title: MultiValuedVariables/Operands
description: Multi-valued operands
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\function myfunc() [all[tiddlers]sort[]]
<$let varname=<<myfunc>>>
<$text text={{{ [(varname)] +[join[-]] }}}/>
</$let>
+
title: ExpectedResult
<p>
$:/core-ExpectedResult-Output
</p>

View File

@@ -0,0 +1,21 @@
title: MultiValuedVariables/Parameters
description: Multi-valued function parameters
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\function myfunc(input) [(input)sort[]]
<$let
input={{{ [all[tiddlers]] }}}
output={{{ [function[myfunc],(input)] }}}
>
<$text text={{{ [(output)] +[join[-]] }}}/>
</$let>
+
title: ExpectedResult
<p>
$:/core-ExpectedResult-Output
</p>

View File

@@ -0,0 +1,21 @@
title: MultiValuedVariables/ParametersShortcut
description: Multi-valued function parameters using shortcut syntax
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\function my.func(input) [(input)sort[]]
<$let
input={{{ [all[tiddlers]] }}}
output={{{ [my.func(input)] }}}
>
<$text text={{{ [(output)] +[join[-]] }}}/>
</$let>
+
title: ExpectedResult
<p>
$:/core-ExpectedResult-Output
</p>

View File

@@ -0,0 +1,17 @@
title: MultiValuedVariables/Simple
description: Simple usage of multivalued assignments with the "let" widget
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
<$let varname={{{ [all[tiddlers]sort[]] }}}
varname2=<<varname>>>
<$text text={{{ [(varname2)] +[join[-]] }}}/>
</$let>
+
title: ExpectedResult
<p>
$:/core-ExpectedResult-Output
</p>

View File

@@ -0,0 +1,12 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/VoidElements
type: text/vnd.tiddlywiki
This tests void elements like <br/> and <br> (without `/`).
Line one<br>Line two
Line three<br/>Line four
<hr>
Images are also void: <img src="test.png">

View File

@@ -2,4 +2,4 @@ title: expected-test-tabs-horizontal-a
type: text/html
description: Horizontal tabs test - This is the expected HTML output from a test in test-wikitext-tabs-macro.js
<p><div class="tc-tab-set "><div class="tc-tab-buttons "><button class="" data-tab-title="TabOne" role="switch">t 1</button><button aria-checked="true" class=" tc-tab-selected" data-tab-title="TabTwo" role="switch">t 2</button><button class="" data-tab-title="TabThree" role="switch">t 3</button><button class="" data-tab-title="TabFour" role="switch">TabFour</button></div><div class="tc-tab-divider "></div><div class="tc-tab-content "><div class=" tc-reveal" hidden="true"></div><div class=" tc-reveal"><p>Text tab 2</p></div><div class=" tc-reveal" hidden="true"></div><div class=" tc-reveal" hidden="true"></div></div></div></p>
<p><div class="tc-tab-set "><div class="tc-tab-buttons "><button class="" data-tab-title="TabOne" role="switch">t 1</button><button aria-checked="true" class=" tc-tab-selected" data-tab-title="TabTwo" role="switch">t 2</button><button class="" data-tab-title="TabThree" role="switch">t 3</button><button class="" data-tab-title="TabFour" role="switch">TabFour</button></div><div class="tc-tab-divider "></div><div class="tc-tab-content "><div class="tc-reveal" hidden="true"></div><div class="tc-reveal"><p>Text tab 2</p></div><div class="tc-reveal" hidden="true"></div><div class="tc-reveal" hidden="true"></div></div></div></p>

View File

@@ -2,4 +2,4 @@ title: expected-test-tabs-horizontal-all
type: text/html
description: Horizontal tabs with all parameters active. This is the expected HTML output from a test in test-wikitext-tabs-macro.js
<p><div class="tc-tab-set "><div class="tc-tab-buttons "><button class="" data-tab-title="TabOne" role="switch">t 1</button><button aria-checked="true" class=" tc-tab-selected" data-tab-title="TabTwo" role="switch">t 2</button><button class="" data-tab-title="TabThree" role="switch">desc</button><button class="" data-tab-title="TabFour" role="switch">TabFour</button></div><div class="tc-tab-divider "></div><div class="tc-tab-content "><div class=" tc-reveal" hidden="true"></div><div class=" tc-reveal"><h2 class="">TabTwo</h2><p><p>Text tab 2</p></p></div><div class=" tc-reveal" hidden="true"></div><div class=" tc-reveal" hidden="true"></div></div></div></p>
<p><div class="tc-tab-set "><div class="tc-tab-buttons "><button class="" data-tab-title="TabOne" role="switch">t 1</button><button aria-checked="true" class=" tc-tab-selected" data-tab-title="TabTwo" role="switch">t 2</button><button class="" data-tab-title="TabThree" role="switch">desc</button><button class="" data-tab-title="TabFour" role="switch">TabFour</button></div><div class="tc-tab-divider "></div><div class="tc-tab-content "><div class="tc-reveal" hidden="true"></div><div class="tc-reveal"><h2 class="">TabTwo</h2><p><p>Text tab 2</p></p></div><div class="tc-reveal" hidden="true"></div><div class="tc-reveal" hidden="true"></div></div></div></p>

View File

@@ -2,4 +2,4 @@ title: expected-test-tabs-vertical
type: text/html
description: Vertical tabs test -- This is the expected HTML output from the test in test-wikitext-tabs-macro.js
<p><div class="tc-tab-set tc-vertical"><div class="tc-tab-buttons tc-vertical"><button class="" data-tab-title="TabOne" role="switch">t 1</button><button aria-checked="true" class=" tc-tab-selected" data-tab-title="TabTwo" role="switch">t 2</button><button class="" data-tab-title="TabThree" role="switch">t 3</button><button class="" data-tab-title="TabFour" role="switch">TabFour</button></div><div class="tc-tab-divider tc-vertical"></div><div class="tc-tab-content tc-vertical"><div class=" tc-reveal" hidden="true"></div><div class=" tc-reveal"><p>Text tab 2</p></div><div class=" tc-reveal" hidden="true"></div><div class=" tc-reveal" hidden="true"></div></div></div></p>
<p><div class="tc-tab-set tc-vertical"><div class="tc-tab-buttons tc-vertical"><button class="" data-tab-title="TabOne" role="switch">t 1</button><button aria-checked="true" class=" tc-tab-selected" data-tab-title="TabTwo" role="switch">t 2</button><button class="" data-tab-title="TabThree" role="switch">t 3</button><button class="" data-tab-title="TabFour" role="switch">TabFour</button></div><div class="tc-tab-divider tc-vertical"></div><div class="tc-tab-content tc-vertical"><div class="tc-reveal" hidden="true"></div><div class="tc-reveal"><p>Text tab 2</p></div><div class="tc-reveal" hidden="true"></div><div class="tc-reveal" hidden="true"></div></div></div></p>

View File

@@ -143,6 +143,33 @@ describe("json filter tests", function() {
expect(wiki.filterTiddlers("[{First}jsonset:json[notjson]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}']);
});
it("should support the jsondelete operator", function() {
// Delete top-level object property
expect(wiki.filterTiddlers("[{First}jsondelete[a]]")).toEqual(['{"b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}']);
expect(wiki.filterTiddlers("[{First}jsondelete[b]]")).toEqual(['{"a":"one","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}']);
expect(wiki.filterTiddlers("[{First}jsondelete[c]]")).toEqual(['{"a":"one","b":"","d":{"e":"four","f":["five","six",true,false,null]}}']);
// Delete nested object property
expect(wiki.filterTiddlers("[{First}jsondelete[d],[e]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"f":["five","six",true,false,null]}}']);
// Delete array element
expect(wiki.filterTiddlers("[{First}jsondelete[d],[f],[0]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["six",true,false,null]}}']);
expect(wiki.filterTiddlers("[{First}jsondelete[d],[f],[1]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five",true,false,null]}}']);
// Delete using negative array index
expect(wiki.filterTiddlers("[{First}jsondelete[d],[f],[-1]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false]}}']);
expect(wiki.filterTiddlers("[{First}jsondelete[d],[f],[-2]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,null]}}']);
expect(wiki.filterTiddlers("[{First}jsondelete[d],[f],[-5]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["six",true,false,null]}}']);
// Delete from array
expect(wiki.filterTiddlers("[{Second}jsondelete[0]]")).toEqual(['["deux","trois",["quatre","cinq"]]']);
expect(wiki.filterTiddlers("[{Second}jsondelete[1]]")).toEqual(['["une","trois",["quatre","cinq"]]']);
expect(wiki.filterTiddlers("[{Second}jsondelete[-1]]")).toEqual(['["une","deux","trois"]']);
// Attempting to delete non-existent property should return unchanged
expect(wiki.filterTiddlers("[{First}jsondelete[missing-property]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}']);
expect(wiki.filterTiddlers("[{First}jsondelete[d],[missing]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}']);
// Attempting to delete root should return unchanged
expect(wiki.filterTiddlers("[{First}jsondelete[]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}']);
// Non-JSON input should return unchanged
expect(wiki.filterTiddlers("[{Third}jsondelete[a]]")).toEqual(["This is not JSON"]);
});
it("should support the format:json operator", function() {
expect(wiki.filterTiddlers("[{First}format:json[]]")).toEqual(["{\"a\":\"one\",\"b\":\"\",\"c\":1.618,\"d\":{\"e\":\"four\",\"f\":[\"five\",\"six\",true,false,null]}}"]);
expect(wiki.filterTiddlers("[{First}format:json[4]]")).toEqual(["{\n \"a\": \"one\",\n \"b\": \"\",\n \"c\": 1.618,\n \"d\": {\n \"e\": \"four\",\n \"f\": [\n \"five\",\n \"six\",\n true,\n false,\n null\n ]\n }\n}"]);

View File

@@ -60,10 +60,10 @@ describe("Widget module", function() {
childNode = childNode.children[0];
}
expect(childNode.getVariableInfo("macro",{allowSelfAssigned:true}).params).toEqual([{name:"a",value:"aa"}]);
expect(childNode.getVariableInfo("macro",{allowSelfAssigned:true}).params).toEqual([{name:"a",value:"aa",multiValue:["aa"]}]);
// function params
expect(childNode.getVariableInfo("fn", {allowSelfAssigned:true}).params).toEqual([{name:"f",value:"ff"}]);
expect(childNode.getVariableInfo("fn", {allowSelfAssigned:true}).params).toEqual([{name:"f",value:"ff",multiValue:["ff"]}]);
// functions have a text and a value
expect(childNode.getVariableInfo("x", {allowSelfAssigned:true}).text).toBe("fff");
expect(childNode.getVariableInfo("x", {allowSelfAssigned:true}).srcVariable.value).toBe("[<fn>]");
@@ -73,7 +73,7 @@ describe("Widget module", function() {
expect(childNode.getVariableInfo("$my.widget", {allowSelfAssigned:true}).params).toEqual([{name:"w",default:"ww"}]);
// no params expected
expect(childNode.getVariableInfo("abc", {allowSelfAssigned:true})).toEqual({text:"def"});
expect(childNode.getVariableInfo("abc", {allowSelfAssigned:true})).toEqual({text:"def", resultList: [ "def" ]});
// debugger; Find code in browser

View File

@@ -0,0 +1,13 @@
created: 20251021142729667
modified: 20251021142729667
tags:
title: Concatenating a text reference to create a URL
type: text/vnd.tiddlywiki
!! Concatenating variables and a text reference to create a URL
<$macrocall $name=wikitext-example src="""<$let hash={{{ [<currentTiddler>encodeuricomponent[]] }}}>
<a href=`${ [{!!base-url}] }$#$(hash)$`>this tiddler on tiddlywiki.com</a>
</$let>"""/>
See: [[Substituted Attribute Values]]

View File

@@ -0,0 +1,13 @@
created: 20251021142733998
modified: 20251021142733998
tags:
title: Concatenating variables to create a URL
type: text/vnd.tiddlywiki
!! Concatenating strings and variables to create a URL
<$macrocall $name=wikitext-example src="""<$let hash={{{ [<currentTiddler>encodeuricomponent[]] }}}>
<a href=`http://tiddlywiki.com/#$(hash)$`>this tiddler on tiddlywiki.com</a>
</$let>"""/>
See: [[Substituted Attribute Values]]

View File

@@ -0,0 +1,13 @@
created: 20251015120940754
modified: 20251101092833913
tags: $:/deprecated [[Core Classes]]
title: Deprecated Core Classes
type: text/vnd.tiddlywiki
<<.warning "It is not recommended to use these classes for styling. Though tiddlywiki might still support them, they may be in the process of being dropped, or may only be kept for compatibility purposes. Avoid using them, and update existing code if possible. Be aware that this feature may cease to work at any time.">>
These [[Core Classes]] are considered deprecated. It is not recommend to use them for styling.
* `tc-tagged-*` <<.deprecated-since 5.1.16>> Use [[Custom styles by data-tags]] instead.
* `tc-reveal` <<.deprecated-since 5.3.8>> for styling purposes as it is subject to change.
* `tc-language-(language code)` <<.deprecated-since 5.3.8>> Please use [[:lang()|https://developer.mozilla.org/en-US/docs/Web/CSS/:lang]] instead.

View File

@@ -0,0 +1,31 @@
created: 20251101085817414
modified: 20251101091035398
tags: [[Core Macros]] Macros $:/deprecated
title: Deprecated core macros
type: text/vnd.tiddlywiki
<<.warning "It is discouraged to use the following macros. Though tiddlywiki might still support them, they may be in the process of being dropped, or may only be kept for compatibility purposes. Avoid using it, and update existing code if possible. Be aware that these macros may cease to work at any time. ">>
Most deprecated macros are defined in [[$:/core/macros/deprecated]]. It is discouraged to use them.
! Stylesheet Macros
<<.deprecated-since 5.4.0>> The following core [[macros|Macros]] used to made it easy to specify alternative browser-specific properties when constructing a [[stylesheet|Cascading Style Sheets]] tiddler. They are deprecated after 2017 baseline is supported in v5.4.0:
; `<<box-shadow shadow>>`
: for the `x-box-shadow` properties
; `<<filter filter>>`
: for the `x-filter` properties
; `<<transition transition>>`
: for the `x-transition` properties
; `<<transform-origin origin>>`
: for the `x-transition-origin` properties
; `<<background-linear-gradient gradient>>`
: for the `x-linear-gradient` values of the `background-image` property
; `<<column-count columns>>`
: for the `x-column-count` property

View File

@@ -0,0 +1,59 @@
created: 20250115120000000
modified: 20250115120000000
tags: [[Operator Examples]] [[jsondelete Operator]]
title: jsondelete Operator (Examples)
<$let object-a="""{
"a": "one",
"b": "",
"c": "three",
"d": {
"e": "four",
"f": [
"five",
"six",
true,
false,
null
],
"g": {
"x": "max",
"y": "may",
"z": "maize"
}
}
}
"""
object-b="""{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}"""
array-a="""["une","deux","trois",["quatre","cinq"]]""">
The examples below assume the following JSON object is contained in the variable `object-a`:
<pre><<object-a>></pre>
<<.operator-example 1 "[<object-a>jsondelete[a]]" "Delete a top-level object property">>
<<.operator-example 2 "[<object-a>jsondelete[d],[e]]" "Delete a nested object property">>
<<.operator-example 3 "[<object-a>jsondelete[d],[f],[0]]" "Delete the first element from an array">>
<<.operator-example 4 "[<object-a>jsondelete[d],[f],[-1]]" "Delete the last element from an array using negative index">>
<<.operator-example 5 "[<object-a>jsondelete[d],[f],[-2]]" "Delete the second-to-last element from an array using negative index">>
<<.operator-example 6 "[<object-a>jsondelete[d],[g],[x]]" "Delete a deeply nested object property">>
<<.operator-example 7 "[<object-a>jsondelete[]]" "If no parameters are specified, the JSON object is returned unchanged">>
<<.operator-example 8 "[<object-a>jsondelete[missing]]" "If the property does not exist, the JSON object is returned unchanged">>
The examples below assume the following JSON object is contained in the variable `object-b`:
<pre><<object-b>></pre>
<<.operator-example 9 "[<object-b>jsondelete[b]]" "Delete an empty string property">>
<<.operator-example 10 "[<object-b>jsondelete[d],[f],[1]]" "Delete a middle element from an array">>
The examples below assume the following JSON array is contained in the variable `array-a`:
<pre><<array-a>></pre>
<<.operator-example 11 "[<array-a>jsondelete[0]]" "Delete the first element from a top-level array">>
<<.operator-example 12 "[<array-a>jsondelete[-1]]" "Delete the last element from a top-level array using negative index">>
<<.operator-example 13 "[<array-a>jsondelete[3],[0]]" "Delete an element from a nested array">>
<<.operator-example 14 "[<object-a>] [<object-b>] :and[jsondelete[a]]" "If the input consists of multiple JSON objects with matching properties, the property is deleted from all of them">>

View File

@@ -1,37 +1,60 @@
created: 20230614225302905
modified: 20230614233448662
modified: 20251021140446753
tags: [[Operator Examples]] [[substitute Operator]]
title: substitute Operator (Examples)
type: text/vnd.tiddlywiki
\define time() morning
\define field() modified
!! Substitute operator parameters
The following example uses a ''function definition'' and a ''function call'' with a ''filter expression''
<<wikitext-example-without-html """\function f.text() [[Hi, I'm $1$ and I live in $2$]substitute[Bugs Bunny],[Rabbit Hole Hill]]
<<f.text>>
""">>
---
The following example uses a [[filtered transclusion|Transclusion in WikiText]] with a [[filter expression|Filter Syntax]] creating a link and a text output.
<<wikitext-example-without-html """link: {{{ [[Hi, I'm $1$ and I live in $2$]substitute[Bugs Bunny],[Rabbit Hole Hill]] }}}
text: <$text text={{{ [[Hi, I'm $1$ and I live in $2$]substitute[Bugs Bunny],[Rabbit Hole Hill]] }}}/>
""">>
!! Substitute variables and parameters
The following example uses [[variable definitions|Pragma: \procedure]] "name", "address", a ''filtered transclusion'' assigned to a ''text-widget''. The variables "name" and "address" are used as ''substitute parameters''.
<<wikitext-example-without-html """\procedure name() Bugs Bunny
\procedure address() Rabbit Hole Hill
<$text text={{{ [[Hi, I'm $1$ and I live in $2$]substitute<name>,<address>] }}}/>
""">>
!! Substitute $(variables)$
The following examples use the variables "name" and "address" and directly substitute the with the [[attribute substitution|Substituted Attribute Values]] syntax.
<<wikitext-example-without-html """\procedure name() Bugs Bunny
\procedure address() Rabbit Hole Hill
<$text text=`Hi, I'm $(name)$ and I live in $(address)$`/>
""">>
!! Substitute a filter expression and a variable
The following example uses a procedure, that defines the text with substitution placeholders. `$(field)$` is defined as a variable. `${ ... }$` is filtered <<.olink2 "substitution placeholder" to:"substitute">>.
<<wikitext-example-without-html """\procedure field() modified
\procedure sentence() This tiddler was last $(field)$ on ${[{!!modified}format:date[DDth MMM YYYY]]}$
\define name() Bugs Bunny
\define address() Rabbit Hole Hill
!Substitute <<.op substitute[]>> operator parameters
<<.operator-example 1 "[[Hi, I'm $1$ and I live in $2$]substitute[Bugs Bunny],[Rabbit Hole Hill]]">>
<$text text={{{ [<sentence>substitute[]] }}}/>
!Substitute variables
This example uses the following variables:
---
* name: <$codeblock code=<<name>>/>
* address: <$codeblock code=<<address>>/>
<$text text=`This tiddler was last $(field)$ on ${[{!!modified}format:date[DDth MMM YYYY]]}$`/>
<<.operator-example 2 "[[Hi, I'm $(name)$ and I live in $(address)$]substitute[]]">>
!Substitute variables and operator parameters
This example uses the following variable:
* time: <$codeblock code=<<time>>/>
<<.operator-example 3 "[[Something in the $(time)$ at $2$ about $1$ ]substitute[Maths],[the Library]]">>
!Substitute a filter expression and a variable
This example uses the following variables:
* field: <$codeblock code=<<field>>/>
* sentence: <$codeblock code=<<sentence>>/>
<<.operator-example 4 "[<sentence>substitute[]]">>
""">>

View File

@@ -0,0 +1,54 @@
caption: jsondelete
created: 20250115120000000
modified: 20250115120000000
op-input: a selection of JSON objects
op-output: the JSON objects with the specified property deleted
op-parameter: one or more indexes of the property to delete
op-purpose: delete a property from JSON objects
tags: [[Filter Operators]] [[JSON Operators]]
title: jsondelete Operator
<<.from-version "5.4.0">> The <<.op jsondelete>> operator is used to delete a property from JSON strings. See [[JSON in TiddlyWiki]] for background. See also the following related operators:
* <<.olink jsonset>> to set values within JSON objects
* <<.olink jsonget>> to retrieve the values of a property in JSON data
* <<.olink jsontype>> to retrieve the type of a JSON value
* <<.olink jsonindexes>> to retrieve the names of the fields of a JSON object, or the indexes of a JSON array
* <<.olink jsonextract>> to retrieve a JSON value as a string of JSON
Properties within a JSON object are identified by a sequence of indexes. In the following example, the value at `[a]` is `one`, and the value at `[d][f][0]` is `five`.
```
{
"a": "one",
"b": "",
"c": "three",
"d": {
"e": "four",
"f": [
"five",
"six",
true,
false,
null
],
"g": {
"x": "max",
"y": "may",
"z": "maize"
}
}
}
```
The <<.op jsondelete>> operator uses multiple parameters to specify the indexes of the property to delete. For object properties, the property is removed using JavaScript's `delete` operator. For array elements, the element is removed using `splice`, which shifts remaining elements.
Negative indexes into an array are counted from the end, so -1 means the last item, -2 the next-to-last item, and so on.
Indexes can be dynamically composed from variables and transclusions, e.g. `[<jsondata>jsondelete<variable>,{!!field},[0]]`.
If the specified property does not exist, the JSON object is returned unchanged. If you attempt to delete the root object itself (by providing no indexes or a blank index), the JSON object is returned unchanged.
If the input consists of multiple JSON objects, the property is deleted from all of them.
<<.operator-examples "jsondelete">>

View File

@@ -1,6 +1,6 @@
caption: substitute
created: 20230614223551834
modified: 20230615173049692
modified: 20251021124124967
op-input: a [[selection of titles|Title Selection]]
op-output: the input titles with placeholders for filter expressions, parameter and variables replaced with their corresponding values
op-parameter: the <<.op substitute>> operator optionally accepts a variable number of parameters, see below for details
@@ -18,7 +18,7 @@ The <<.op substitute>> operator replaces any placeholders in the input titles in
# variables
|placeholder syntax|description|h
|`$n$`|Text substitution of a parameter provided to the operator, where n is the position of the parameter starting with 1 for the first parameter. Unmatched placeholders pass through unchanged. |
|`$n$`|Text substitution of a parameter provided to the operator, where n is the position of the parameter starting with 1 for the first parameter.<br>Unmatched placeholders pass through unchanged. |
|`$(varname)$`|Text substitution of a variable. Undefined variables are replaced with an empty string. |
|`${ filter expression }$`|Text substitution of the first result of evaluating a filter expression. In other words, if the filter returns multiple titles only the first one will be used. |

View File

@@ -14,6 +14,7 @@ In technical / logical terms:
|`-[run]` |`:except[run]` |difference of sets |... AND NOT run |
|`~[run]` |`:else[run]` |else |... ELSE run |
|`=[run]` |`:all[run]` |union of sets without de-duplication |... OR run |
|`=>[run]` |`:let[run]` |<<.from-version "5.4.0">> assign results to a variable |... LET run |
The input of a run is normally a list of all the non-[[shadow|ShadowTiddlers]] tiddler titles in the wiki (in no particular order).<br>But the `+` prefix can change this:

View File

@@ -0,0 +1,28 @@
created: 20250307212252946
from-version: 5.4.0
modified: 20250307212252946
rp-input: all titles from previous filter runs
rp-output: an empty title list is always returned from the "let" filter run prefix
rp-purpose: assign the title list resulting from previous filter runs to a multi-valued variable
tags: [[Named Filter Run Prefix]]
title: Let Filter Run Prefix
type: text/vnd.tiddlywiki
<$railroad text="""
\start none
\end none
( ":let" )
[[run|"Filter Run"]]
"""/>
The `:let` filter run prefix assigns the title list resulting from previous filter runs to a [[multi-valued variable|Multi-Valued Variable]]. The variable is named with the first result returned by the filter run.
The variable is made available to the remaining [[filter runs|Filter Run]] in the [[filter expression|Filter Expression]]. Only the first item in the result list is returned when the variable is accessed in the usual way (or an empty string if the result list is empty). Using round brackets instead of angle brackets around a variable name as an operand retrieves the complete list of items in the result list.
This prefix has an optional [[shortcut syntax|Shortcut Filter Run Prefix]] symbol `=>run`. For example:
```
=[<myfun1>] =[<myfun2>] =>myvar
```
The `:let` filter run prefix always clears the current result list.

View File

@@ -23,6 +23,7 @@ A named filter run prefix can precede any [[run|Filter Run]] of a [[filter expre
[[<":or"> |"Or Filter Run Prefix"]] |
[[<":reduce"> |"Reduce Filter Run Prefix"]] |
[[<":sort"> /"v5.2.0"/ |"Sort Filter Run Prefix"]] |
[[<":let"> /"v5.4.0"/ |"Let Filter Run Prefix"]] |
[[<":then"> /"v5.3.0"/ |"Then Filter Run Prefix"]]) [[run|"Filter Run"]]
"""/>

View File

@@ -9,7 +9,7 @@ Shortcut prefixes are commonly used by advanced users because they are fast to t
<$railroad text="""
\start none
\end none
(-|:"+"|"-"|"~"|"=")
(-|:"+"|"-"|"~"|"="|"=>")
[[run|"Filter Run"]]
"""/>
@@ -23,7 +23,8 @@ If a run has:
* 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 `=`, output titles are appended to the filter's output without de-duplication. <<.from-version "5.1.20">>
* the prefix `=`, output titles are appended to the filter's output without de-duplication. <<.from-version "5.1.20">>
* the prefix `=>`, the input is assigned to the variable named with the output title. <<.from-version "5.4.0">>
{{Interchangeable Filter Run Prefixes}}

View File

@@ -16,4 +16,6 @@ op-neg-output: the input, but with tiddler <<.place T>> filtered out if it exist
<<.op title>> is a [[constructor|Selection Constructors]] (except in the form `!title`), but <<.olink2 "field:title" field>> is a [[modifier|Selection Constructors]].
<<.from-version "5.4.0">> If the operand is quoted with round brackets then the <<.op title>> operator returns the complete list of titles assigned to the multi-valued variable. When negated, the title operator with multi-valued operands returns all the titles that are not present in the operand list.
<<.operator-examples "title">>

View File

@@ -6,7 +6,7 @@ type: text/vnd.tiddlywiki
!! Important
<<.from-version "5.3.0">> It is recommended to use [[substituted attributes|Substituted Attribute Values]] or the [[substitute filter operator|substitute Operator]] to concatenate text and variables.
Since <<.from-version "5.3.0">> it is recommended to use [[substituted attributes|Substituted Attribute Values]] or the [[substitute filter operator|substitute Operator]] to concatenate text and variables.
It's a frequent use case in ~TiddlyWiki that you will want to put the results of variables together with various bits of strings of text. This process in some programming languages is often referred to as "concatenating" text.

View File

@@ -1,25 +1,10 @@
created: 20150221181835000
modified: 20230803034031256
modified: 20251101090648379
tags: Macros [[Core Macros]]
title: Stylesheet Macros
type: text/vnd.tiddlywiki
The following core [[macros|Macros]] make it easy to specify alternative browser-specific properties when constructing a [[stylesheet|Cascading Style Sheets]] tiddler:
;`<<box-shadow shadow>>`
: for the `x-box-shadow` properties
;`<<filter filter>>`
: for the `x-filter` properties
;`<<transition transition>>`
: for the `x-transition` properties
;`<<transform-origin origin>>`
: for the `x-transition-origin` properties
;`<<background-linear-gradient gradient>>`
: for the `x-linear-gradient` values of the `background-image` property
;`<<column-count columns>>`
: for the `x-column-count` property
The following macros are documented separately:
The following stylesheet macros are documented separately:
* <<.mlink colour>>
* <<.mlink datauri>>

View File

@@ -4,5 +4,5 @@ release: 5.3.8
tags: $:/tags/ChangeNote
change-type: enhancement
change-category: translation
links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9133
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9133
github-contributors: BramChen

View File

@@ -4,5 +4,5 @@ release: 5.3.8
tags: $:/tags/ChangeNote
change-type: bugfix
change-category: translation
links: https://github.com/TiddlyWiki/TiddlyWiki5/issue/9166
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/issue/9166
github-contributors: Leilei332

View File

@@ -4,5 +4,5 @@ release: 5.3.8
tags: $:/tags/ChangeNote
change-type: bugfix
change-category: palette
links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9175
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9175
github-contributors: Leilei332

View File

@@ -4,5 +4,5 @@ release: 5.3.8
tags: $:/tags/ChangeNote
change-type: enhancement
change-category: translation
links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9184
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9184
github-contributors: EvidentlyCube

View File

@@ -4,5 +4,5 @@ release: 5.3.8
tags: $:/tags/ChangeNote
change-type: bugfix
change-category: plugin
links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9185
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9185
github-contributors: Leilei332

View File

@@ -4,7 +4,7 @@ release: 5.3.8
tags: $:/tags/ChangeNote
change-type: bugfix
change-category: hackability
links: https://github.com/TiddlyWiki/TiddlyWiki5/commit/32caeb69c3e7b75a80a84a1e14363e87175b164e
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/commit/32caeb69c3e7b75a80a84a1e14363e87175b164e
github-contributors: Jermolene
Reverted the [[change in v5.3.7|https://github.com/TiddlyWiki/TiddlyWiki5/pull/8721]] that broke the [[list-tagged-draggable Macro]]. The change involved updating some core macros to use procedures

View File

@@ -4,5 +4,5 @@ release: 5.3.8
tags: $:/tags/ChangeNote
change-type: enhancement
change-category: translation
links: https://github.com/TiddlyWiki/TiddlyWiki5/commit/75502266176de9d4a5e1f89cd7f2e455b7a2f6da
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/commit/75502266176de9d4a5e1f89cd7f2e455b7a2f6da
github-contributors: superuser-does

View File

@@ -4,5 +4,5 @@ release: 5.3.8
tags: $:/tags/ChangeNote
change-type: bugfix
change-category: usability
links: https://github.com/TiddlyWiki/TiddlyWiki5/commit/93d30f374da4a6b2037b335f7f7d4eddce8192db
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/commit/93d30f374da4a6b2037b335f7f7d4eddce8192db
github-contributors: Jermolene

View File

@@ -0,0 +1,38 @@
title: $:/changenotes/5.4.0/#8258
description: Add ability to serialize WikiText AST nodes back to wikitext strings
tags: $:/tags/ChangeNote
release: 5.4.0
change-type: feature
change-category: developer
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/8258
github-contributors: linonetwo
This PR introduces a new utility `$tw.utils.serializeWikitextParseTree()` that can convert WikiText Abstract Syntax Tree (AST) nodes back into wikitext strings.
There is also a utility `serializeAttribute` for a single attribute node, like an attribute of a widget.
!! Use Cases
* Programmatically manipulating wikitext content by modifying the AST, and use this to write it back
* Building WYSIWYG editors
* Creating wikitext formatters and linters
!! Implementation
* New core plugin `tiddlywiki/wikitext-serialize` containing most of the logic
* Separate serialize handlers for each WikiText rule as `module-type: wikiruleserializer`
* Test suite with tag `$:/tags/wikitext-serialize-test-spec`
* It uses each parser's name as rule (`nextMatch.rule.name`), each AST node that needs serialization has a `type` property matching the rule name
** HTML tags and widgets are handled by the `html` serializer
!! Example Usage
```javascript
// Parse a tiddler's wikitext to AST
var parseTree = $tw.wiki.parseTiddler("MyTiddler").tree;
// Serialize AST back to wikitext string
var wikitextString = $tw.utils.serializeWikitextParseTree(parseTree).trimEnd();
```
This feature offers new tools for JS plugin developers. It is not a user-facing change.

View File

@@ -0,0 +1,8 @@
title: $:/changenotes/5.4.0/#8702/impacts/darkmode-info-tiddler
changenote: $:/changenotes/5.4.0/#8702
created: 20250901000000000
modified: 20250901000000000
tags: $:/tags/ImpactNote
description: `$:/info/darkmode` has been superseded by `$:/info/browser/darkmode`
impact-type: deprecation

View File

@@ -0,0 +1,9 @@
title: $:/changenotes/5.4.0/#8702/impacts/palette-pluginisation
changenote: $:/changenotes/5.4.0/#8702
created: 20250901000000000
modified: 20250901000000000
tags: $:/tags/ImpactNote
description: Existing colour palettes have been moved to a new "palettes-legacy" plugin
impact-type: pluginisation
Add the plugin to your wiki in order to continue using the legacy palettes.

View File

@@ -0,0 +1,27 @@
title: $:/changenotes/5.4.0/#8702
description: Colour handling improvements
release: 5.4.0-disabled because this PR is not yet merged
tags: $:/tags/ChangeNote
change-type: enhancement
change-category: hackability
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/8702
github-contributors: Jermolene
This PR brings several new features for end users:
* Automatically switching between a dark and light palette as the operating system setting changes (and to do so without making the wiki dirty)
* Customisation options for palettes. For example, users might choose a base hue, with the colours of the palette automatically adapting to it
* A generalisation of the dark vs. light mechanism to allow an arbitrary number of distinct schemes that are dynamically selected. For example, a palette that has a different scheme for night, morning, day and evening that automatically change with the time of day
There are also new capabilities for palette authors:
* Inheritance for palettes, making it easy to create chains of variants of a base palette
* Self contained palettes that can contain both dark and light variants (or variants for any other custom scheme)
To make all of these new features possible, this PR also includes some useful new general purpose mechanisms and features:
* Background actions that are triggered whenever there is a change to the results of a specified filter
* Several new filter operators for manipulating colour values. The underlying functionality comes from the [[color.js|https://colorjs.io/]] library
* New media query tracking mechanism that can track the results of any CSS media query (not just dark mode), storing the results in a shadow `$:/info/...` tiddler
* New `changecount` filter operator
* New `:apply` filter run prefix (to be replaced by the `:let` filter run prefix in [[#8972|https://github.com/TiddlyWiki/TiddlyWiki5/pull/8972]]))

View File

@@ -0,0 +1,16 @@
title: $:/changenotes/5.4.0/#8972
description: Multi-valued variables and let filter run prefix
release: 5.4.0
tags: $:/tags/ChangeNote
change-type: enhancement
change-category: hackability
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/8972
github-contributors: Jermolene
This PR introduces a new filter run prefix `:let` that assigns the result of the filter run to a variable that is made available for the remaining filter runs of the filter expression. It solves the problem that previously it was impossible to compute values for filter operator parameters; parameters could only be a literal string, text reference or variable reference.
This PR also introduces multi-valued variables, the ability to store a list of results in a variable, not just a single string. They can be assigned with the new `:let` filter run prefix, or the existing `<$let>` widget. The full list of values can be retrieved using round brackets instead of the usual angle brackets. In all other contexts only the first item in the list is used as the variable value.
* [[Multi-Valued Variables]]
* [[Let Filter Run Prefix]]
* [[LetWidget]]

View File

@@ -0,0 +1,16 @@
title: $:/changenotes/5.4.0/#9078
description: Web server get-file route now supports HTTP Range headers and streaming
tags: $:/tags/ChangeNote
release: 5.4.0
change-type: enhancement
change-category: nodejs
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9078
github-contributors: linonetwo
The web server's `get-file` route now supports HTTP Range requests and file streaming, enabling better loading and playback of large media files.
!! Features
* HTTP Range header: Enables partial content delivery with `206 Partial Content` responses, which is used when the user drags the progress bar in video/audio playback
* Streaming file delivery: Browsers can now properly seek and stream video files from the `/files/` directory. Files are read using Node.js streams for better performance and memory efficiency.
* Resumable downloads: To save interrupted downloads

View File

@@ -0,0 +1,18 @@
title: $:/changenotes/5.4.0/#9103
description: Add support for commands and startups which return promises
release: 5.4.0
tags: $:/tags/ChangeNote
change-type: enhancement
change-category: developer
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9103
github-contributors: Arlen22
This adds support for async functions to commands and startups.
In both `synchronous: true` and `synchronous: false` mode, if you return a promise (manually or by using the async keyword), it will wait for the promise to resolve, and then proceed using the resolved value as the return value of the function.
Importantly, in `synchronous: false` mode, returning a promise will not change the callback behavior. So you can safely use an async function and still call the callback to continue execution.
Previously, in `synchronous: true` mode, returning a promise would have just logged the promise to console and halted execution. Now the promise will be awaited, and if the value is truthy, it will be logged to console and halt execution. If the promise resolves to a falsy value, execution will continue. This is the main breaking change, but since logging an opaque promise to console is the most useless of all error messages, this is unlikely to seriously break anything in practice. Throwing anything, truthy or not, will still stop execution (in `synchronous: true` mode).
This also does not add any error handling code. Rejected promises should still be logged to console as unhandled rejections just as uncaught exceptions are currently.

View File

@@ -0,0 +1,10 @@
title: $:/changenotes/5.4.0/#9118
description: Update highlightjs plugin
release: 5.4.0
tags: $:/tags/ChangeNote
change-type: enhancement
change-category: plugin
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9118
github-contributors: Leilei332
Update highlightjs version to 11.11.1

View File

@@ -0,0 +1,7 @@
title: $:/changenotes/5.4.0/#9119/impacts/sticky-flag
changenote: $:/changenotes/5.4.0/#9119
created: 20251114082949025
modified: 20251114082949025
tags: $:/tags/ImpactNote
description: Tiddlywiki no longer works on old browsers that doesn't support [[sticky flag|https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/sticky]].
impact-type: compatibility-break

View File

@@ -0,0 +1,8 @@
title: $:/changenotes/5.4.0/#9119
description: Use sticky flag to improve regexp search performance
release: 5.4.0
tags: $:/tags/ChangeNote
change-type: performance
change-category: internal
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9118
github-contributors: Leilei332

View File

@@ -0,0 +1,9 @@
title: $:/changenotes/5.4.0/#9131/impacts/strikethrough
changenote: $:/changenotes/5.4.0/#9131
created: 20250901000000000
modified: 20250901000000000
tags: $:/tags/ImpactNote
description: CSS rules using `strike` selector will broken
impact-type: compatibility-break
`strike` should be replaced by `s`.

View File

@@ -0,0 +1,8 @@
title: $:/changenotes/5.4.0/#9131
description: Render s tag instead of strike
release: 5.4.0
tags: $:/tags/ChangeNote
change-type: deprecation
change-category: internal
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9118
github-contributors: Leilei332

View File

@@ -0,0 +1,10 @@
title: $:/changenotes/5.4.0/#9135
description: Update ESLint target to ES2017
release: 5.4.0
tags: $:/tags/ChangeNote
change-type: enhancement
change-category: developer
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9135
github-contributors: Arlen22
Updates the eslint config to check for syntax newer than ES2017. This uses a plugin to check for newer syntax, for better error messages, and may need to be updated regularly along with eslint to catch the latest features.

View File

@@ -0,0 +1,8 @@
title: $:/changenotes/5.4.0/#9154
description: Allow button widget to use all aria attributes
release: 5.4.0
tags: $:/tags/ChangeNote
change-type: enhancement
change-category: widget
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9154
github-contributors: Leilei332

View File

@@ -0,0 +1,10 @@
title: $:/changenotes/5.4.0/#9167
description: Allow link widget to use all aria attributes
release: 5.4.0
tags: $:/tags/ChangeNote
change-type: enhancement
change-category: widget
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9167
github-contributors: Leilei332
Allow LinkWidget to use all aria attributes directly.

View File

@@ -0,0 +1,9 @@
title: $:/changenotes/5.4.0/#9183/impacts/core-server-pluginisation
changenote: $:/changenotes/5.4.0/#9183
created: 20250901000000000
modified: 20250901000000000
tags: $:/tags/ImpactNote
description: Server components of the core have been moved into a new `$:/core-server` plugin
impact-type: pluginisation
It is not necessary for wikis to explicitly include the `$:/core-server` plugin.

View File

@@ -2,9 +2,9 @@ title: $:/changenotes/5.4.0/#9183
description: Offload server-only components to a plugin
release: 5.4.0
tags: $:/tags/ChangeNote
change-type: pluginisation
change-type: performance
change-category: internal
links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9183 https://github.com/TiddlyWiki/TiddlyWiki5/pull/9288
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9183 https://github.com/TiddlyWiki/TiddlyWiki5/pull/9288
github-contributors: Jermolene Leilei332
This change reduces the size of the core plugin by 119.3KB or about 4.5%.

View File

@@ -0,0 +1,10 @@
title: $:/changenotes/5.4.0/#9235
description: Fix nested `span.tc-keyboard` element in core
release: 5.4.0
tags: $:/tags/ChangeNote
change-type: enhancement
change-category: internal
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9235 https://github.com/TiddlyWiki/TiddlyWiki5/pull/9325
github-contributors: Leilei332
Removes nested `<span class="tc-keyboard">` element in core search field.

View File

@@ -0,0 +1,7 @@
title: $:/changenotes/5.4.0/#9242/impacts/drpecate-css-macros
changenote: $:/changenotes/5.4.0/#9242
created: 20250901000000000
modified: 20250901000000000
tags: $:/tags/ImpactNote
description: Currently all the CSS property macros are deprecated
impact-type: deprecation

View File

@@ -0,0 +1,10 @@
title: $:/changenotes/5.4.0/#9242
description: Deprecate CSS macros supported in 2017 baseline
release: 5.4.0
tags: $:/tags/ChangeNote
change-type: deprecation
change-category: developer
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9242
github-contributors: Leilei332
Mark CSS macros supported in 2017 baseline as deprecated. They are moved to [[$:/core/macros/deprecated]] now.

View File

@@ -0,0 +1,8 @@
title: $:/changenotes/5.4.0/#9243
description: Replace CSS property macros in Snow White theme
release: 5.4.0
tags: $:/tags/ChangeNote
change-type: enhancement
change-category: theme
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9243
github-contributors: Leilei332

View File

@@ -0,0 +1,8 @@
title: $:/changenotes/5.4.0/#9253/impacts/textencoder
changenote: $:/changenotes/5.4.0/#9253
created: 20251115041709112
modified: 20251115041709112
tags: $:/tags/ImpactNote
description: Encoding & decoding base64 no longer works on browsers that doesn't support [[TextEncoder|https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder]] or [[TextDecoder|https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder]]
impact-type: compatibility-break

View File

@@ -0,0 +1,10 @@
title: $:/changenotes/5.4.0/#9253
description: Switch to native support for converting utf-8 between base64
release: 5.4.0
tags: $:/tags/ChangeNote
change-type: performance
change-category: internal
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9253
github-contributors: Leilei332
Replace `base64-utf8` module with `TextEncoder` method to convert between utf-8 and Base64.

View File

@@ -0,0 +1,7 @@
title: $:/changenotes/5.4.0/#9275/impacts/remove-range-ie
changenote: $:/changenotes/5.4.0/#9275
created: 20250901000000000
modified: 20250901000000000
tags: $:/tags/ImpactNote
description: RangeWidget will not work on Internet Explorer 10
impact-type: compatibility-break

View File

@@ -0,0 +1,8 @@
title: $:/changenotes/5.4.0/#9275
description: Remove support for Internet Explorer in RangeWidget
release: 5.4.0
tags: $:/tags/ChangeNote
change-type: deprecation
change-category: widget
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9275
github-contributors: Leilei332

View File

@@ -4,7 +4,7 @@ release: 5.4.0
tags: $:/tags/ChangeNote
change-type: enhancement
change-category: internal
links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9287
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9287
github-contributors: Jermolene
Doing so enables us to filter and group changes. For example, we could show all the breaking changes between two releases.

View File

@@ -0,0 +1,14 @@
title: $:/changenotes/5.4.0/#9295
description: Markdown: Fix missing inline support and macrocall args parsing error
release: 5.4.0
tags: $:/tags/ChangeNote
change-type: bugfix
change-category: plugin
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9295
github-contributors: cdruan
* Fix markdown parser to respect ''parseAsInline'' option [[#8917|https://github.com/TiddlyWiki/TiddlyWiki5/pull/8917]].
* Fix improper parsing of macrocall, whenenver args contain `>` character.
* Use ''$tw.log.MARKDOWN'' flag to enable debug messages.

View File

@@ -0,0 +1,13 @@
title: $:/changenotes/5.4.0/#9305
description: Let tiddler modules overwrite shadow modules with the same exports but different names
release: 5.4.0
tags: $:/tags/ChangeNote
change-type: bugfix
change-category: internal
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9305
github-contributors: yaisog
Tiddlers were previously processed before shadows during module registration. The shadow modules registration algorithm only checked for a matching title to prevent overwriting, but a differently named tiddler with the same exports would be overwritten by a shadow. This change swaps the order of $tw.wiki.defineTiddlerModules() and $tw.wiki.defineShadowModules() in boot.js, so that tiddlers are processed after shadows and can therefore override them.
Each group (tiddlers or shadows) is sorted alphabetically, so plugin shadows would previously correctly overwrite core shadows (assuming their name starts with $:/plugins/), which remains unchanged. This change only affects module tiddlers that have the same export as a shadow, but a different name.

View File

@@ -0,0 +1,10 @@
title: $:/changenotes/5.4.0/#9313
description: Set modal's mask-closable attribute to yes by default
release: 5.4.0
tags: $:/tags/ChangeNote
change-type: enhancement
change-category: usability
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9313
github-contributors: Leilei332

View File

@@ -0,0 +1,8 @@
title: $:/changenotes/5.4.0/#9316/impacts/currentcolor
changenote: $:/changenotes/5.4.0/#9316
created: 20251115032340986
modified: 20251115032340986
tags: $:/tags/ImpactNote
description: SVG icons will inherit colors instead of using `#333333` if its parent element has `color` property set
impact-type: compatibility-break

Some files were not shown because too many files have changed in this diff Show More