1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2026-01-27 05:13:40 +00:00

Compare commits

..

300 Commits

Author SHA1 Message Date
Jeremy Ruston
fe708eefc6 Merge branch 'master' into colour-improvements 2026-01-23 09:03:47 +00:00
Jeremy Ruston
a7525037fd Improve English 2026-01-22 12:11:45 +00:00
Jeremy Ruston
15736bceb9 Add test for invalid pragma 2026-01-21 21:49:16 +00:00
Jeremy Ruston
35b0108da6 Fix parsing bug 2026-01-21 21:45:45 +00:00
Saq Imtiaz
eb3a80968e docs: changenotes for previously merged PRs (#9599)
* docs: changenotes for previously merged PRs

* fix: added text field for changenotes
2026-01-21 09:08:50 +01:00
Mario Pietsch
62ae4b24bc allow space after const {}. Set keyword spacing to warning (#9589) 2026-01-20 22:03:43 +01:00
yaisog
ded76aa84f Fixes Ctrl-Enter not working in EditTemplate tag name input (#9600)
* Assign save-tiddler-actions to inputAcceptVariantActions

* Add changenote
2026-01-20 20:22:42 +01:00
Jeremy Ruston
59d23da97c Merge branch 'default-prefix-for-subfilter' into colour-improvements 2026-01-20 16:22:01 +00:00
Jeremy Ruston
3f654bccaa Revert previous merge from #9595
This reverts commit eb8f69dd03.
2026-01-20 16:04:28 +00:00
XLBilly
e42ed6808e Split escapecss into two platforms (#9475)
* Split escapecss into to platforms

This will reduce core size slightly

* Update change note
2026-01-20 12:13:48 +01:00
yaisog
844564180f Fix LetWidget to always set all staged variables on first render (#9494)
* Always set all staged variables on first render

* Add changenote
2026-01-20 10:24:56 +00:00
Saq Imtiaz
a670de0e95 Fixes regressions in list-tagged-draggable (#9596)
* Fix regressions in list-tagged-draggable

* Remove trailing newline from list.tid
2026-01-20 10:01:02 +01:00
Bram Chen
faee49ee01 Update chinese language files (#9576)
* Update chinese language files
* add chinese translations for alert aria message

* Update chinese language files
* update related change note
2026-01-20 07:33:05 +01:00
yaisog
dd20be49f0 Make impact note for #9337 less verbose / repetitive (#9481)
* Make impact note less verbose / repetitive for #9337

* Correct some fields
2026-01-20 07:16:21 +01:00
Simon Huber
a27f74bbdc Fixes bug in sidebar tab Open to allow disabling drag and drop (#9504)
the variable is called `tv-enable-drag-and-drop` - not `tv-allow-drag-and-drop`
2026-01-20 07:06:29 +01:00
Jeremy Ruston
a45a8723c0 Add a view template body for palettes 2026-01-19 21:20:08 +00:00
Jeremy Ruston
ae7d6da35b Add a P3 palette for testing 2026-01-19 12:54:27 +00:00
Jeremy Ruston
a8e056c88c Merge branch 'default-prefix-for-subfilter' into colour-improvements 2026-01-19 12:54:01 +00:00
Jeremy Ruston
5b0af905a2 Use the subfilter suffix to suppress deduping palette entries 2026-01-19 12:53:45 +00:00
Jeremy Ruston
eb8f69dd03 Merge subfilter fix from #9595 2026-01-19 12:52:58 +00:00
Jeremy Ruston
ef49828989 temp 2026-01-19 12:49:08 +00:00
Jeremy Ruston
9b56734451 Extend subfilter operator with suffix for specifying the default filter run prefix 2026-01-19 11:31:09 +00:00
Jeremy Ruston
2f8d53d3f7 Allow default filter run prefix to be specified for filter evaluation 2026-01-19 11:30:50 +00:00
Jeremy Ruston
5b96929f4e Fix tests 2026-01-18 12:03:59 +00:00
Jeremy Ruston
829bb61378 Merge branch 'master' into colour-improvements 2026-01-18 11:48:42 +00:00
Jeremy Ruston
ae4e99951a Revert #9554 Refactor stylesheets in single <style> tags
These changes break the palette switcher
2026-01-18 11:40:10 +00:00
Jeremy Ruston
421ecff30f Merge branch 'master' into colour-improvements 2026-01-17 17:52:34 +00:00
Mario Pietsch
be84dee26b Allow unused variables in function headers and caught exceptions (#9588) 2026-01-17 17:51:51 +00:00
Jeremy Ruston
c7511fc99f Tweak credits for 2026 palettes 2026-01-17 17:50:27 +00:00
Jeremy Ruston
87f797b1ae Fix use of creaky old technique for setting alpha/opacity from a different base colour 2026-01-17 17:46:25 +00:00
Jeremy Ruston
8cc94d8ea7 Fix year category for new palettes 2026-01-17 17:45:12 +00:00
Jeremy Ruston
dcaff6a243 Revert linter fixes for unused function parameters 2026-01-17 17:29:28 +00:00
Jeremy Ruston
e98aed6f6e Fix remaining references to palettes-2025 plugin 2026-01-17 17:23:16 +00:00
Jeremy Ruston
d35e34b55e Remove debugging code
It was breaking colour assignment as a property, rather than in a stylesheet
2026-01-17 17:22:57 +00:00
Jeremy Ruston
bb4d57e170 More linting dancing 2026-01-17 17:00:24 +00:00
Jeremy Ruston
fd709afc37 More linting errors 2026-01-17 16:53:52 +00:00
Jeremy Ruston
93388bc3cc Fix lint errors 2026-01-17 16:05:32 +00:00
Jeremy Ruston
a8c4437587 Properly credit the new palettes 2026-01-17 15:48:16 +00:00
Jeremy Ruston
6dc83386bd Update new palettes to 2026 category 2026-01-17 15:44:42 +00:00
Jeremy Ruston
9641e6a2d0 Remove docs for apply filter run prefix 2026-01-17 15:43:45 +00:00
Jeremy Ruston
3254544eb2 Update the 2025 palette plugin to 2026 2026-01-17 15:42:09 +00:00
Jeremy Ruston
c707b9a432 Enable change note 2026-01-17 15:39:37 +00:00
Jeremy Ruston
3cab1571be Don't lint color.js 2026-01-17 15:39:19 +00:00
Jeremy Ruston
502598bd25 Switch from apply filter run to let filter run 2026-01-17 15:35:27 +00:00
XLBilly
00e17874f0 Add stylesheet wiki information (#9565)
* Add stylesheet wiki information

* Use 2 spaces

* Update change note

* Use ∈ symbol instead
I think this one is less confusing than the previous one
2026-01-17 15:00:32 +00:00
XLBilly
9041f099a3 Refactor stylesheets in single <style> tags (#9554)
* Create RootStylesheet.tid

* put stylesheets into single <style> tags

* viewHandler should not be a widget

* format RootStylesheet

* add ROOT_STYLESHEET_TITLE constant

* use let widget in RootStylesheet

* use view widget from #8135 for testing

* reformat RootStylesheet

* update view widget

* update view widget

* update view widget

* update view widget

* update view widget

* update view widget

* trying to fix the date parsing

* refactor view widget with more extensible architecture

* remove performance instrumentation

* hardcode reset and base stylesheets

* adopt ES2017 syntax for view widget

* don't hardcode vanilla/base

* remove unused constructors

* remove unused variable

* make refresh method static

* trying to fix missingThis

* add changenote

* revert change in vanilla/base

* Revert changes to the view widget

* Refactor RootStylesheet.tid
* Use transclude widget instead of view widget
* Remove hardcoded noramlize.css

* Add comment for debugging

* Migrate some templates to use RootStylesheet

* Remove PAGE_STYLESHEET_TITLE
Since it is no longer used in render.js

* Update change note

---------

Co-authored-by: Simon Huber <huber.simon@protonmail.com>
2026-01-17 14:59:10 +00:00
yaisog
3ba31be2a8 Add the words and lines modes to $diff-text (#9551)
* Initial commit

* Add line ending configuration for consistent tests

* Initial commit

* Correctly consider editcost parameter

* Move diffPartsToChars() to $tw.utils

* Remove superfluous file

* Correct "efficiency" parameter naming in the documentation

The parameter was incorrectly referred to as "efficent" in several places.

* Update diffPartsToChars to ES2017 style

* Consolidate let/const declarations
2026-01-17 14:57:52 +00:00
XLBilly
99d8afd515 Bump markdown-it to latest version (#9513)
* Bump markdown-it to newest version

* Update change note
2026-01-17 14:56:16 +00:00
Jeremy Ruston
8a18f92710 Typo 2026-01-17 13:02:54 +00:00
Jeremy Ruston
3353d0e005 Upgrade color.js 2026-01-17 12:53:41 +00:00
Jeremy Ruston
fd85842036 Merge branch 'master' into colour-improvements 2026-01-16 22:40:35 +00:00
Jeremy Ruston
b0dc46c9e9 Avoid using camel case for new docs tiddlers 2026-01-16 22:40:27 +00:00
Jeremy Ruston
2ab5f26644 Merge branch 'tiddlywiki-com' 2026-01-16 17:36:54 +00:00
Mario Pietsch
419fe68ee2 [Docs] Improve tag-pill documentation and examples (#9580)
* [Docs] Improve tag-pill documentation and examples

* Apply suggestion from @saqimtiaz

---------

Co-authored-by: Saq Imtiaz <saq.imtiaz@gmail.com>
2026-01-14 14:04:34 +01:00
XLBilly
0e765bdbdb Make alert aria message translatable (#9575)
* Make alerts aria message translatable

* Update change notes

* Update change note
2026-01-12 10:21:35 +01:00
Jeremy Ruston
855d8a9638 Fix images loaded from _canonical_uri tiddlers do not have loading and error classes assigned (#9570)
* Fix images loaded from _canonical_uri tiddlers do not have loading and error classes assigned

* Create changenote

* Joyously fix eslint error
2026-01-10 14:50:31 +00:00
Jeremy Ruston
afcf108d29 Add changenote 2026-01-10 14:42:58 +00:00
Jeremy Ruston
8f9acc0ca2 Fix missing file extension 2026-01-10 14:35:26 +00:00
Jeremy Ruston
5e4b8fbb3c Additional fix for #9177 2026-01-08 18:20:57 +00:00
lin onetwo
8e301178a4 Feat/view button condition (#9466)
* feat: support condition field on $:/tags/ViewToolbar button

* Delete convert-markdown.tid

* Create #9466.tid

* Update title.tid
2026-01-08 13:34:58 +01:00
Jeremy Ruston
5b0c923a82 Merge branch 'master' into colour-improvements 2026-01-08 11:23:43 +00:00
Cameron Fischer
a72d3a09bf Quick fix to some deprecated class methods (#9561) 2026-01-08 10:13:16 +01:00
Jeremy Ruston
56634ffe29 Merge branch 'tiddlywiki-com' 2026-01-05 16:36:49 +00:00
Jeremy Ruston
24c317e1ab Revert "Add macro operator (#9520)"
This reverts commit 3c8ee86e23.
2026-01-05 16:18:01 +00:00
Jeremy Ruston
07329c6849 Revert "Root stylesheet refresh - Stylesheets in single <style> tags (#8130)"
This reverts commit da41a55f29.
2026-01-05 16:05:15 +00:00
yaisog
47ab3476f6 Improve LogWidget and ActionLogWidget documentation (#9550) 2026-01-05 14:31:11 +01:00
Mario Pietsch
f0e64660f2 [DOCS] Add multi-columns class to Widgets in Wikitext (#9516) 2026-01-04 21:45:19 +00:00
Mohammad Rahmani
9663e65f4b list-tagged-draggable to use title field by default (#9177)
* Revert the list-tagged-draggable to use title field by default

* Update list.tid

It seems `<$transclude field=<<__field__>> />` when field default value is empty does not work as expected.
This commit uses `field:"title"` instead of `field:""` in the list-tagged-draggable header.

* Update list.tid

The redundant `field` removed. The new `displayField` attribute is used to let user choose the filed they like to display when the list items are rendered as simple links.

* Update list.tid

The proposed field attribute in list-tagged-draggable updated to use the same `displayField` aslist-links-draggable.

* Create #9177

change note for #9177 was created

* Update list.tid

The `list-links-draggable` now is fully backward compatible. It uses caption as default field as before and is compatible with `list-links`.

The `list-tagged-draggable` is fully backward compatible while got a new displayField parameter.

* Update list-tagged-draggable Macro.tid

Update docs to reflect new displayField parameter

* Update list-links-draggable Macro.tid

Update documentation to reflect the new displayField parameter.

* Update list-tagged-draggable Macro.tid

Update docs. Remove formatting error in doc.
2026-01-04 21:41:38 +00:00
Mario Pietsch
7cb422242a DOCS - Fix typo in Date Fields (#9518) 2026-01-04 21:36:57 +00:00
Jeremy Ruston
f075f24e6b Merge branch 'tiddlywiki-com' 2026-01-04 20:48:58 +00:00
superuser-does
5fa1098c03 tw5.com: remove duplicate text from Forums tiddler (#9523) 2026-01-04 18:15:37 +00:00
superuser-does
92dc927c7b docs: savetiddlers extension is now Firefox-only (#9524)
* tw5.com: removed Chrome browser from savetiddlers

Was deprecated in early 2025, and is now Firefox-only

Additional changes:
* Lowercased buggyj in line with how the name is used elsewhere on tw5.com and the rest of the internet
* Pulled url from the url field (following the example of Timimi resource tiddler)
* Changed formatting of title to match other community resources

* tw5.com: update French & Japanese savetiddlers plugin docs to note it is now Firefox-only

Please note this update was validated using machine translation
2026-01-04 18:12:56 +00:00
XLBilly
98a61f01bb Add locale support for sort operator (#9400)
* Locale support for sort operator

* Add checkLanguageCode util function

* Update docs & add language code validation

* Replace multiple isDescending with reverse

* Revert "Replace multiple isDescending with reverse"

This reverts commit 793177b8bc.

* Simplify sortTiddler with Intl.Collator

* Add change notes

* Remove comment

* Update makeCompareFunction to support locale

* Update checkLanguageCode

* Add locale support for sortsub

* Add locale support for sort filter run prefix

* Revert "Add locale support for sort filter run prefix"

This reverts commit 9479a156d7.

* Remove checkLanguageCode
Since filters are able to catch errors now

* Update locale example

* Revert " Add locale support for sortsub"

This reverts commit 4a617188fc.

* Revert "Update makeCompareFunction to support locale"

This reverts commit 0ebca08036.

* Update docs

* Update change note
2026-01-04 18:03:54 +00:00
superuser-does
f3fa69e229 Docs: rename 'definition lists' to 'description lists' (#9535)
* docs: Definition Lists to definition lists

* tw5.com: rename Definition Lists to Description Lists throught tiddler

* apply recommendations by @pmario on #9535
2026-01-04 17:37:54 +00:00
Mario Pietsch
e2fb22ade0 Bug report template: Rename description to about (#9546) 2026-01-04 16:50:01 +00:00
peteratkins
7fb8560908 Signing CLA (#9534)
Co-authored-by: Jeremy Ruston <jeremy@jermolene.com>
2026-01-04 13:30:54 +00:00
Jeremy Ruston
ff7814360e Cleanup whitespace in CLA 2026-01-04 13:19:24 +00:00
hsteve11
2d9303c6ff Signing CLA (#9539) 2026-01-04 13:18:46 +00:00
Mario Pietsch
6aee5eb0c7 Add Filter Syntax History link to Filters tiddler (#9541) 2026-01-04 13:16:54 +00:00
Mario Pietsch
6eb881bffe New issue and bug report templates (#9512)
* New issue and bug report templates

* Change bug_report to .md file

* update bug report text

* Move To Reproduce up next to Problem Description

* Fix typo
2026-01-04 13:08:40 +00:00
IchijikuIchigo
2a5ce95d99 Update Japanese translation (#9542)
* Update Japanese translation

* Update Japanese translation

* Update Japanese translation

* Update Japanese translation
2026-01-04 12:14:41 +00:00
XLBilly
bd4fdd8f2e Improve tabs macro accessibility (#9348)
* Improve tabs macro accessibility

* Fix wrong aria-selected attribute

* Patch button widget to allow changing the default aria-checked attribute

* Patch button widget to have aria-checked attribute set to false

* Update tests

* Further fix tests

* Update docs

* Update change notes
2026-01-04 12:13:10 +00:00
XLBilly
09379abd5d Bidirectional improvements for core classes (#9148)
* Bidirectional improvements for core classes

* Fix Control Panel table text direction

* Switch to logical properties

* Add -webkit-margin-* properties

* Improve backward compatibility

* Use supports CSS at-rule for fallback

* Tiddler controls, alert, TOC update.

* tc-tree & testcase update

* .tc-sidebar-scrollable use new syntax

* Snow white use standard value

* Update divider

* Avoid negative logic

* Remove :dir rules at the moment

* Use property procedures

* Add change note

* Update blockquote

* Update unfold banner

* Update tiddlerinfo close button

* Group supports rule

* Update tiddler title icon

* Fix wrong float and margin
2026-01-04 12:02:28 +00:00
XLBilly
c6906120d8 Migrate diff-match-patch to a modern fork (#9511)
* Migrate to diff-match-patch-es & update api

* Update acknowledgements

* Update change notes

* Fix editcost attribute not working

* Make library compatible with ES2017
2026-01-04 11:56:25 +00:00
Jeremy Ruston
c4c60933f4 Merge branch 'tiddlywiki-com' 2026-01-04 11:40:01 +00:00
XLBilly
a3979cda9c Refactor base64 utility functions (#9488)
* Split base64 utility functions to two platforms

* Simplify Nodejs atob

* Update change note

* Update docs

* Move base64Decode & base64Encode back to utils.js

* Add missing use strict
2026-01-04 11:35:02 +00:00
XLBilly
3c8ee86e23 Add macro operator (#9520)
* Add macro operator

* Replace unnecessary usage of wikify widget

* Simplify multiple let widget

* fixup! Replace unnecessary usage of wikify widget

* Update tests

* Update change note

* fixup! Replace unnecessary usage of wikify widget

* Update docs

* fixup! Update tests
2026-01-04 11:31:30 +00:00
XLBilly
921c0174fb Purge IE related docs (#9134) 2026-01-04 09:42:01 +01:00
XLBilly
4196d96adc [DOCS] Update docs about saving via WebDAV (#9543) 2026-01-02 09:42:58 +01:00
Jeremy Ruston
743e99d12d Further fix to #9538 2025-12-29 21:44:57 +00:00
Jeremy Ruston
6beeb23d10 Fix browser storage plugin triggers save (#9538)
* Initial Commit

* Add changenote
2025-12-29 21:09:43 +00:00
wiki-tutor
838fad916d Signing CLA (#9527) 2025-12-28 18:30:51 +00:00
Jeremy Ruston
935e89bd93 Tweak HelloThere thumbnail ordering 2025-12-20 09:25:25 +00:00
Jeremy Ruston
1b8a2caa23 Merge branch 'tiddlywiki-com' 2025-12-19 09:12:27 +00:00
Jeremy Ruston
ecba671bcf Update Newsletter links and editorial 2025-12-19 09:12:06 +00:00
yaisog
fc1e53a777 Enable popup clamping to container (#7898)
* Initial commit

* Show demo tiddler by default

* Correct filename for example tiddler

*sigh*

* Distinguish right/bottom/both/none

* Fix typo

* Revert DefaultTiddlers.tid to original version

* Add changenote

* Fix ESLint errors

* Update documentation tiddlers
2025-12-19 08:44:03 +00:00
Jeremy Ruston
2b6bdcbf97 Merge branch 'tiddlywiki-com' 2025-12-18 21:06:20 +00:00
yaisog
119706a918 Protect cached array data from external mutation (#9495)
* Initial commit

* Add changenote
2025-12-18 18:39:11 +00:00
XLBilly
86d15585b6 Fix github linguist unmarking generated files not working (#9497)
* Fix github linguist unmarking generated files not working

* further fix
2025-12-18 18:16:52 +00:00
IchijikuIchigo
4b824795c8 [ja_JP] Update of Japanese translations (#9499)
* [ja_JP] Japanese translation update from commit: c625e3c, ac83b46, 314ce12

* [ja_JP] Japanese translation update from commit: b0d950f, 381388f, 4dc89f6, 6a39a4e

* [ja_JP] Japanese translation update from commit: 29a567f, 3597e65, 3378497

* [ja_JP] Japanese translation update from commit: 4dc89f6

* [ja_JP] Japanese translation update from commit: b0d950f, 381388f

* [ja_JP] Japanese translation update from commit: 614ba84

* [ja_JP] Japanese translation update from commit: 23a23d9

* [ja_JP] Japanese translation update from commit: 8993572

* [ja_JP] Japanese translation update from commit: 8993572, 697171a, 75e89a1, ee55ab6, a73e03c, ac83b46

* [ja_JP] Japanese translation update from commit: 8993572

* [ja_JP] Japanese translation update from commit: 8993572

* [ja_JP] Japanese translation update from commit: 81862b5

* [ja_JP] Japanese translation update from commit: 81d8d67

* [ja_JP] Japanese translation update from commit: 899a498

* [ja-JP] A little correction to the Japanese translation

* [ja_JP] Japanese translation update from commit: d63a189

* [ja_JP] Japanese translation update from commit: 789d64f

* [ja_JP] Japanese translation update from commit: 5490b78

* [ja-JP] A little correction to the Japanese translation

* [ja-JP] A little correction to the Japanese translation

* [ja-JP] A little correction to the Japanese translation

* [ja-JP] A little correction to the Japanese translation

* [ja_JP] Japanese translation update from commit: 29a567f
2025-12-18 16:13:07 +00:00
Joe Bordes
cd173a959d i18n(ES) update to latest code base (#9311) 2025-12-17 17:53:42 +00:00
Mario Pietsch
86d61e09bd Edit-text widget: rows parameter takes precedence, CM accepts rows now (#9454)
* edit-text rows parameter takes precedence, CM accepts rows now

* Add changenote for PR

* Clarify edit-text widget rows parameter precedence
2025-12-17 15:03:23 +00:00
s793016
7c197f6ecc Fix character disappearing and false matching issues in TiddlyWiki 5.4 Freelink plugin (#9397)
* Update aho-corasick.js

False positive matches

Symptom: Words like "it is", "Choose", "Set up" are incorrectly linked to tiddler "FooBar" when a tiddler titled "xxx x FooBar" exists.

Root cause: The Aho-Corasick algorithm's output merging mechanism in buildFailureLinks caused failure link outputs to be incorrectly merged into intermediate nodes, resulting in false matches.

Fix:

Remove incorrect output merging in buildFailureLinks
Implement proper output collection during search by traversing the failure link chain
Add exact match validation: verify that the matched text exactly equals the pattern before accepting it
Add cycle detection to prevent infinite loops in failure link traversal

* Update text.js

First character disappearing

Symptom: When freelinking is enabled, the first character of matched words disappears (e.g., "The" becomes "he", "Filter" becomes "ilter").

Root cause: When the current tiddler's title was being filtered out, it was done too late in the process (during parse tree construction), causing text rendering issues.

Fix:

Move the current tiddler title filtering to the match validation stage (in processTextWithMatches)
Use substring instead of slice for better stability
Add proper case-insensitive comparison for title matching

* Update text.js

add back description

* Update aho-corasick.js

add back description

* Update tiddlywiki.info

add freelinks plugin for testing

* Update tiddlywiki.info

restore

* Update tiddlywiki.info

add freelinks plugin for test

* Update aho-corasick.js

erase comment

* Update text.js

erase comment

* Update aho-corasick.js

add back some commets

* Update aho-corasick.js

clean comment

* change note #9397

change note #9397

* Update tiddlywiki.info

reversed to original

* Update #9397.tid

update detail

* Update #9397.tid

another link added

* Update #9397.tid

add "release: 5.4.0"

* Update #9397.tid

some format modified
2025-12-17 15:02:42 +00:00
Mario Pietsch
8f8b46dab7 Add diff-text editcost parameter (#9453)
* Add diff-text editcost parameter

* Add changenote

* Add typo for diff-text testing

* Add typo for diff-text testing

* Add typo for diff-text testing
2025-12-17 14:57:56 +00:00
Simon Huber
7b013af240 Update reveal.js to position popups correctly when animated (#9239) 2025-12-17 14:51:32 +00:00
lin onetwo
05f3e6d5a0 Feat/import options (#9465)
* feat: add $:/tags/ImportOptions support on import panel

* feat: style of import option

* fix: conditional show import option

* Delete ImportOptions.tid

* feat: allow multiple import options

* Revert "Delete ImportOptions.tid"

This reverts commit 3487e3dac5.

* DEBUG options

* remove debug files

* Create #9465.tid
2025-12-17 14:44:06 +00:00
XLBilly
48eeb4603a Deprecate and simplify some utility functions (#9251)
* Deprecate some utility functions

* Drop IE support

* Update two function

* Update comment

* Further simplify with arrow function

* Fix node error

* Deprecate logTable

* Deprecate class functions

* Attempt to fix error

* Deprecate two functions

* Remove deprecation for getLocationPath

* Deprecate stringifyNumber, domContains, domMatchesSelector

* Deprecate $tw.utils.each

* Revert "Deprecate $tw.utils.each"

This reverts commit 650df1d575.

* Simplify getFullScreenApis

* Replace LLMap with Map

* Revert "Replace LLMap with Map"

This reverts commit 4410ac194a.

* Move some deprecated functions to deprecated.js

* Remove Opera & MS prefix

* Deprecate getLocationPath

* Fix code style

* Revert "Remove Opera & MS prefix"

This reverts commit e5771c00be.

* Revert "Simplify getFullScreenApis"

This reverts commit 894cb479ea.

* Further simplify toggleClass

* Second attempt to simplify $tw.utils.each

* Revert "Second attempt to simplify $tw.utils.each"

This reverts commit 74cb4f766e.

* Third attempt to simplify $tw.utils.each

* Add missing comma

* Update comments

* Deprecate hopArray

Since it is easy to implement it with some method

* Update change notes

* Deprecate tagToCssSelector

Since tc-tagged-* classes are deprecated
2025-12-17 14:40:47 +00:00
Christian Byron
fc74219c0b Add CeebeeTree comunity card (#9501) 2025-12-17 14:28:23 +00:00
XLBilly
86c4770a28 Fix RSOE from filter operator errors (#9496)
* Fix RSOE from decodebase64

* Update change note

* Revert "Fix RSOE from decodebase64"

This reverts commit 4145f08623.

* Use a more general implementation

* Operator error should let whole filter fail
2025-12-17 14:24:25 +00:00
Jeremy Ruston
ffde2da16c Add @CeeBeeTree to the comunity card for the Newsletter team 2025-12-16 09:20:51 +00:00
Simon Huber
da41a55f29 Root stylesheet refresh - Stylesheets in single <style> tags (#8130)
* Create RootStylesheet.tid

* put stylesheets into single <style> tags

* viewHandler should not be a widget

* format RootStylesheet

* add ROOT_STYLESHEET_TITLE constant

* use let widget in RootStylesheet

* use view widget from #8135 for testing

* reformat RootStylesheet

* update view widget

* update view widget

* update view widget

* update view widget

* update view widget

* update view widget

* trying to fix the date parsing

* refactor view widget with more extensible architecture

* remove performance instrumentation

* hardcode reset and base stylesheets

* adopt ES2017 syntax for view widget

* don't hardcode vanilla/base

* remove unused constructors

* remove unused variable

* make refresh method static

* trying to fix missingThis

* add changenote

* revert change in vanilla/base
2025-12-13 10:17:22 +00:00
XLBilly
846ac9a0dd Add issue types (#9490) 2025-12-13 10:14:54 +00:00
XLBilly
65edda224b Further update eslint configuration (#9474)
* Add comment to disable indent rule

* Enable no-eval rule

And only disable it in evalGlobal

* Diable indent rule in bootprefix

* Update change note

* 更新 boot.js

Co-authored-by: Mario Pietsch <pmariojo@gmail.com>

---------

Co-authored-by: Mario Pietsch <pmariojo@gmail.com>
2025-12-13 10:14:18 +00:00
yaisog
41dac42f3b Fix sluggishness bug in ActionLogWidget (#9489)
* Call getVariableInfo only for non-functions

* Add changenote
2025-12-13 09:34:47 +00:00
XLBilly
87d9754a4e github-linguist improvements (#9384) 2025-12-13 09:31:55 +00:00
Mario Pietsch
5cd3084298 Add pmario PR changenotes (#9442) 2025-12-02 10:31:19 +00:00
XLBilly
4c27c09b4d Update eslint config (#9457)
* Update eslint config

* Switch off max classes restriction
* Enforce tab indent, semicolon
* Warn when there exists unused vars

* Update change note

* Update change note

* Update change note
2025-11-28 13:16:43 +00:00
Christian Höhne
4bb0bc5527 Signing CLA (#9307)
Co-authored-by: Jeremy Ruston <jeremy@jermolene.com>
2025-11-23 18:32:43 +00:00
Jeremy Ruston
6cb333b65b Merge branch 'tiddlywiki-com' 2025-11-22 17:48:43 +00:00
Jeremy Ruston
3378497816 Add link to launch archive 2025-11-22 17:48:28 +00:00
Jeremy Ruston
033d5cf225 Missing documentation updates for #8972 2025-11-22 13:21:01 +00:00
Jeremy Ruston
52d73eb1a8 Fix accidentally committed change to default tiddlers from #8972 2025-11-22 12:59:38 +00:00
Jeremy Ruston
5d1c1eaf87 Introduce multi-valued variables and let filter run prefix (#8972)
* Introduce let filter run prefix for assigning filter run result to a variable

* Get rid of the special behaviour for all[]

Not needed because the input to the filter run is available

* Fix tests

* Fix tests

* Cleanup

* 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

* Docs update

* Introduce letlist widget for assigning lists to variables

Terrible name. Annoyingly, we can't overload the existing let or set widgets.

* Docs update

* Update DefaultTiddlers to highlight the new docs

* Fixed varlist crash with empty parameter

* Switch to triple brace syntax for assigning filtered lists

* Docs update

* Test for multivalued functions

* varlist operator: fixed crash accessing non-existent variable

See https://github.com/TiddlyWiki/TiddlyWiki5/pull/8972#issuecomment-2712068743

* 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.

* 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.

* Missed off 211b135265

* Docs update

* Bug fix

* Introduce round brackets for multi-valued filter operands

Allowing us to drop the varlist operator

* 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

* Fix bug exposed in "Filter Operators" tiddler

See https://github.com/TiddlyWiki/TiddlyWiki5/pull/8972#issuecomment-2740003414

* Fix bug with missing variable attributes

See https://github.com/TiddlyWiki/TiddlyWiki5/pull/8972#issuecomment-2752792329

* Fix bug with round brackets for 2nd parameter onwards

* Allow functions to take multivalued parameters

* Simplify title operator

* Extend title operator to allow negated form to use multi-valued variables

* Remove duplicate test

* Update action-log widget to log multi-valued attributes

* Docs updates

* Fix typos

* Happy linter happy life

* Fix version numbers of from-version procedures

* Another incorrect version number

* Add change note

* Fix filenames of tests

* Typo

* Update let.js

* Docs updates
2025-11-22 12:29:42 +00:00
Jeremy Ruston
d81204c6ab Add Community Card for QA team
@Leilei332 I hope you don't mind, but I've nominated you to be the lead of the QA team. If you're happy to take the role, please could you submit a community card?
2025-11-12 13:03:14 +00:00
lin onetwo
e001c21bf5 Adds commuinity card for LinOnetwo (#9426)
(cherry picked from commit 98ecbf7441)
2025-11-12 12:26:27 +01:00
Jeremy Ruston
ae33a4521d Merge branch 'master' into colour-improvements 2025-09-14 17:22:59 +01:00
Jeremy Ruston
4b83e89d40 Move palettes into plugins 2025-08-06 15:13:10 +01:00
Jeremy Ruston
da41483172 Prepare to split palettes out into plugins 2025-08-06 15:07:15 +01:00
Jeremy Ruston
39488f118b Use TwentyTwenties palette by default 2025-08-06 14:39:48 +01:00
Jeremy Ruston
f9721b029e Merge branch 'master' into colour-improvements 2025-08-06 10:30:30 +01:00
Jeremy Ruston
974904588c Switch to minified version of color.js
See this discussion https://github.com/TiddlyWiki/TiddlyWiki5/pull/8702#issuecomment-3158465038
2025-08-06 10:29:04 +01:00
Jeremy Ruston
38457a4667 Add category field to palettes 2025-06-21 15:14:25 +01:00
Jeremy Ruston
96ef5c8314 Move palettes into category folders 2025-06-21 14:53:49 +01:00
Jeremy Ruston
96d24756ea Add new palettes from https://yatagarasu.tiddlyhost.com 2025-06-21 14:41:49 +01:00
Jeremy Ruston
77c99b418f Change default colour space for interpolation 2025-06-21 11:36:34 +01:00
Jeremy Ruston
7f2a47f303 Adopt more ES2017 features 2025-06-20 17:54:28 +01:00
Jeremy Ruston
dcf0f449c4 Adopt class syntax 2025-06-20 17:46:31 +01:00
Jeremy Ruston
a4c84d727c Remove function wrappers 2025-06-20 17:33:28 +01:00
Jeremy Ruston
5335ebf044 Merge branch 'master' into colour-improvements 2025-06-20 17:23:43 +01:00
Jeremy Ruston
e9d87fb551 Merge branch 'master' into colour-improvements 2025-06-19 17:13:53 +01:00
Jeremy Ruston
54382d6666 Merge branch 'master' into colour-improvements 2025-04-16 14:31:47 +01:00
Jeremy Ruston
e86eb28890 Merge branch 'master' into colour-improvements 2025-03-21 17:24:19 +00:00
Jeremy Ruston
5897b82c51 Fix tests 2025-03-07 16:34:09 +00:00
Jeremy Ruston
5e27462acf TwentyTwenties improvements 2025-03-07 16:23:29 +00:00
Jeremy Ruston
4c95ae546f Fix colour-interpolate with missing colours 2025-03-07 16:23:16 +00:00
Jeremy Ruston
d42e3d36bc Add colour-set-alpha operator 2025-03-07 16:23:00 +00:00
Jeremy Ruston
ee977def52 Include palette entry name in palette entries
Makes debugging easier, and works in CSS and as a style.prop assignment
2025-03-07 16:21:51 +00:00
Jeremy Ruston
30016967b1 Preview: Fix sidebar link colour 2025-03-07 16:21:07 +00:00
Jeremy Ruston
250ee90b07 TwentyTwenties: copy over missing palette entries
These are mostly RGB entries that were previously missing, filled in with values from Vanilla.

The goal is still not to have any direct RGB colours in the palette, just computed colours derived from the base colours
2025-03-06 21:53:45 +00:00
Jeremy Ruston
624cf95197 Fix from mismerging from master 2025-03-06 21:44:16 +00:00
Jeremy Ruston
8467fa333d Merge branch 'master' into colour-improvements 2025-03-06 21:38:49 +00:00
Jeremy Ruston
75ba08556f Merge branch 'master' into colour-improvements 2025-02-18 21:15:13 +00:00
Jeremy Ruston
93f954411b Merge branch 'master' into colour-improvements 2025-02-18 16:54:54 +00:00
Jeremy Ruston
059e439702 Merge branch 'master' into colour-improvements 2025-02-14 16:46:02 +00:00
Jeremy Ruston
2028420e3b Fixes suggested by @pmario 2025-02-14 14:11:08 +00:00
Jeremy Ruston
8b05b725aa Merge branch 'master' into colour-improvements 2025-02-14 12:01:14 +00:00
Jeremy Ruston
d7df7eddb1 Introduce tf.colour function to make palette entries more concise 2025-02-13 16:26:22 +00:00
Jeremy Ruston
0037813b39 Background action demos should require explicit enabling 2025-02-13 09:10:30 +00:00
Jeremy Ruston
377856c6a1 Add temporary guide tiddler at the top 2025-02-12 18:43:19 +00:00
Jeremy Ruston
49969a2f1e Docs for apply filter run prefix 2025-02-12 16:41:37 +00:00
Jeremy Ruston
09f8ab9962 Move from v5.3.7 -> v5.4.0 2025-02-12 12:48:35 +00:00
Jeremy Ruston
422b092eb2 Don't need consent banner anymore
Was just for testing
2025-02-12 12:48:02 +00:00
Jeremy Ruston
067a1a22c6 Move sample background item out of the core
It shouldn't be enabled by default, either, but we'll come back to that
2025-02-12 12:47:41 +00:00
Jeremy Ruston
83c6223617 Refactor the interpolate operator so it can be used with the range operator 2025-02-12 10:05:16 +00:00
Jeremy Ruston
611adadaed Add apply filter run prefix
The map filter run prefix is often used as a way to move a computed value in the input list into a variable so that it can be used as a parameter of a filter operator. The apply filter run prefix extends this idea to make the input list available as variables $1, $2 etc. Unlike the map prefix, the apply filter run is only evaluated once.
2025-02-12 10:04:46 +00:00
Jeremy Ruston
28935a5856 Add hue adjuster to colour-interpolate operator 2025-02-11 20:46:11 +00:00
Jeremy Ruston
71a144f6f9 Typo 2025-02-10 17:01:45 +00:00
Jeremy Ruston
92b7819259 Fix Codemirror colour palette fallbacks 2025-02-10 17:01:37 +00:00
Jeremy Ruston
d2204ae72e Fill in some missing palette entries 2025-02-10 13:48:02 +00:00
Jeremy Ruston
7da70ecf6a Add tabs and recent list to the sidebar 2025-02-10 13:47:41 +00:00
Jeremy Ruston
d8dfc10ea8 Typo 2025-02-09 21:54:31 +00:00
Jeremy Ruston
7df987803d Move generic tests into a background palette
These are the generic tests that should be applied to every palette
2025-02-09 21:40:15 +00:00
Jeremy Ruston
4665bab700 AutoToggle should default to light mode so that static exports are light mode 2025-02-09 14:29:12 +00:00
Jeremy Ruston
e87aaff06b Add tabs to the preview 2025-02-06 22:19:10 +00:00
Jeremy Ruston
7c4938293e Refactor the palette preview macros into a generic widget
Takes the opportunity to simplify things now that we don't have to worry about wikified palettes
2025-02-06 21:27:06 +00:00
Jeremy Ruston
eba73eebcb Missed off 961b26a984 2025-02-06 17:28:28 +00:00
Jeremy Ruston
1f4f164d5e Take advantage of compiled palettes
More robust because previously we couldn't cope with indirect palette entries in these situations
2025-02-06 12:55:45 +00:00
Jeremy Ruston
bfea62b43b The tags macros do not actually need access to the palette 2025-02-06 12:53:40 +00:00
Jeremy Ruston
4c216646a4 Introduce background palettes for plugins 2025-02-06 11:53:07 +00:00
Jeremy Ruston
4fe90a6c73 Add VanillaCherry palette 2025-02-06 11:28:24 +00:00
Jeremy Ruston
961b26a984 Get rid of palette-types...
Instead, we'll special case transforming `<<colour X>>` into `[function[colour],[x]]`

Makes everything much easier and avoids all the kerfuffle of not being able to mix palettes
2025-02-06 11:27:04 +00:00
Jeremy Ruston
0d9ab2e2f6 Remove unneeded scheme processing
Now that we're dealing with the schemes during the import process it is no longer necessary for the compilation process to worry about it.
2025-02-05 18:05:57 +00:00
Jeremy Ruston
796c33bc46 Refactor some variable names 2025-02-05 17:11:51 +00:00
Jeremy Ruston
4d06ecd535 Only allow palettes to import palettes of the same type 2025-02-04 21:18:54 +00:00
Jeremy Ruston
f9e4dd8fd3 Rename custom palette editor to custom palette settings
To avoid confusion with existing usage of "palette editor"
2025-02-04 21:08:02 +00:00
Jeremy Ruston
6e4d7aa7f1 Give the AutoToggle palette a custom editor 2025-02-04 21:03:13 +00:00
Jeremy Ruston
998d5c8d8f The lingo macro shouldn't mess with currentTiddler 2025-02-04 21:02:47 +00:00
Jeremy Ruston
38865a40ad Palette should autocompile when the palette tiddler itself changes 2025-02-04 20:00:17 +00:00
Jeremy Ruston
a5c4d90154 Refactor palette switcher a little 2025-02-04 19:59:39 +00:00
Jeremy Ruston
2e5a988bb3 Clarify comment 2025-02-04 16:44:07 +00:00
Jeremy Ruston
0ee2f286aa Set colour scheme property of root element 2025-02-04 16:40:48 +00:00
Jeremy Ruston
30a7d61e56 Easier for development if the prerelease uses the tw5.com palette 2025-02-04 16:26:46 +00:00
Jeremy Ruston
c7f9dbfc29 Compile colour scheme handling 2025-02-04 16:26:15 +00:00
Jeremy Ruston
c6bb2b51e6 Rethink palette manager
The current content of $:/PaletteManager is moved into $:/PaletteEditor, and $:/PaletteManager repurposed as the control panel palette switcher
2025-02-04 16:11:51 +00:00
Jeremy Ruston
a053f03818 Remove logging 2025-02-04 16:10:38 +00:00
Jeremy Ruston
6970ac24bf Typo 2025-02-03 18:44:58 +00:00
Jeremy Ruston
431149d20c Introduce dynamic colour scheme mechanism
Also introduces palette inheritance

This finally allows us to have a palette that automatically switches between dark and light variants. The mechanism is more flexible that that, and allows for multiple colour schemes (night, morning, day, evening, for example) with automatic switching between them.
2025-02-03 13:26:25 +00:00
Jeremy Ruston
a366d62358 Include tiddler borders in preview
Several of the core palettes have distinctive borders
2025-01-31 17:06:55 +00:00
Jeremy Ruston
a6a91d49b7 Restore accidentally deleted field 2025-01-30 18:35:58 +00:00
Jeremy Ruston
22cf3b25bd Remove infinite loop that was added for testing 2025-01-28 21:40:34 +00:00
Jeremy Ruston
139b61fff1 Proper recursion detection for palettes 2025-01-28 19:18:58 +00:00
Jeremy Ruston
c1fd82f50f Minor cleanups 2025-01-28 15:44:42 +00:00
Jeremy Ruston
effeed7ade Remove extraneous logging 2025-01-27 21:47:03 +00:00
Jeremy Ruston
546e438943 Allow background actions to be scoped by platform 2025-01-27 21:45:17 +00:00
Jeremy Ruston
efcd23993e Merge branch 'master' into colour-improvements 2025-01-27 16:55:03 +00:00
Jeremy Ruston
0c8aad49f9 Replace accumulate-palette-entries with new changecount filter
Much more direct and efficient
2025-01-27 11:09:18 +00:00
Jeremy Ruston
317e1245c8 Introduce an improved but temporary cache invalidation method for palettes 2025-01-26 17:54:47 +00:00
Jeremy Ruston
d2bbc56c78 Move modern palettes to correct directory 2025-01-25 17:28:06 +00:00
Jeremy Ruston
8957424e55 Start adding tests for palette operations 2025-01-25 17:28:00 +00:00
Jeremy Ruston
0fd5b04b9a Merge branch 'master' into colour-improvements 2025-01-25 11:05:21 +00:00
Jeremy Ruston
3ea7cd3bf7 Palette editors no longer need to manually recompile the palette 2025-01-25 11:05:13 +00:00
Jeremy Ruston
7513e4426c Allow text editor type=color for colours not in hex RGB format
See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/color#value
2025-01-25 11:03:53 +00:00
Jeremy Ruston
0dfde0660a Detect darkmode at startup 2025-01-24 18:34:55 +00:00
Jeremy Ruston
8c619fd86e Compile palette at startup 2025-01-24 18:32:00 +00:00
Jeremy Ruston
407e58f837 Add a basic dark palette
Obviously some things are hardcoded at the moment
2025-01-24 11:17:55 +00:00
Jeremy Ruston
9588b7f1a3 Autocompile palettes when they change 2025-01-24 11:16:31 +00:00
Jeremy Ruston
28c1e77b60 Make sure rootwidget is available before background actions start 2025-01-24 11:15:27 +00:00
Jeremy Ruston
0baf395030 Merge background actions and media tracker from #8555
The changes in #8555 are needed in order to be able to offer the desired user experience for dark mode changes.
2025-01-23 18:16:10 +00:00
Jeremy Ruston
9681b0deda Merge branch 'master' into colour-improvements 2025-01-23 18:15:44 +00:00
Jeremy Ruston
62fb916a68 Merge branch 'master' into colour-improvements 2025-01-23 14:37:23 +00:00
Jeremy Ruston
3614236cfc Paldette and style tweaks 2025-01-21 16:36:00 +00:00
Jeremy Ruston
c75f50e99a Fix palette switcher used in the sidebar
by adding a new "thumbnails" parameter to the template
2025-01-09 14:51:48 +00:00
Jeremy Ruston
55d9e92032 Edit text widget shouldn't fully refresh when default attribute changes 2025-01-09 10:51:02 +00:00
Jeremy Ruston
2edcf0f46b Refactor TwentyTwenties editor 2025-01-09 10:50:37 +00:00
Jeremy Ruston
69363bf7ef Palette manager should recompile palette on edits 2025-01-08 21:38:08 +00:00
Jeremy Ruston
2b0c634fb8 Refactor actions for recompiling current palette 2025-01-08 21:37:44 +00:00
Jeremy Ruston
28167adc22 Add a palette that automatically switches between dark and light
Will requires #7999 to work fully; for the moment when you switch between dark and light you will see the preview change, and then next time you select the palette it will be in the correct mode
2025-01-08 16:02:05 +00:00
Jeremy Ruston
bd4b3e4107 Fix some old-style palette references 2025-01-08 16:00:29 +00:00
Jeremy Ruston
2cbd1080fa Fix more TwentyTwenties entries 2025-01-08 15:57:39 +00:00
Jeremy Ruston
a4293068bf Clarify method name 2025-01-08 14:49:19 +00:00
Jeremy Ruston
d1ce54806f Download button should use palette colours 2025-01-08 10:06:56 +00:00
Jeremy Ruston
6b39d6aa43 Fix editing colours that are not in 6 digit hex format
See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/color#value
2025-01-08 09:59:04 +00:00
Jeremy Ruston
5d1cf251b9 Add best contrast operator 2025-01-07 17:40:23 +00:00
Jeremy Ruston
c02c82557b Add a primitive custom palette editor 2025-01-02 17:10:51 +00:00
Jeremy Ruston
b90b449ceb Use interpolation to derive colours 2025-01-02 17:10:29 +00:00
Jeremy Ruston
6a06df79c4 Fix palette switcher 2024-12-27 12:00:50 +00:00
Jeremy Ruston
2558dc0b10 TwentyTwenties palette: use interpolation 2024-12-22 19:20:05 +00:00
Jeremy Ruston
2eee3bfcd6 Merge branch 'master' into colour-improvements 2024-12-21 09:48:10 +00:00
Jeremy Ruston
2685fa7c4e Improvements to TwentyTwenties palettes
Still a work in progress, but getting more coherent
2024-12-15 18:55:17 +00:00
Jeremy Ruston
3c44532551 Barebones docs for colour spaces 2024-12-15 18:49:04 +00:00
Jeremy Ruston
2640406f5b Include contrast value in contrast errors 2024-12-15 18:48:48 +00:00
Jeremy Ruston
c1e36a1e5f Add colour-interpolate operator 2024-12-15 18:48:28 +00:00
Jeremy Ruston
ae1d9f5b86 Add colour-get-oklch operator 2024-12-15 13:04:33 +00:00
Jeremy Ruston
1df0ac486b Refactor colour-oklch operator to colour-set-oklch 2024-12-15 11:56:49 +00:00
Jeremy Ruston
b1fcb18d9e Refactor for clearer variable names 2024-12-15 11:49:23 +00:00
Jeremy Ruston
5a6eea7fa2 Make the alert and notification previews be optional
Via some future UI
2024-12-14 17:33:02 +00:00
Jeremy Ruston
6fe16bc71a Fix colour for site title 2024-12-14 17:32:43 +00:00
Jeremy Ruston
6a66c49261 Merge branch 'master' into colour-improvements 2024-12-14 17:12:51 +00:00
Jeremy Ruston
fc695e7a50 Merge branch 'master' into colour-improvements 2024-12-05 17:38:50 +00:00
Jeremy Ruston
9efcad9360 Display test results at the bottom of the palette switcher 2024-11-19 09:09:11 +00:00
Jeremy Ruston
cd5bbcda8d Add contrast checks to TwentyTwenties palette
I think I might be building a programming language for writing palettes...
2024-11-18 18:15:53 +00:00
Jeremy Ruston
aa69c3ae91 Add colour-contrast operator 2024-11-18 18:15:20 +00:00
Jeremy Ruston
76f2decf8b Start fixing the TwentyTwenties palettes 2024-11-18 16:40:49 +00:00
Jeremy Ruston
84bef54802 Docs for colour-oklch operator 2024-11-18 16:38:54 +00:00
Jeremy Ruston
3507b0f952 Fix filtered palette previews 2024-11-17 16:11:10 +00:00
Jeremy Ruston
b0828cc099 Add notification preview 2024-11-17 16:10:57 +00:00
Jeremy Ruston
32ac67166a Add alert preview 2024-11-17 14:45:52 +00:00
Jeremy Ruston
b2d0c22d75 Componentise more preview components 2024-11-17 14:45:41 +00:00
Jeremy Ruston
cfabc92945 Update contrastcolour macro to use color.js 2024-11-16 14:37:14 +00:00
Jeremy Ruston
809465b6e2 Merge branch 'master' into colour-improvements 2024-11-16 14:22:19 +00:00
Jeremy Ruston
8b59b6166e Palette tweaks
These filtered palettes are still just experiments with the techniques, and not yet a serious palette
2024-11-14 17:45:10 +00:00
Jeremy Ruston
3faf9bae5c Fix previews of filtered palettes 2024-11-14 17:40:58 +00:00
Jeremy Ruston
c2ee0727a5 Merge branch 'master' into colour-improvements 2024-11-12 20:57:46 +00:00
Jeremy Ruston
7de5f40884 Merge branch 'master' into colour-improvements 2024-11-12 20:37:01 +00:00
Jeremy Ruston
21cd3b86ad Merge branch 'master' into colour-improvements 2024-11-12 19:51:52 +00:00
Jeremy Ruston
ad1b0fdddd Palette tweaks 2024-11-12 19:26:20 +00:00
Jeremy Ruston
1db8cf7fe5 Merge branch 'master' into colour-improvements 2024-11-12 19:18:54 +00:00
Jeremy Ruston
46da1619af Add aria labels to palette switcher
Co-authored-by: Mario Pietsch <pmariojo@gmail.com>
2024-11-11 15:52:55 +00:00
Jeremy Ruston
bad9517153 Another fix for filtered palette previews 2024-11-10 16:52:49 +00:00
Jeremy Ruston
38d5daaf12 Improve chooser chosen item highlight 2024-11-10 16:52:23 +00:00
Jeremy Ruston
297ae7eccb Fix preview of filtered palettes 2024-11-10 09:52:43 +00:00
Jeremy Ruston
b54d56ec47 Support for imported palettes
Also consolidate the palette entries into a temporary palette before compiling them

Note that imported palettes is not currently recursive
2024-11-09 19:03:54 +00:00
Jeremy Ruston
4e2f2bebd0 Avoid redefining the colour function by adding a configuration variable 2024-11-09 17:02:00 +00:00
Jeremy Ruston
f88915728f Introduce new static palette architecture 2024-11-09 16:15:33 +00:00
Jeremy Ruston
1be89a28bf Make preview templates tag driven and extensible 2024-11-08 09:12:48 +00:00
Jeremy Ruston
250e57cd79 Remove wikify operator and refactor palette preview
The implementation of the palette preview is much less elegant like this, but it does work
2024-11-07 18:30:39 +00:00
Jeremy Ruston
a4d930322e Testing CI 2024-11-06 08:32:42 +00:00
Jeremy Ruston
c3ce9cafb7 Testing Netlify CI 2024-11-06 08:22:42 +00:00
Jeremy Ruston
348f7177a7 Remove logging 2024-11-05 22:42:46 +00:00
Jeremy Ruston
c6074402bb Restore default styling for chosen chooser item
See https://github.com/TiddlyWiki/TiddlyWiki5/pull/8702#discussion_r1816584692
2024-11-05 10:24:01 +00:00
Jeremy Ruston
23eccd1df6 Merge branch 'master' into colour-improvements 2024-11-04 19:26:16 +00:00
Jeremy Ruston
ff5c846130 Indentation for palette switcher 2024-11-04 18:12:00 +00:00
Jeremy Ruston
81b7bb4124 Merge branch 'master' into colour-improvements 2024-11-04 10:39:28 +00:00
Jeremy Ruston
a8fb07137d Add expertimental colour-oklch operator 2024-11-03 16:13:09 +00:00
Jeremy Ruston
85fa913b1c Merge branch 'master' into colour-improvements 2024-11-03 16:10:47 +00:00
Jeremy Ruston
bc0fde6853 Fix palette chooser when displayed in "Tools" dropdown 2024-10-31 18:03:50 +00:00
Jeremy Ruston
4f2754d16c Merge branch 'master' into colour-improvements 2024-10-31 17:46:30 +00:00
Jeremy Ruston
4445111a08 Merge branch 'master' into colour-improvements 2024-10-30 20:38:58 +00:00
Jeremy Ruston
151f61adc0 Palette chooser styling tweaks 2024-10-25 11:03:21 +01:00
Jeremy Ruston
fc369415e4 Improve colour palette switcher with previews 2024-10-25 09:28:33 +01:00
Jeremy Ruston
b5a22e3e9e Remove obsolete comment 2024-10-25 09:28:17 +01:00
Jeremy Ruston
d372729ed0 Add colour-lighten and colour-darken operators 2024-10-24 12:01:50 +01:00
Jeremy Ruston
96b85edfa2 Fix up the tests 2024-10-24 10:46:48 +01:00
Jeremy Ruston
4af573aaf9 Fix nested colour definitions 2024-10-23 09:39:18 +01:00
Jeremy Ruston
1e5c69eb99 Fix typo 2024-10-23 08:25:24 +01:00
Jeremy Ruston
e1e73d2aa0 Merge branch 'master' into colour-improvements 2024-10-23 08:22:01 +01:00
Jeremy Ruston
a23ee165d8 Rewrite colour macro as a function
Using the new wikify operator.

Currently has a bug whereby redirected colours (like "tiddler-background") do not work. Direct colours like "background" do work.

Note the hacks needed to makeFakeWidgetWithVariables work
2024-10-22 17:26:04 +01:00
Jeremy Ruston
467a1a47cc Introduce wikify operator
Really just syntactic sugar for the wikify widget
2024-10-22 17:24:51 +01:00
Jeremy Ruston
93d1c05ca7 Include colour.js license and mark version number 2024-10-21 17:25:56 +01:00
Jeremy Ruston
fb9c0d6a5a Replace 12 year old CSS colour parsing library
The replacement library from https://colorjs.io/ is much, much larger but I think we can develop a custom build that uses treeshaking to whittle the code down to the bits that we need. @linonetwo does that sound feasible?

I intend the explore further improvements but I wanted to start by establishing a library that can do modern P3 and OKLCH colour calculations.
2024-10-21 09:03:39 +01:00
379 changed files with 9880 additions and 3614 deletions

4
.gitattributes vendored Normal file
View File

@@ -0,0 +1,4 @@
boot/** -linguist-generated
**/tiddlywiki.files linguist-language=JSON
**/tiddlywiki.info linguist-language=JSON
**/plugin.info linguist-language=JSON

62
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,62 @@
---
name: Bug report
about: Create a report to help us improve TiddlyWiki 5
title: "[Report] "
type: report
---
<!-- Remove elements, that you do not need -->
<!-- Add screenshots where needed -->
**Problem Description**
<!-- Describe your problem: A clear and concise description of what your problem is -->
**To Reproduce**
Steps to reproduce the behavior:
1. At https://tiddlywiki.com
2. Click on ...
3. Scroll down to ...
4. See ...
**Expected behavior**
As a user,
<!-- As a developer, -->
I would expect ...
**TiddlyWiki Configuration**
<!-- Please complete the following information -->
- Report created with: [Wiki Information](https://tiddlywiki.com/#%24%3A%2Fcore%2Fui%2FControlPanel%2FWikiInformation)
<!-- Your report comes here -->
<!-- or -->
<!-- Add it manually -->
- Version: <!-- e.g. v5.3.8 -->
- Saving mechanism: <!-- e.g. Node.js, TiddlyDesktop, TiddlyHost etc -->
- Plugins installed: <!-- e.g. Freelinks, TiddlyMap ... other 3rd party plugins -->
**Desktop**
<!-- Please complete the following information -->
- OS: <!-- e.g. iOS -->
- Browser: <!-- e.g. chrome, safari, FireFox -- Version: -->
**Smartphone**
<!-- Please complete the following information -->
- Device: <!-- e.g. iPhone6 -->
- OS: <!-- e.g. iOS8.1 -->
- Browser: <!-- e.g. stock browser, safari, FireFox -- Version: -->
**Additional context**
<!-- Add any other context about the problem here. -->

View File

@@ -1,67 +0,0 @@
name: Bug report
description: Create a report to help us improve TiddlyWiki 5
title: "[BUG] "
body:
- type: textarea
id: Describe
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
id: Expected
attributes:
label: Expected behavior
description: A clear and concise description of what you expected to happen.
validations:
required: false
- type: textarea
id: Reproduce
attributes:
label: To Reproduce
description: "Steps to reproduce the behavior:"
placeholder: |
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
validations:
required: false
- type: textarea
id: Screenshots
attributes:
label: Screenshots
description: If applicable, add screenshots to help explain your problem.
placeholder: Drag image here to upload screenshot!
validations:
required: false
- type: textarea
id: Configuration
attributes:
label: TiddlyWiki Configuration
description: please complete the following information
placeholder: |
- Version [e.g. v5.1.24]
- Saving mechanism [e.g. Node.js, TiddlyDesktop, TiddlyHost etc]
- Plugins installed [e.g. Freelinks, TiddlyMap]
### Desktop (please complete the following information):
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
### Smartphone (please complete the following information):
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
validations:
required: true
- type: textarea
id: Context
attributes:
label: Additional context
description: Add any other context about the problem here.

View File

@@ -4,17 +4,23 @@ about: Suggest an idea for TiddlyWiki 5
title: "[IDEA]"
labels: ''
assignees: ''
type: idea
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Is your idea related to a problem? Please describe.**
A clear and concise description of what the problem is. Eg:
As a user, I would like [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -8,6 +8,8 @@ On the server this file is executed directly to boot TiddlyWiki. In the browser,
\*/
/* eslint-disable @stylistic/indent */
var _boot = (function($tw) {
/*jslint node: true, browser: true */
@@ -44,12 +46,8 @@ $tw.utils.hop = function(object,property) {
return object ? Object.prototype.hasOwnProperty.call(object,property) : false;
};
/*
Determine if a value is an array
*/
$tw.utils.isArray = function(value) {
return Object.prototype.toString.call(value) == "[object Array]";
};
/** @deprecated Use Array.isArray instead */
$tw.utils.isArray = value => Array.isArray(value);
/*
Check if an array is equal by value and by reference.
@@ -128,35 +126,22 @@ $tw.utils.pushTop = function(array,value) {
return array;
};
/*
Determine if a value is a date
*/
$tw.utils.isDate = function(value) {
return Object.prototype.toString.call(value) === "[object Date]";
};
/** @deprecated Use instanceof Date instead */
$tw.utils.isDate = value => value instanceof Date;
/*
Iterate through all the own properties of an object or array. Callback is invoked with (element,title,object)
*/
/** @deprecated Use array iterative methods instead */
$tw.utils.each = function(object,callback) {
var next,f,length;
if(object) {
if(Object.prototype.toString.call(object) == "[object Array]") {
for(f=0, length=object.length; f<length; f++) {
next = callback(object[f],f,object);
if(next === false) {
break;
}
}
if(Array.isArray(object)) {
object.every((element,index,array) => {
const next = callback(element,index,array);
return next !== false;
});
} else {
var keys = Object.keys(object);
for(f=0, length=keys.length; f<length; f++) {
var key = keys[f];
next = callback(object[key],key,object);
if(next === false) {
break;
}
}
Object.entries(object).every(entry => {
const next = callback(entry[1], entry[0], object);
return next !== false;
});
}
}
};
@@ -331,32 +316,13 @@ $tw.utils.htmlDecode = function(s) {
return s.toString().replace(/&lt;/mg,"<").replace(/&nbsp;/mg,"\xA0").replace(/&gt;/mg,">").replace(/&quot;/mg,"\"").replace(/&amp;/mg,"&");
};
/*
Get the browser location.hash. We don't use location.hash because of the way that Firefox auto-urldecodes it (see http://stackoverflow.com/questions/1703552/encoding-of-window-location-hash)
*/
$tw.utils.getLocationHash = function() {
var href = window.location.href;
var idx = href.indexOf('#');
if(idx === -1) {
return "#";
} else if(href.substr(idx + 1,1) === "#" || href.substr(idx + 1,3) === "%23") {
// Special case: ignore location hash if it itself starts with a #
return "#";
} else {
return href.substring(idx);
}
};
/** @deprecated Use window.location.hash instead. */
$tw.utils.getLocationHash = () => window.location.hash;
/*
Pad a string to a given length with "0"s. Length defaults to 2
*/
$tw.utils.pad = function(value,length) {
length = length || 2;
var s = value.toString();
if(s.length < length) {
s = "000000000000000000000000000".substr(0,length - s.length) + s;
}
return s;
/** @deprecated Pad a string to a given length with "0"s. Length defaults to 2 */
$tw.utils.pad = function(value,length = 2) {
const s = value.toString();
return s.padStart(length, "0");
};
// Convert a date into UTC YYYYMMDDHHMMSSmmm format
@@ -630,7 +596,7 @@ $tw.utils.evalGlobal = function(code,context,filename,sandbox,allowGlobals) {
// Compile the code into a function
var fn;
if($tw.browser) {
fn = window["eval"](code + "\n\n//# sourceURL=" + filename);
fn = window["eval"](code + "\n\n//# sourceURL=" + filename); // eslint-disable-line no-eval -- See https://github.com/TiddlyWiki/TiddlyWiki5/issues/6839
} else {
if(sandbox){
fn = vm.runInContext(code,sandbox,filename)
@@ -2801,6 +2767,8 @@ return $tw;
});
/* eslint-enable @stylistic/indent */
if(typeof(exports) !== "undefined") {
exports.TiddlyWiki = _boot;
} else {

View File

@@ -12,6 +12,8 @@ See Boot.js for further details of the boot process.
\*/
/* eslint-disable @stylistic/indent */
var _bootprefix = (function($tw) {
"use strict";
@@ -114,6 +116,8 @@ return $tw;
});
/* eslint-enable @stylistic/indent */
if(typeof(exports) === "undefined") {
// Set up $tw global for the browser
window.$tw = _bootprefix(window.$tw);

View File

@@ -0,0 +1,14 @@
title: @Christian_Byron
tags: Community/Person
fullname: Christian Byron
talk.tiddlywiki.org: Christian_Byron
github: ceebeetree
linkedin: www.linkedin.com/in/christian-byron-b84a594/
avatar: /9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAgICAgJCAkKCgkNDgwODRMREBARExwUFhQWFBwrGx8bGx8bKyYuJSMlLiZENS8vNUROQj5CTl9VVV93cXecnNEBCAgICAkICQoKCQ0ODA4NExEQEBETHBQWFBYUHCsbHxsbHxsrJi4lIyUuJkQ1Ly81RE5CPkJOX1VVX3dxd5yc0f/CABEIACAAIAMBIgACEQEDEQH/xAAuAAEBAAMBAAAAAAAAAAAAAAAHBgEDBQQBAAMBAAAAAAAAAAAAAAAAAAABAwX/2gAMAwEAAhADEAAAADv2xtJlY03sqePW3ARS1RSydIhcH//EACcQAAICAgIBAgYDAAAAAAAAAAECAwQFEQASMRMhBhBBk8HRIzJx/9oACAEBAAE/AMFQxs+NExqJLMCwYE+SOT4bF3qr+hAIpRsDQ6lWH0Yco4S/eVniRVQHXZzrZ5dwGQpQtNII2RfJVvHMRl5cbKxC94n/ALp+RxfiKpNcgMMUqPIwjcnWip/I5XtUowaL3Ujir/xt79Glb6/4OZ7MV5oEpUzuIa7MPB14A5jpoYLsEsydo1bbLre+CWEEEYab7Uf74ZYSSThpvtR/vmRmhnuzywp1jZtquta+VPM49qlcy24lf017At7g8uZnHrUsGK3Ez+m3UBvcnXy//8QAHhEAAgEFAAMAAAAAAAAAAAAAAQIDAAQRIkEyUaH/2gAIAQIBAT8AmiuVlZkLEeQOflJPcvMAF0z65V+h0YIW52rBDuxUrztf/8QAIxEBAAEDAwMFAAAAAAAAAAAAAgEAAxEEBSMSQcEiMVJxof/aAAgBAwEBPwC/Z1ZvNBOYz1Gc/lDUat3ySPRM/H2P3W4hcbIldpxnxW3BcjQk9oznzX//2Q==
Hello ~TiddlyWikiers - I have been a long time fan, recent contributor to the TW community.
Recently I have volunteered to run the [[TiddlyWiki Newsletter|https://tiddlywiki.substack.com/]] to spread the great news about TW.
I have been in the IT industry for about thirty years, mostly as a consultant and technical arcitect.
More recently I went back to study a masters in IT focussing on AI and data science.
Now my partner and I have started our own business ([[Sphere Innovations|https://sphere-innovations.com.au]]) - in consulting and building web applications for small to medium size businesses here in Australia.

View File

@@ -0,0 +1,19 @@
avatar: /9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAgICAgJCAkKCgkNDgwODRMREBARExwUFhQWFBwrGx8bGx8bKyYuJSMlLiZENS8vNUROQj5CTl9VVV93cXecnNEBCAgICAkICQoKCQ0ODA4NExEQEBETHBQWFBYUHCsbHxsbHxsrJi4lIyUuJkQ1Ly81RE5CPkJOX1VVX3dxd5yc0f/CABEIACAAIAMBIgACEQEDEQH/xAAuAAEAAwEBAAAAAAAAAAAAAAAGAwQHAgUBAAMBAAAAAAAAAAAAAAAAAAACAwT/2gAMAwEAAhADEAAAAOfCWAMdKKetM4wOvY5OcvZnrYf/xAApEAACAQQBBAECBwAAAAAAAAABAgMABAURQQYSIVETFCIxMkJicYKh/9oACAEBAAE/AEtysaStr7mPaPeuazWdMM4gEnfPryW8hBUuZvou2RXRxyreBWPmgyNqs8f8MOQalhdY7Vz+R4/s/qfP+1edNi/zl7HDcFbmS3E8CcMR4INP0PkBhklIm+sZNtFtQiV0nj57Owl+dSrSTFgD6/CtH4VV9lU3oAbPngAVY389lc5URuUZkMxhnR4pvW0VwDqsP1FNmLWYqCpikMbngmliJNY+aKzyTxXS6lRAyg/u5rq+5x2RsuyTa3MQMlvKniRGThTUd1JYXUdzAwDvqVxGdRXMbfrVOD7HBrG3mNEsU8z98TRhl9eRzX//xAAcEQACAgIDAAAAAAAAAAAAAAABAgARAzESIVH/2gAIAQIBAT8ARuXZPsul3Eoje5lBQWBP/8QAGREAAwEBAQAAAAAAAAAAAAAAAAECEiER/9oACAEDAQE/AM98Lk7LJe20z//Z
created: 20251110102157310
first-sighting: 2019-03-01
fullname: Lin Onetwo
github: linonetwo
homepage: https://wiki.onetwo.website/
modified: 20251111184556193
tags: Community/Person Community/Team/Contributors
talk.tiddlywiki.org: linonetwo
title: @linonetwo
type: text/vnd.tiddlywiki
Since 2014, when I started college, I've been on a quest for a lifelong PKM tool. I cherish my life and all my experiences, and I dont want to forget any of them. When Im deeply focused on a task, its easy to lose sight of other important parts of my life—so I needed a system to help me stay balanced.
Early on, I tried TiddlyWiki several times, but I was initially put off by its save mechanism and markup editing. That changed when I discovered an auto-backup script, which gave me the confidence to fully commit. Over time, I improved the script and eventually transitioned to using TidGi-Desktop and TidGi-Mobile.
Today, my TiddlyWiki holds all my game design ideas and progress logs—it has truly become my second brain. With the help of LLM-powered programming tools, Ive enhanced it with numerous plugins, allowing me to manage my mind in a more programmable and structured way. As a game developer, TiddlyWiki isn't the core of my professional work; But I've invested so much time because it's fundamentally about upgrading my mind.
Most of my notes are open by default and shared publicly on my homepage as a digital garden.

View File

@@ -1,6 +0,0 @@
title: Newsletter Team
tags: Community/Team
modified: 20250909171928024
created: 20250909171928024
The Newsletter Team is responsible for producing the TiddlyWiki Newsletter, a monthly email newsletter that highlights news, updates, and community contributions related to TiddlyWiki.

View File

@@ -0,0 +1,11 @@
title: Quality Assurance Team
created: 20251112125742296
modified: 20251112125742296
tags: Community/Team
team:
leader: @Leilei332
title: Quality Assurance Team
The Quality Assurance Team is responsible for ensuring the quality and reliability of TiddlyWiki releases. This includes reviewing code submissions, testing new features, identifying bugs, and verifying that fixes are effective.

View File

@@ -0,0 +1,15 @@
title: TiddlyWiki Newsletter Team
tags: Community/Team
modified: 20251219090709874
created: 20250909171928024
leader: @Christian_Byron
The Newsletter Team is responsible for producing the [[TiddlyWiki Newsletter]]. We would love to have your help if you would like to get involved.
! Audience
The newsletter is intended for TiddlyWiki end users who do not track all the discussions on https://talk.tiddlywiki.org/.
Coverage of developer topics such as JavaScript and intricate wikitext should be handled thoughtfully to avoid alienating the core audience of end users.
Subscribing to the newsletter is intended to give people confidence that they will not miss any important developments.

View File

@@ -1,5 +1,5 @@
title: Community/Team
modified: 20250909171928024
created: 20250909171928024
list: [[Project Team]] [[Core Team]] [[Documentation Team]] [[MultiWikiServer Team]] [[Newsletter Team]] [[Infrastructure Team]] [[Succession Team]]
list: [[Project Team]] [[Core Team]] [[Documentation Team]] [[Quality Assurance Team]] [[Infrastructure Team]] [[MultiWikiServer Team]] [[Newsletter Team]] [[Succession Team]]

View File

@@ -0,0 +1,30 @@
/*\
title: $:/core-modules/modules/utils/base64.js
type: application/javascript
module-type: utils-node
Base64 UTF-8 utlity functions.
\*/
"use strict";
const{ TextEncoder, TextDecoder } = require("node:util");
exports.btoa = binstr => Buffer.from(binstr, "binary").toString("base64");
exports.atob = b64 => Buffer.from(b64, "base64").toString("binary");
function base64ToBytes(base64) {
const binString = exports.atob(base64);
return Uint8Array.from(binString, m => m.codePointAt(0));
};
function bytesToBase64(bytes) {
const binString = Array.from(bytes, byte => String.fromCodePoint(byte)).join("");
return exports.btoa(binString);
};
exports.base64EncodeUtf8 = str => bytesToBase64(new TextEncoder().encode(str));
exports.base64DecodeUtf8 = str => new TextDecoder().decode(base64ToBytes(str));

View File

@@ -0,0 +1,95 @@
/*\
title: $:/core-server/modules/utils/escapecss.js
type: application/javascript
module-type: utils-node
Provides CSS.escape() functionality.
\*/
"use strict";
exports.escapeCSS = (function() {
// see also https://drafts.csswg.org/cssom/#serialize-an-identifier
/* eslint-disable */
/*! https://mths.be/cssescape v1.5.1 by @mathias | MIT license */
return function(value) {
if (arguments.length == 0) {
throw new TypeError('`CSS.escape` requires an argument.');
}
var string = String(value);
var length = string.length;
var index = -1;
var codeUnit;
var result = '';
var firstCodeUnit = string.charCodeAt(0);
while (++index < length) {
codeUnit = string.charCodeAt(index);
// Note: theres no need to special-case astral symbols, surrogate
// pairs, or lone surrogates.
// If the character is NULL (U+0000), then the REPLACEMENT CHARACTER
// (U+FFFD).
if (codeUnit == 0x0000) {
result += '\uFFFD';
continue;
}
if (
// If the character is in the range [\1-\1F] (U+0001 to U+001F) or is
// U+007F, […]
(codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit == 0x007F ||
// If the character is the first character and is in the range [0-9]
// (U+0030 to U+0039), […]
(index == 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
// If the character is the second character and is in the range [0-9]
// (U+0030 to U+0039) and the first character is a `-` (U+002D), […]
(
index == 1 &&
codeUnit >= 0x0030 && codeUnit <= 0x0039 &&
firstCodeUnit == 0x002D
)
) {
// https://drafts.csswg.org/cssom/#escape-a-character-as-code-point
result += '\\' + codeUnit.toString(16) + ' ';
continue;
}
if (
// If the character is the first character and is a `-` (U+002D), and
// there is no second character, […]
index == 0 &&
length == 1 &&
codeUnit == 0x002D
) {
result += '\\' + string.charAt(index);
continue;
}
// If the character is not handled by one of the above rules and is
// greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or
// is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to
// U+005A), or [a-z] (U+0061 to U+007A), […]
if (
codeUnit >= 0x0080 ||
codeUnit == 0x002D ||
codeUnit == 0x005F ||
codeUnit >= 0x0030 && codeUnit <= 0x0039 ||
codeUnit >= 0x0041 && codeUnit <= 0x005A ||
codeUnit >= 0x0061 && codeUnit <= 0x007A
) {
// the character itself
result += string.charAt(index);
continue;
}
// Otherwise, the escaped character.
// https://drafts.csswg.org/cssom/#escape-a-character
result += '\\' + string.charAt(index);
}
return result;
};
/* eslint-enable */
})();

View File

@@ -5,3 +5,4 @@ TiddlyWiki incorporates code from these fine OpenSource projects:
* [[The Stanford Javascript Crypto Library|http://bitwiseshiftleft.github.io/sjcl/]]
* [[The Jasmine JavaScript Test Framework|https://jasmine.github.io/]]
* [[modern-normalize by Sindre Sorhus|https://github.com/sindresorhus/modern-normalize]]
* [[diff-match-patch-es by antfu|https://github.com/antfu/diff-match-patch-es]]

View File

@@ -57,6 +57,7 @@ LayoutSwitcher/Caption: Layout
LoadedModules/Caption: Loaded Modules
LoadedModules/Hint: These are the currently loaded tiddler modules linked to their source tiddlers. Any italicised modules lack a source tiddler, typically because they were setup during the boot process.
Palette/Caption: Palette
Palette/CustomSettings/Prompt: Custom settings for current palette: <<palette-link>>
Palette/Editor/Clone/Caption: clone
Palette/Editor/Clone/Prompt: It is recommended that you clone this shadow palette before editing it
Palette/Editor/Delete/Hint: delete this entry from the current palette

View File

@@ -1,5 +1,6 @@
title: $:/language/
Alerts: Alerts
AboveStory/ClassicPlugin/Warning: It looks like you are trying to load a plugin designed for ~TiddlyWiki Classic. Please note that [[these plugins do not work with TiddlyWiki version 5.x.x|https://tiddlywiki.com/#TiddlyWikiClassic]]. ~TiddlyWiki Classic plugins detected:
BinaryWarning/Prompt: This tiddler contains binary data
ClassicWarning/Hint: This tiddler is written in TiddlyWiki Classic wiki text format, which is not fully compatible with TiddlyWiki version 5. See https://tiddlywiki.com/static/Upgrading.html for more details.
@@ -29,6 +30,7 @@ Error/DeserializeOperator/MissingOperand: Filter Error: Missing operand for 'des
Error/DeserializeOperator/UnknownDeserializer: Filter Error: Unknown deserializer provided as operand for the 'deserialize' operator
Error/Filter: Filter error
Error/FilterSyntax: Syntax error in filter expression
Error/FilterPragma: Filter Error: Unknown filter pragma
Error/FilterRunPrefix: Filter Error: Unknown prefix for filter run
Error/IsFilterOperator: Filter Error: Unknown parameter for the 'is' filter operator
Error/FormatFilterOperator: Filter Error: Unknown suffix for the 'format' filter operator

View File

@@ -0,0 +1,116 @@
/*\
title: $:/core/modules/background-actions.js
type: application/javascript
module-type: global
Class to dispatch actions when filters change
\*/
"use strict";
class BackgroundActionDispatcher {
constructor(filterTracker, wiki) {
this.filterTracker = filterTracker;
this.wiki = wiki;
this.nextTrackedFilterId = 1;
this.trackedFilters = new Map(); // Use Map for better key management
// Track the filter for the background actions
this.filterTracker.track({
filterString: "[all[tiddlers+shadows]tag[$:/tags/BackgroundAction]!is[draft]]",
fnEnter: title => this.trackFilter(title),
fnLeave: (title, enterValue) => this.untrackFilter(enterValue),
fnChange: (title, enterValue) => {
this.untrackFilter(enterValue);
return this.trackFilter(title);
},
fnProcess: changes => this.process(changes)
});
}
trackFilter(title) {
const tiddler = this.wiki.getTiddler(title);
const id = this.nextTrackedFilterId++;
const tracker = new BackgroundActionTracker({
wiki: this.wiki,
title,
trackFilter: tiddler.fields["track-filter"],
actions: tiddler.fields.text
});
this.trackedFilters.set(id, tracker);
return id;
}
untrackFilter(enterValue) {
const tracker = this.trackedFilters.get(enterValue);
if(tracker) {
tracker.destroy();
}
this.trackedFilters.delete(enterValue);
}
process(changes) {
for(const tracker of this.trackedFilters.values()) {
tracker.process(changes);
}
}
}
/*
Represents an individual tracked filter. Options include:
wiki: wiki to use
title: title of the tiddler being tracked
trackFilter: filter string to track changes
actions: actions to be executed when the filter changes
*/
class BackgroundActionTracker {
constructor({wiki, title, trackFilter, actions}) {
this.wiki = wiki;
this.title = title;
this.trackFilter = trackFilter;
this.actions = actions;
this.filterTracker = new $tw.FilterTracker(this.wiki);
this.hasChanged = false;
this.trackerID = this.filterTracker.track({
filterString: this.trackFilter,
fnEnter: () => { this.hasChanged = true; },
fnLeave: () => { this.hasChanged = true; },
fnProcess: changes => {
if(this.hasChanged) {
this.hasChanged = false;
console.log("Processing background action", this.title);
const tiddler = this.wiki.getTiddler(this.title);
let doActions = true;
if(tiddler && tiddler.fields.platforms) {
doActions = false;
const platforms = $tw.utils.parseStringArray(tiddler.fields.platforms);
if(($tw.browser && platforms.includes("browser")) || ($tw.node && platforms.includes("node"))) {
doActions = true;
}
}
if(doActions) {
this.wiki.invokeActionString(
this.actions,
null,
{
currentTiddler: this.title
},{
parentWidget: $tw.rootWidget
}
);
}
}
}
});
}
process(changes) {
this.filterTracker.handleChangeEvent(changes);
}
destroy() {
this.filterTracker.untrack(this.trackerID);
}
}
exports.BackgroundActionDispatcher = BackgroundActionDispatcher;

View File

@@ -31,7 +31,7 @@ function FramedEngine(options) {
this.parentNode.insertBefore(this.iframeNode,this.nextSibling);
this.iframeDoc = this.iframeNode.contentWindow.document;
// (Firefox requires us to put some empty content in the iframe)
var paletteTitle = this.widget.wiki.getTiddlerText("$:/palette");
var paletteTitle = this.widget.wiki.getTiddlerText("$:/palette/palette-colours");
var colorScheme = (this.widget.wiki.getTiddler(paletteTitle) || {fields: {}}).fields["color-scheme"] || "light";
this.iframeDoc.open();
this.iframeDoc.write("<!DOCTYPE html><html><head><meta name='color-scheme' content='" + colorScheme + "'></head><body></body></html>");
@@ -156,8 +156,8 @@ Fix the height of textarea to fit content
FramedEngine.prototype.fixHeight = function() {
// Make sure styles are updated
this.copyStyles();
// Adjust height
if(this.widget.editTag === "textarea") {
// If .editRows is initialised, it takes precedence
if(this.widget.editTag === "textarea" && !this.widget.editRows) {
if(this.widget.editAutoHeight) {
if(this.domNode && !this.domNode.isTiddlyWikiFakeDom) {
var newHeight = $tw.utils.resizeTextAreaToFit(this.domNode,this.widget.editMinHeight);

View File

@@ -28,7 +28,12 @@ function SimpleEngine(options) {
if(this.widget.editTag === "textarea") {
this.domNode.appendChild(this.widget.document.createTextNode(this.value));
} else {
this.domNode.value = this.value;
if(this.widget.editType === "color") {
// The <input type="color"> element requires a six digit hex value
this.domNode.value = $tw.utils.convertCSSColorToRGBString(this.value);
} else {
this.domNode.value = this.value;
}
}
// Set the attributes
if(this.widget.editType && this.widget.editTag !== "textarea") {
@@ -83,6 +88,9 @@ Update the DomNode with the new text
*/
SimpleEngine.prototype.updateDomNodeText = function(text) {
try {
if(this.widget.editType === "color") {
text = $tw.utils.convertCSSColorToRGBString(text);
}
this.domNode.value = text;
} catch(e) {
// Ignore
@@ -100,7 +108,8 @@ SimpleEngine.prototype.getText = function() {
Fix the height of textarea to fit content
*/
SimpleEngine.prototype.fixHeight = function() {
if(this.widget.editTag === "textarea") {
// If .editRows is initialised, it takes precedence
if((this.widget.editTag === "textarea") && !this.widget.editRows) {
if(this.widget.editAutoHeight) {
if(this.domNode && !this.domNode.isTiddlyWikiFakeDom) {
$tw.utils.resizeTextAreaToFit(this.domNode,this.widget.editMinHeight);

View File

@@ -217,12 +217,10 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
EditTextWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
// Completely rerender if any of our attributes have changed
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes["default"] || changedAttributes["class"] || changedAttributes.placeholder || changedAttributes.size || changedAttributes.autoHeight || changedAttributes.minHeight || changedAttributes.focusPopup || changedAttributes.rows || changedAttributes.tabindex || changedAttributes.cancelPopups || changedAttributes.inputActions || changedAttributes.refreshTitle || changedAttributes.autocomplete || changedTiddlers[HEIGHT_MODE_TITLE] || changedTiddlers[ENABLE_TOOLBAR_TITLE] || changedTiddlers["$:/palette"] || changedAttributes.disabled || changedAttributes.fileDrop) {
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes["class"] || changedAttributes.placeholder || changedAttributes.size || changedAttributes.autoHeight || changedAttributes.minHeight || changedAttributes.focusPopup || changedAttributes.rows || changedAttributes.tabindex || changedAttributes.cancelPopups || changedAttributes.inputActions || changedAttributes.refreshTitle || changedAttributes.autocomplete || changedTiddlers[HEIGHT_MODE_TITLE] || changedTiddlers[ENABLE_TOOLBAR_TITLE] || changedTiddlers["$:/palette"] || changedAttributes.disabled || changedAttributes.fileDrop) {
this.refreshSelf();
return true;
} else if (changedTiddlers[this.editRefreshTitle]) {
this.engine.updateDomNodeText(this.getEditInfo().value);
} else if(changedTiddlers[this.editTitle]) {
} else if(changedAttributes["default"] || changedTiddlers[this.editRefreshTitle] || changedTiddlers[this.editTitle]) {
var editInfo = this.getEditInfo();
this.updateEditor(editInfo.value,editInfo.type);
}

View File

@@ -0,0 +1,106 @@
/*\
title: $:/core/modules/filter-tracker.js
type: application/javascript
module-type: global
Class to track the results of a filter string
\*/
"use strict";
class FilterTracker {
constructor(wiki) {
this.wiki = wiki;
this.trackers = new Map();
this.nextTrackerId = 1;
}
handleChangeEvent(changes) {
this.processTrackers();
this.processChanges(changes);
}
/*
Add a tracker to the filter tracker. Returns null if any of the parameters are invalid, or a tracker id if the tracker was added successfully. Options include:
filterString: the filter string to track
fnEnter: function to call when a title enters the filter results. Called even if the tiddler does not actually exist. Called as (title), and should return a truthy value that is stored in the tracker as the "enterValue"
fnLeave: function to call when a title leaves the filter results. Called as (title,enterValue)
fnChange: function to call when a tiddler changes in the filter results. Only called for filter results that identify a tiddler or shadow tiddler. Called as (title,enterValue), and may optionally return a replacement enterValue
fnProcess: function to call each time the tracker is processed, after any enter, leave or change functions are called. Called as (changes)
*/
track(options = {}) {
const {
filterString,
fnEnter,
fnLeave,
fnChange,
fnProcess
} = options;
const id = this.nextTrackerId++;
const tracker = {
id,
filterString,
fnEnter,
fnLeave,
fnChange,
fnProcess,
previousResults: [],
resultValues: {}
};
this.trackers.set(id, tracker);
// Process the tracker
this.processTracker(id);
return id;
}
untrack(id) {
this.trackers.delete(id);
}
processTrackers() {
for(const id of this.trackers.keys()) {
this.processTracker(id);
}
}
processTracker(id) {
const tracker = this.trackers.get(id);
if(!tracker) return;
const results = [];
// Evaluate the filter and remove duplicate results
$tw.utils.each(this.wiki.filterTiddlers(tracker.filterString), title => {
$tw.utils.pushTop(results, title);
});
// Process the newly entered results
results.forEach(title => {
if(!tracker.previousResults.includes(title) && !tracker.resultValues[title] && tracker.fnEnter) {
tracker.resultValues[title] = tracker.fnEnter(title) || true;
}
});
// Process the results that have just left
tracker.previousResults.forEach(title => {
if(!results.includes(title) && tracker.resultValues[title] && tracker.fnLeave) {
tracker.fnLeave(title, tracker.resultValues[title]);
delete tracker.resultValues[title];
}
});
// Update the previous results
tracker.previousResults = results;
}
processChanges(changes) {
for(const tracker of this.trackers.values()) {
Object.keys(changes).forEach(title => {
if(title && tracker.previousResults.includes(title) && tracker.fnChange) {
tracker.resultValues[title] = tracker.fnChange(title, tracker.resultValues[title]) || tracker.resultValues[title];
}
});
if(tracker.fnProcess) {
tracker.fnProcess(changes);
}
}
}
}
exports.FilterTracker = FilterTracker;

View File

@@ -0,0 +1,24 @@
/*\
title: $:/core/modules/filterrunprefixes/apply.js
type: application/javascript
module-type: filterrunprefix
Filter run prefix to make input titles available as variables when evaluating the filter run
\*/
"use strict";
exports.apply = function(operationSubFunction) {
return function(results,source,widget) {
source = widget.wiki.makeTiddlerIterator([]);
var variables = {},
counter = 1;
results.each(function(title) {
variables["$" + counter] = title;
counter++;
});
results.clear();
results.pushTop(operationSubFunction(source,widget.makeFakeWidgetWithVariables(variables)));
};
};

View File

@@ -88,8 +88,8 @@ function parseFilterOperation(operators,filterString,p) {
rexMatch = rex.exec(filterString.substring(p));
if(rexMatch) {
operator.regexp = new RegExp(rexMatch[1], rexMatch[2]);
// DEPRECATION WARNING
console.log("WARNING: Filter",operator.operator,"has a deprecated regexp operand",operator.regexp);
// DEPRECATION WARNING
console.log("WARNING: Filter",operator.operator,"has a deprecated regexp operand",operator.regexp);
nextBracketPos = p + rex.lastIndex - 1;
}
else {
@@ -146,14 +146,16 @@ exports.parseFilter = function(filterString) {
match;
var whitespaceRegExp = /(\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;
// 1 - pragma
// 2 - pragma suffix
// 3 - entire filter run prefix
// 4 - filter run prefix name
// 5 - filter run prefix suffixes
// 6 - opening square bracket following filter run prefix
// 7 - double quoted string following filter run prefix
// 8 - single quoted string following filter run prefix
// 9 - anything except for whitespace and square brackets
operandRegExp = /(?:::(\w+)(?:\:(\w+))?(?=\s|$)|((?:\+|\-|~|(?:=>?)|:(\w+)(?:\:([\w\:, ]*))?)?)(?:(\[)|(?:"([^"]*)")|(?:'([^']*)')|([^\s\[\]]+)))/mg;
while(p < filterString.length) {
// Skip any whitespace
whitespaceRegExp.lastIndex = p;
@@ -170,18 +172,23 @@ exports.parseFilter = function(filterString) {
};
match = operandRegExp.exec(filterString);
if(match && match.index === p) {
// If there is a filter run prefix
if(match[1]) {
operation.prefix = match[1];
// If there is a filter pragma
operation.pragma = match[1];
operation.suffix = match[2];
p = match.index + match[0].length;
} else if(match[3]) {
// If there is a filter run prefix
operation.prefix = match[3];
p = p + operation.prefix.length;
// Name for named prefixes
if(match[2]) {
operation.namedPrefix = match[2];
if(match[4]) {
operation.namedPrefix = match[4];
}
// Suffixes for filter run prefix
if(match[3]) {
if(match[5]) {
operation.suffixes = [];
$tw.utils.each(match[3].split(":"),function(subsuffix) {
$tw.utils.each(match[5].split(":"),function(subsuffix) {
operation.suffixes.push([]);
$tw.utils.each(subsuffix.split(","),function(entry) {
entry = $tw.utils.trim(entry);
@@ -193,7 +200,7 @@ exports.parseFilter = function(filterString) {
}
}
// Opening square bracket
if(match[4]) {
if(match[6]) {
p = parseFilterOperation(operation.operators,filterString,p);
} else {
p = match.index + match[0].length;
@@ -203,9 +210,9 @@ exports.parseFilter = function(filterString) {
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
if(match[7] || match[8] || match[9]) { // Double quoted string, single quoted string or unquoted title
operation.operators.push(
{operator: "title", operands: [{text: match[5] || match[6] || match[7]}]}
{operator: "title", operands: [{text: match[7] || match[8] || match[9]}]}
);
}
results.push(operation);
@@ -230,23 +237,30 @@ exports.getFilterRunPrefixes = function() {
return this.filterRunPrefixes;
}
exports.filterTiddlers = function(filterString,widget,source) {
var fn = this.compileFilter(filterString);
return fn.call(this,source,widget);
exports.filterTiddlers = function(filterString,widget,source,options) {
var fn = this.compileFilter(filterString,options);
try {
const fnResult = fn.call(this,source,widget);
return fnResult;
} catch(e) {
return [`${$tw.language.getString("Error/Filter")}: ${e}`];
}
};
/*
Compile a filter into a function with the signature fn(source,widget) where:
Compile a filter into a function with the signature fn(source,widget,options) where:
source: an iterator function for the source tiddlers, called source(iterator), where iterator is called as iterator(tiddler,title)
widget: an optional widget node for retrieving the current tiddler etc.
*/
exports.compileFilter = function(filterString) {
exports.compileFilter = function(filterString,options) {
var defaultFilterRunPrefix = (options || {}).defaultFilterRunPrefix || "or";
var cacheKey = filterString + "|" + defaultFilterRunPrefix;
if(!this.filterCache) {
this.filterCache = Object.create(null);
this.filterCacheCount = 0;
}
if(this.filterCache[filterString] !== undefined) {
return this.filterCache[filterString];
if(this.filterCache[cacheKey] !== undefined) {
return this.filterCache[cacheKey];
}
var filterParseTree;
try {
@@ -314,19 +328,20 @@ exports.compileFilter = function(filterString) {
// Invoke the appropriate filteroperator module
results = operatorFunction(accumulator,{
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,
regexp: operator.regexp
},{
wiki: self,
widget: widget
});
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,
regexp: operator.regexp
},{
wiki: self,
widget: widget,
defaultFilterRunPrefix: defaultFilterRunPrefix
});
if($tw.utils.isArray(results)) {
accumulator = self.makeTiddlerIterator(results);
} else {
@@ -346,29 +361,45 @@ exports.compileFilter = function(filterString) {
var filterRunPrefixes = self.getFilterRunPrefixes();
// Wrap the operator functions in a wrapper function that depends on the prefix
operationFunctions.push((function() {
var options = {wiki: self, suffixes: operation.suffixes || []};
switch(operation.prefix || "") {
case "": // No prefix means that the operation is unioned into the result
return filterRunPrefixes["or"](operationSubFunction, options);
case "=": // The results of the operation are pushed into the result without deduplication
return filterRunPrefixes["all"](operationSubFunction, options);
case "-": // The results of this operation are removed from the main result
return filterRunPrefixes["except"](operationSubFunction, options);
case "+": // This operation is applied to the main results so far
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);
} else {
if(operation.pragma) {
switch(operation.pragma) {
case "defaultprefix":
defaultFilterRunPrefix = operation.suffix || "or";
break;
default:
return function(results,source,widget) {
results.clear();
results.push($tw.language.getString("Error/FilterRunPrefix"));
results.push($tw.language.getString("Error/FilterPragma"));
};
}
}
return function(results,source,widget) {
// Dummy response
};
} else {
var options = {wiki: self, suffixes: operation.suffixes || []};
switch(operation.prefix || "") {
case "": // Use the default filter run prefix if none is specified
return filterRunPrefixes[defaultFilterRunPrefix](operationSubFunction, options);
case "=": // The results of the operation are pushed into the result without deduplication
return filterRunPrefixes["all"](operationSubFunction, options);
case "-": // The results of this operation are removed from the main result
return filterRunPrefixes["except"](operationSubFunction, options);
case "+": // This operation is applied to the main results so far
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);
} else {
return function(results,source,widget) {
results.clear();
results.push($tw.language.getString("Error/FilterRunPrefix"));
};
}
}
}
})());
});
@@ -407,7 +438,7 @@ exports.compileFilter = function(filterString) {
this.filterCache = Object.create(null);
this.filterCacheCount = 0;
}
this.filterCache[filterString] = fnMeasured;
this.filterCache[cacheKey] = fnMeasured;
this.filterCacheCount++;
return fnMeasured;
};

View File

@@ -0,0 +1,21 @@
/*\
title: $:/core/modules/filters/changecount.js
type: application/javascript
module-type: filteroperator
Filter operator for retrieving the changecount for each title in the list.
\*/
"use strict";
/*
Export our filter function
*/
exports.changecount = function(source,operator,options) {
var results = [];
source(function(tiddler,title) {
results.push(options.wiki.getChangeCount(title) + "");
});
return results;
};

View File

@@ -0,0 +1,140 @@
/*\
title: $:/core/modules/filters/colour-ops.js
type: application/javascript
module-type: filteroperator
Filter operators for colour operations
\*/
"use strict";
var Color = require("$:/core/modules/utils/dom/color.js").Color,
colourSpacesList = Object.keys(Color.spaces),
hueAdjustersList = ["raw","increasing","decreasing","longer","shorter"];
exports["colour-lighten"] = makeSerialColourOperator(function (colour, operator, options) {
return colour.lighten($tw.utils.parseNumber(operator.operand)).display().toString();
});
exports["colour-darken"] = makeSerialColourOperator(function (colour, operator, options) {
return colour.darken($tw.utils.parseNumber(operator.operand)).display().toString();
});
exports["colour-get-oklch"] = makeSerialColourOperator(function (colour, operator, options) {
var prop = ((operator.suffixes || [])[0] || ["l"])[0];
if(["l","c","h"].indexOf(prop) !== -1) {
colour = colour.oklch[prop];
}
return colour.toString();
});
exports["colour-set-oklch"] = makeSerialColourOperator(function (colour, operator, options) {
var prop = ((operator.suffixes || [])[0] || ["l"])[0];
if(["l","c","h"].indexOf(prop) !== -1) {
colour.oklch[prop] = $tw.utils.parseNumber(operator.operand);
}
return colour.display().toString();
});
exports["colour-set-alpha"] = makeSerialColourOperator(function (colour, operator, options) {
colour.alpha = $tw.utils.parseNumber(operator.operand);
return colour.display().toString();
});
exports["colour-contrast"] = makeParallelColourOperator(function (colours, operator, options) {
var colourContrasts = [];
$tw.utils.each(colours,function(colour,index) {
if(!colour) {
colour = $tw.utils.parseCSSColorObject("white");
colours[index] = colour;
}
if(index > 0) {
colourContrasts.push(colour.contrast(colours[index - 1],"DeltaPhi").toString());
}
});
return colourContrasts;
});
exports["colour-best-contrast"] = makeParallelColourOperator(function (colours, operator, options, originalColours) {
var bestContrast = 0,
bestColour = null;
if(colours.length < 2) {
return [];
}
var targetColour = colours[colours.length - 1];
for(var t=0; t<colours.length; t++) {
var colour = colours[t];
if(colour) {
var contrast = colour.contrast(targetColour,"DeltaPhi");
if(contrast > bestContrast) {
bestContrast = contrast;
bestColour = originalColours[t];
}
}
}
if(bestColour) {
return [bestColour];
} else {
return [];
}
});
exports["colour-interpolate"] = function(source,operator,options) {
// Get the colour space suffix
var space = ((((operator.suffixes || [])[0] || ["srgb"])[0]) || "").toLowerCase();
if(colourSpacesList.indexOf(space) === -1) {
space = "lch";
}
// Get the hue adjuster suffix
var hueAdjuster = ((((operator.suffixes || [])[1] || ["shorter"])[0]) || "").toLowerCase();
if(hueAdjustersList.indexOf(hueAdjuster) === -1) {
hueAdjuster = "shorter";
}
// Get the colours
if(operator.operands.length < 2) {
return [];
}
var colourA = $tw.utils.parseCSSColorObject(operator.operands[0]),
colourB = $tw.utils.parseCSSColorObject(operator.operands[1]);
if(!colourA || !colourB) {
return [];
}
var rangefn = colourA.range(colourB,{space: space, hue: hueAdjuster});
// Cycle through the weights
var results = [];
source(function(tiddler,title) {
var index = $tw.utils.parseNumber(title);
var colour = rangefn(index);
results.push(colour.display().toString());
});
return results;
};
function makeSerialColourOperator(fn) {
return function (source, operator, options) {
var results = [];
source(function (tiddler, title) {
var c = $tw.utils.parseCSSColorObject(title);
if (c) {
c = fn(c, operator, options);
results.push(c);
} else {
results.push("");
}
});
return results;
};
}
function makeParallelColourOperator(fn) {
return function (source, operator, options) {
var originalColours = [],
colours = [];
source(function (tiddler, title) {
originalColours.push(title);
colours.push($tw.utils.parseCSSColorObject(title));
});
return fn(colours, operator, options, originalColours);
};
}

View File

@@ -13,7 +13,9 @@ Filter operator returning those input titles that pass a subfilter
Export our filter function
*/
exports.filter = function(source,operator,options) {
var filterFn = options.wiki.compileFilter(operator.operand),
var suffixes = operator.suffixes || [],
defaultFilterRunPrefix = (suffixes[0] || [options.defaultFilterRunPrefix] || [])[0] || "or",
filterFn = options.wiki.compileFilter(operator.operand,{defaultFilterRunPrefix}),
results = [],
target = operator.prefix !== "!";
source(function(tiddler,title) {

View File

@@ -14,31 +14,31 @@ Export our filter function
*/
exports.sort = function(source,operator,options) {
var results = prepare_results(source);
options.wiki.sortTiddlers(results,operator.operand || "title",operator.prefix === "!",false,false);
options.wiki.sortTiddlers(results,operator.operands[0] || "title",operator.prefix === "!",false,false,undefined,operator.operands[1]);
return results;
};
exports.nsort = function(source,operator,options) {
var results = prepare_results(source);
options.wiki.sortTiddlers(results,operator.operand || "title",operator.prefix === "!",false,true);
options.wiki.sortTiddlers(results,operator.operands[0] || "title",operator.prefix === "!",false,true,undefined,operator.operands[1]);
return results;
};
exports.sortan = function(source, operator, options) {
var results = prepare_results(source);
options.wiki.sortTiddlers(results, operator.operand || "title", operator.prefix === "!",false,false,true);
options.wiki.sortTiddlers(results, operator.operands[0] || "title", operator.prefix === "!",false,false,true,operator.operands[1]);
return results;
};
exports.sortcs = function(source,operator,options) {
var results = prepare_results(source);
options.wiki.sortTiddlers(results,operator.operand || "title",operator.prefix === "!",true,false);
options.wiki.sortTiddlers(results,operator.operands[0] || "title",operator.prefix === "!",true,false,undefined,operator.operands[1]);
return results;
};
exports.nsortcs = function(source,operator,options) {
var results = prepare_results(source);
options.wiki.sortTiddlers(results,operator.operand || "title",operator.prefix === "!",true,true);
options.wiki.sortTiddlers(results,operator.operands[0] || "title",operator.prefix === "!",true,true,undefined,operator.operands[1]);
return results;
};

View File

@@ -37,14 +37,14 @@ exports.trim = function(source,operator,options) {
operand = (operator.operand || ""),
fnCalc;
if(suffix === "prefix") {
fnCalc = function(a,b) {return [$tw.utils.trimPrefix(a,b)];}
fnCalc = function(a,b) {return [$tw.utils.trimPrefix(a,b)];};
} else if(suffix === "suffix") {
fnCalc = function(a,b) {return [$tw.utils.trimSuffix(a,b)];}
fnCalc = function(a,b) {return [$tw.utils.trimSuffix(a,b)];};
} else {
if(operand === "") {
fnCalc = function(a) {return [$tw.utils.trim(a)];}
fnCalc = function(a) {return [$tw.utils.trim(a)];};
} else {
fnCalc = function(a,b) {return [$tw.utils.trimSuffix($tw.utils.trimPrefix(a,b),b)];}
fnCalc = function(a,b) {return [$tw.utils.trimSuffix($tw.utils.trimPrefix(a,b),b)];};
}
}
source(function(tiddler,title) {
@@ -71,107 +71,53 @@ exports.join = makeStringReducingOperator(
},null
);
var dmp = require("$:/core/modules/utils/diff-match-patch/diff_match_patch.js");
const dmp = require("$:/core/modules/utils/diff-match-patch/diff_match_patch.js");
exports.levenshtein = makeStringBinaryOperator(
function(a,b) {
var dmpObject = new dmp.diff_match_patch(),
diffs = dmpObject.diff_main(a,b);
return [dmpObject.diff_levenshtein(diffs) + ""];
const diffs = dmp.diffMain(a,b);
return [dmp.diffLevenshtein(diffs).toString()];
}
);
// these two functions are adapted from https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs
// this function is adapted from https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs
function diffLineWordMode(text1,text2,mode) {
var dmpObject = new dmp.diff_match_patch();
var a = diffPartsToChars(text1,text2,mode);
var a = $tw.utils.diffPartsToChars(text1,text2,mode);
var lineText1 = a.chars1;
var lineText2 = a.chars2;
var lineArray = a.lineArray;
var diffs = dmpObject.diff_main(lineText1,lineText2,false);
dmpObject.diff_charsToLines_(diffs,lineArray);
var diffs = dmp.diffMain(lineText1,lineText2,false);
dmp.diffCharsToLines(diffs,lineArray);
return diffs;
}
function diffPartsToChars(text1,text2,mode) {
var lineArray = [];
var lineHash = {};
lineArray[0] = '';
function diff_linesToPartsMunge_(text,mode) {
var chars = '';
var lineStart = 0;
var lineEnd = -1;
var lineArrayLength = lineArray.length,
regexpResult;
var searchRegexp = /\W+/g;
while(lineEnd < text.length - 1) {
if(mode === "words") {
regexpResult = searchRegexp.exec(text);
lineEnd = searchRegexp.lastIndex;
if(regexpResult === null) {
lineEnd = text.length;
}
lineEnd = --lineEnd;
} else {
lineEnd = text.indexOf('\n', lineStart);
if(lineEnd == -1) {
lineEnd = text.length - 1;
}
}
var line = text.substring(lineStart, lineEnd + 1);
if(lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : (lineHash[line] !== undefined)) {
chars += String.fromCharCode(lineHash[line]);
} else {
if(lineArrayLength == maxLines) {
line = text.substring(lineStart);
lineEnd = text.length;
}
chars += String.fromCharCode(lineArrayLength);
lineHash[line] = lineArrayLength;
lineArray[lineArrayLength++] = line;
}
lineStart = lineEnd + 1;
}
return chars;
}
var maxLines = 40000;
var chars1 = diff_linesToPartsMunge_(text1,mode);
maxLines = 65535;
var chars2 = diff_linesToPartsMunge_(text2,mode);
return {chars1: chars1, chars2: chars2, lineArray: lineArray};
};
exports.makepatches = function(source,operator,options) {
var dmpObject = new dmp.diff_match_patch(),
suffix = operator.suffix || "",
var suffix = operator.suffix || "",
result = [];
source(function(tiddler,title) {
var diffs, patches;
if(suffix === "lines" || suffix === "words") {
diffs = diffLineWordMode(title,operator.operand,suffix);
patches = dmpObject.patch_make(title,diffs);
} else {
patches = dmpObject.patch_make(title,operator.operand);
}
Array.prototype.push.apply(result,[dmpObject.patch_toText(patches)]);
});
source(function(tiddler,title) {
let diffs, patches;
if(suffix === "lines" || suffix === "words") {
diffs = diffLineWordMode(title,operator.operand,suffix);
patches = dmp.patchMake(title,diffs);
} else {
patches = dmp.patchMake(title,operator.operand);
}
Array.prototype.push.apply(result,[dmp.patchToText(patches)]);
});
return result;
};
exports.applypatches = makeStringBinaryOperator(
function(a,b) {
var dmpObject = new dmp.diff_match_patch(),
patches;
let patches;
try {
patches = dmpObject.patch_fromText(b);
patches = dmp.patchFromText(b);
} catch(e) {
}
if(patches) {
return [dmpObject.patch_apply(patches,a)[0]];
return [dmp.patchApply(patches,a)[0]];
} else {
return [a];
}
@@ -279,7 +225,7 @@ exports.pad = function(source,operator,options) {
}
});
return results;
}
};
exports.charcode = function(source,operator,options) {
var chars = [];

View File

@@ -13,7 +13,9 @@ Filter operator returning its operand evaluated as a filter
Export our filter function
*/
exports.subfilter = function(source,operator,options) {
var list = options.wiki.filterTiddlers(operator.operand,options.widget,source);
var suffixes = operator.suffixes || [],
defaultFilterRunPrefix = (suffixes[0] || [options.defaultFilterRunPrefix] || [])[0] || "or";
var list = options.wiki.filterTiddlers(operator.operand,options.widget,source,{defaultFilterRunPrefix});
if(operator.prefix === "!") {
var results = [];
source(function(tiddler,title) {

View File

@@ -0,0 +1,67 @@
/*\
title: $:/core/modules/info/mediaquerytracker.js
type: application/javascript
module-type: info
Initialise $:/info/ tiddlers derived from media queries via
\*/
"use strict";
exports.getInfoTiddlerFields = function(updateInfoTiddlersCallback) {
if($tw.browser) {
// Functions to start and stop tracking a particular media query tracker tiddler
function track(title) {
var result = {},
tiddler = $tw.wiki.getTiddler(title);
if(tiddler) {
var mediaQuery = tiddler.fields["media-query"],
infoTiddler = tiddler.fields["info-tiddler"],
infoTiddlerAlt = tiddler.fields["info-tiddler-alt"];
if(mediaQuery && infoTiddler) {
// Evaluate and track the media query
result.mqList = window.matchMedia(mediaQuery);
function getResultTiddlers() {
var value = result.mqList.matches ? "yes" : "no",
tiddlers = [];
tiddlers.push({title: infoTiddler, text: value});
if(infoTiddlerAlt) {
tiddlers.push({title: infoTiddlerAlt, text: value});
}
return tiddlers;
};
updateInfoTiddlersCallback(getResultTiddlers());
result.handler = function(event) {
updateInfoTiddlersCallback(getResultTiddlers());
};
result.mqList.addEventListener("change",result.handler);
}
}
return result;
}
function untrack(enterValue) {
if(enterValue.mqList && enterValue.handler) {
enterValue.mqList.removeEventListener("change",enterValue.handler);
}
}
// Track media query tracker tiddlers
function fnEnter(title) {
return track(title);
}
function fnLeave(title,enterValue) {
untrack(enterValue);
}
function fnChange(title,enterValue) {
untrack(enterValue);
return track(title);
}
$tw.filterTracker.track({
filterString: "[all[tiddlers+shadows]tag[$:/tags/MediaQueryTracker]!is[draft]]",
fnEnter: fnEnter,
fnLeave: fnLeave,
fnChange: fnChange
});
}
return [];
};

View File

@@ -33,13 +33,6 @@ exports.getInfoTiddlerFields = function(updateInfoTiddlersCallback) {
// Screen size
infoTiddlerFields.push({title: "$:/info/browser/screen/width", text: window.screen.width.toString()});
infoTiddlerFields.push({title: "$:/info/browser/screen/height", text: window.screen.height.toString()});
// Dark mode through event listener on MediaQueryList
var mqList = window.matchMedia("(prefers-color-scheme: dark)"),
getDarkModeTiddler = function() {return {title: "$:/info/darkmode", text: mqList.matches ? "yes" : "no"};};
infoTiddlerFields.push(getDarkModeTiddler());
mqList.addListener(function(event) {
updateInfoTiddlersCallback([getDarkModeTiddler()]);
});
// Language
infoTiddlerFields.push({title: "$:/info/browser/language", text: navigator.language || ""});
}

View File

@@ -26,25 +26,26 @@ exports.params = [
Run the macro
*/
exports.run = function(target,fallbackTarget,colourA,colourB) {
var rgbTarget = $tw.utils.parseCSSColor(target) || $tw.utils.parseCSSColor(fallbackTarget);
var rgbTarget = $tw.utils.parseCSSColorObject(target) || $tw.utils.parseCSSColorObject(fallbackTarget);
if(!rgbTarget) {
return colourA;
}
var rgbColourA = $tw.utils.parseCSSColor(colourA),
rgbColourB = $tw.utils.parseCSSColor(colourB);
var rgbColourA = $tw.utils.parseCSSColorObject(colourA),
rgbColourB = $tw.utils.parseCSSColorObject(colourB);
if(rgbColourA && !rgbColourB) {
return rgbColourA;
return colourA;
}
if(rgbColourB && !rgbColourA) {
return rgbColourB;
return colourB;
}
if(!rgbColourA && !rgbColourB) {
// If neither colour is readable, return a crude inverse of the target
return [255 - rgbTarget[0],255 - rgbTarget[1],255 - rgbTarget[2],rgbTarget[3]];
rgbTarget.srgb.r = 1 - rgbTarget.srgb.r;
rgbTarget.srgb.g = 1 - rgbTarget.srgb.g;
rgbTarget.srgb.b = 1 - rgbTarget.srgb.b;
return rgbTarget.display();
}
// Colour brightness formula derived from http://www.w3.org/WAI/ER/WD-AERT/#color-contrast
var brightnessTarget = rgbTarget[0] * 0.299 + rgbTarget[1] * 0.587 + rgbTarget[2] * 0.114,
brightnessA = rgbColourA[0] * 0.299 + rgbColourA[1] * 0.587 + rgbColourA[2] * 0.114,
brightnessB = rgbColourB[0] * 0.299 + rgbColourB[1] * 0.587 + rgbColourB[2] * 0.114;
return Math.abs(brightnessTarget - brightnessA) > Math.abs(brightnessTarget - brightnessB) ? colourA : colourB;
var aContrast = rgbColourA.contrast(rgbTarget,"DeltaPhi"),
bContrast = rgbColourB.contrast(rgbTarget,"DeltaPhi");
return aContrast > bContrast ? colourA : colourB;
};

View File

@@ -11,17 +11,16 @@ The image parser parses an image into an embeddable HTML element
var ImageParser = function(type,text,options) {
var element = {
type: "element",
tag: "img",
attributes: {}
};
type: "image",
attributes: {}
};
if(options._canonical_uri) {
element.attributes.src = {type: "string", value: options._canonical_uri};
element.attributes.source = {type: "string", value: options._canonical_uri};
} else if(text) {
if(type === "image/svg+xml" || type === ".svg") {
element.attributes.src = {type: "string", value: "data:image/svg+xml," + encodeURIComponent(text)};
element.attributes.source = {type: "string", value: "data:image/svg+xml," + encodeURIComponent(text)};
} else {
element.attributes.src = {type: "string", value: "data:" + type + ";base64," + text};
element.attributes.source = {type: "string", value: "data:" + type + ";base64," + text};
}
}
this.tree = [element];

View File

@@ -13,6 +13,11 @@ Load core modules
exports.name = "load-modules";
exports.synchronous = true;
// Set to `true` to enable performance instrumentation
var PERFORMANCE_INSTRUMENTATION_CONFIG_TITLE = "$:/config/Performance/Instrumentation";
var widget = require("$:/core/modules/widgets/widget.js");
exports.startup = function() {
// Load modules
$tw.modules.applyMethods("utils",$tw.utils);
@@ -31,6 +36,27 @@ exports.startup = function() {
$tw.modules.applyMethods("tiddlerdeserializer",$tw.Wiki.tiddlerDeserializerModules);
$tw.macros = $tw.modules.getModulesByTypeAsHashmap("macro");
$tw.wiki.initParsers();
// --------------------------
// The rest of the startup process here is not strictly to do with loading modules, but are needed before other startup
// modules are executed. It is easier to put them here than to introduce a new startup module
// --------------------------
// Create a root widget for attaching event handlers. By using it as the parentWidget for another widget tree, one can reuse the event handlers
$tw.rootWidget = new widget.widget({
type: "widget",
children: []
},{
wiki: $tw.wiki,
document: $tw.browser ? document : $tw.fakeDocument
});
// Set up the performance framework
$tw.perf = new $tw.Performance($tw.wiki.getTiddlerText(PERFORMANCE_INSTRUMENTATION_CONFIG_TITLE,"no") === "yes");
// Kick off the filter tracker
$tw.filterTracker = new $tw.FilterTracker($tw.wiki);
$tw.wiki.addEventListener("change",function(changes) {
$tw.filterTracker.handleChangeEvent(changes);
});
// Kick off the background action dispatcher
$tw.backgroundActionDispatcher = new $tw.BackgroundActionDispatcher($tw.filterTracker,$tw.wiki);
if($tw.node) {
$tw.Commander.initCommands();
}

View File

@@ -14,11 +14,6 @@ exports.name = "startup";
exports.after = ["load-modules"];
exports.synchronous = true;
// Set to `true` to enable performance instrumentation
var PERFORMANCE_INSTRUMENTATION_CONFIG_TITLE = "$:/config/Performance/Instrumentation";
var widget = require("$:/core/modules/widgets/widget.js");
exports.startup = function() {
// Minimal browser detection
if($tw.browser) {
@@ -54,16 +49,6 @@ exports.startup = function() {
}
// Initialise version
$tw.version = $tw.utils.extractVersionInfo();
// Set up the performance framework
$tw.perf = new $tw.Performance($tw.wiki.getTiddlerText(PERFORMANCE_INSTRUMENTATION_CONFIG_TITLE,"no") === "yes");
// Create a root widget for attaching event handlers. By using it as the parentWidget for another widget tree, one can reuse the event handlers
$tw.rootWidget = new widget.widget({
type: "widget",
children: []
},{
wiki: $tw.wiki,
document: $tw.browser ? document : $tw.fakeDocument
});
// Kick off the language manager and switcher
$tw.language = new $tw.Language();
$tw.languageSwitcher = new $tw.PluginSwitcher({

View File

@@ -0,0 +1,31 @@
/*\
title: $:/core/modules/utils/base64.js
type: application/javascript
module-type: utils-browser
Base64 utility functions
\*/
"use strict";
/*
Base64 utility functions that work in either browser or Node.js
*/
exports.btoa = binstr => window.btoa(binstr);
exports.atob = b64 => window.atob(b64);
function base64ToBytes(base64) {
const binString = exports.atob(base64);
return Uint8Array.from(binString, m => m.codePointAt(0));
};
function bytesToBase64(bytes) {
const binString = Array.from(bytes, byte => String.fromCodePoint(byte)).join("");
return exports.btoa(binString);
};
exports.base64EncodeUtf8 = str => bytesToBase64(new TextEncoder().encode(str));
exports.base64DecodeUtf8 = str => new TextDecoder().decode(base64ToBytes(str));

View File

@@ -0,0 +1,58 @@
/*\
title: $:/core/modules/utils/deprecated.js
type: application/javascript
module-type: utils
Deprecated util functions
\*/
exports.logTable = data => console.table(data);
exports.repeat = (str,count) => str.repeat(count);
exports.startsWith = (str,search) => str.startsWith(search);
exports.endsWith = (str,search) => str.endsWith(search);
exports.trim = function(str) {
if(typeof str === "string") {
return str.trim();
} else {
return str;
}
};
exports.hopArray = (object,array) => array.some(element => $tw.utils.hop(object,element));
exports.sign = Math.sign;
exports.strEndsWith = (str,ending,position) => str.endsWith(ending,position);
exports.stringifyNumber = num => num.toString();
exports.tagToCssSelector = function(tagName) {
return "tc-tagged-" + encodeURIComponent(tagName).replace(/[!"#$%&'()*+,\-./:;<=>?@[\\\]^`{\|}~,]/mg,function(c) {
return "\\" + c;
});
};
exports.domContains = (a,b) => a.compareDocumentPosition(b) & 16;
exports.domMatchesSelector = (node,selector) => node.matches(selector);
exports.hasClass = (el,className) => el.classList && el.classList.contains(className);
exports.addClass = function(el,className) {
el.classList && className && el.classList.add(className);
};
exports.removeClass = function(el,className) {
el.classList && className && el.classList.remove(className);
};
exports.toggleClass = function(el,className,status) {
el.classList && className && el.classList.toggle(className, status);
};
exports.getLocationPath = () => window.location.origin + window.location.pathname;

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,55 @@
/*\
title: $:/core/modules/utils/color-utils.js
type: application/javascript
module-type: utils
Color.js related utilities
\*/
"use strict";
var Color = require("$:/core/modules/utils/dom/color.js").Color;
/*
For backwards compatibility
*/
exports.parseCSSColor = function(colourString) {
var c = exports.parseCSSColorObject(colourString);
if(c) {
var rgb = c.srgb;
return [rgb[0],rgb[1],rgb[2],c.alpha];
} else {
return null;
}
};
/*
Preferred way to parse a Color.js colour
*/
exports.parseCSSColorObject = function(colourString) {
var c = null;
try {
c = new Color(colourString);
} catch(e) {
// Return null if there is an error
}
return c;
};
/*
Convert a CSS colour to an RGB string suitable for use with the <input type="color"> element
*/
exports.convertCSSColorToRGBString = function(colourString) {
var c = exports.parseCSSColorObject(colourString);
if(c) {
var hex = c.toString({format: "hex"});
if(hex.length === 4) {
hex = "#" + hex[1] + hex[1] + hex[2] + hex[2] + hex[3] + hex[3];
}
return hex;
} else {
return null;
}
};

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Lea Verou, Chris Lilley
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,21 @@
{
"tiddlers": [
{
"file": "color.global.min.v0.6.1.js",
"fields": {
"type": "application/javascript",
"title": "$:/core/modules/utils/dom/color.js",
"module-type": "library"
},
"prefix": "",
"suffix": ";\nexports.Color = Color;"
},
{
"file": "LICENSE",
"fields": {
"type": "text/plain",
"title": "$:/core/modules/utils/dom/color.js/license"
}
}
]
}

View File

@@ -1,200 +0,0 @@
// (c) Dean McNamee <dean@gmail.com>, 2012.
//
// https://github.com/deanm/css-color-parser-js
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
// http://www.w3.org/TR/css3-color/
var kCSSColorTable = {
"transparent": [0,0,0,0], "aliceblue": [240,248,255,1],
"antiquewhite": [250,235,215,1], "aqua": [0,255,255,1],
"aquamarine": [127,255,212,1], "azure": [240,255,255,1],
"beige": [245,245,220,1], "bisque": [255,228,196,1],
"black": [0,0,0,1], "blanchedalmond": [255,235,205,1],
"blue": [0,0,255,1], "blueviolet": [138,43,226,1],
"brown": [165,42,42,1], "burlywood": [222,184,135,1],
"cadetblue": [95,158,160,1], "chartreuse": [127,255,0,1],
"chocolate": [210,105,30,1], "coral": [255,127,80,1],
"cornflowerblue": [100,149,237,1], "cornsilk": [255,248,220,1],
"crimson": [220,20,60,1], "cyan": [0,255,255,1],
"darkblue": [0,0,139,1], "darkcyan": [0,139,139,1],
"darkgoldenrod": [184,134,11,1], "darkgray": [169,169,169,1],
"darkgreen": [0,100,0,1], "darkgrey": [169,169,169,1],
"darkkhaki": [189,183,107,1], "darkmagenta": [139,0,139,1],
"darkolivegreen": [85,107,47,1], "darkorange": [255,140,0,1],
"darkorchid": [153,50,204,1], "darkred": [139,0,0,1],
"darksalmon": [233,150,122,1], "darkseagreen": [143,188,143,1],
"darkslateblue": [72,61,139,1], "darkslategray": [47,79,79,1],
"darkslategrey": [47,79,79,1], "darkturquoise": [0,206,209,1],
"darkviolet": [148,0,211,1], "deeppink": [255,20,147,1],
"deepskyblue": [0,191,255,1], "dimgray": [105,105,105,1],
"dimgrey": [105,105,105,1], "dodgerblue": [30,144,255,1],
"firebrick": [178,34,34,1], "floralwhite": [255,250,240,1],
"forestgreen": [34,139,34,1], "fuchsia": [255,0,255,1],
"gainsboro": [220,220,220,1], "ghostwhite": [248,248,255,1],
"gold": [255,215,0,1], "goldenrod": [218,165,32,1],
"gray": [128,128,128,1], "green": [0,128,0,1],
"greenyellow": [173,255,47,1], "grey": [128,128,128,1],
"honeydew": [240,255,240,1], "hotpink": [255,105,180,1],
"indianred": [205,92,92,1], "indigo": [75,0,130,1],
"ivory": [255,255,240,1], "khaki": [240,230,140,1],
"lavender": [230,230,250,1], "lavenderblush": [255,240,245,1],
"lawngreen": [124,252,0,1], "lemonchiffon": [255,250,205,1],
"lightblue": [173,216,230,1], "lightcoral": [240,128,128,1],
"lightcyan": [224,255,255,1], "lightgoldenrodyellow": [250,250,210,1],
"lightgray": [211,211,211,1], "lightgreen": [144,238,144,1],
"lightgrey": [211,211,211,1], "lightpink": [255,182,193,1],
"lightsalmon": [255,160,122,1], "lightseagreen": [32,178,170,1],
"lightskyblue": [135,206,250,1], "lightslategray": [119,136,153,1],
"lightslategrey": [119,136,153,1], "lightsteelblue": [176,196,222,1],
"lightyellow": [255,255,224,1], "lime": [0,255,0,1],
"limegreen": [50,205,50,1], "linen": [250,240,230,1],
"magenta": [255,0,255,1], "maroon": [128,0,0,1],
"mediumaquamarine": [102,205,170,1], "mediumblue": [0,0,205,1],
"mediumorchid": [186,85,211,1], "mediumpurple": [147,112,219,1],
"mediumseagreen": [60,179,113,1], "mediumslateblue": [123,104,238,1],
"mediumspringgreen": [0,250,154,1], "mediumturquoise": [72,209,204,1],
"mediumvioletred": [199,21,133,1], "midnightblue": [25,25,112,1],
"mintcream": [245,255,250,1], "mistyrose": [255,228,225,1],
"moccasin": [255,228,181,1], "navajowhite": [255,222,173,1],
"navy": [0,0,128,1], "oldlace": [253,245,230,1],
"olive": [128,128,0,1], "olivedrab": [107,142,35,1],
"orange": [255,165,0,1], "orangered": [255,69,0,1],
"orchid": [218,112,214,1], "palegoldenrod": [238,232,170,1],
"palegreen": [152,251,152,1], "paleturquoise": [175,238,238,1],
"palevioletred": [219,112,147,1], "papayawhip": [255,239,213,1],
"peachpuff": [255,218,185,1], "peru": [205,133,63,1],
"pink": [255,192,203,1], "plum": [221,160,221,1],
"powderblue": [176,224,230,1], "purple": [128,0,128,1],
"red": [255,0,0,1], "rosybrown": [188,143,143,1],
"royalblue": [65,105,225,1], "saddlebrown": [139,69,19,1],
"salmon": [250,128,114,1], "sandybrown": [244,164,96,1],
"seagreen": [46,139,87,1], "seashell": [255,245,238,1],
"sienna": [160,82,45,1], "silver": [192,192,192,1],
"skyblue": [135,206,235,1], "slateblue": [106,90,205,1],
"slategray": [112,128,144,1], "slategrey": [112,128,144,1],
"snow": [255,250,250,1], "springgreen": [0,255,127,1],
"steelblue": [70,130,180,1], "tan": [210,180,140,1],
"teal": [0,128,128,1], "thistle": [216,191,216,1],
"tomato": [255,99,71,1], "turquoise": [64,224,208,1],
"violet": [238,130,238,1], "wheat": [245,222,179,1],
"white": [255,255,255,1], "whitesmoke": [245,245,245,1],
"yellow": [255,255,0,1], "yellowgreen": [154,205,50,1]}
function clamp_css_byte(i) { // Clamp to integer 0 .. 255.
i = Math.round(i); // Seems to be what Chrome does (vs truncation).
return i < 0 ? 0 : i > 255 ? 255 : i;
}
function clamp_css_float(f) { // Clamp to float 0.0 .. 1.0.
return f < 0 ? 0 : f > 1 ? 1 : f;
}
function parse_css_int(str) { // int or percentage.
if (str[str.length - 1] === '%')
return clamp_css_byte(parseFloat(str) / 100 * 255);
return clamp_css_byte(parseInt(str));
}
function parse_css_float(str) { // float or percentage.
if (str[str.length - 1] === '%')
return clamp_css_float(parseFloat(str) / 100);
return clamp_css_float(parseFloat(str));
}
function css_hue_to_rgb(m1, m2, h) {
if (h < 0) h += 1;
else if (h > 1) h -= 1;
if (h * 6 < 1) return m1 + (m2 - m1) * h * 6;
if (h * 2 < 1) return m2;
if (h * 3 < 2) return m1 + (m2 - m1) * (2/3 - h) * 6;
return m1;
}
function parseCSSColor(css_str) {
// Remove all whitespace, not compliant, but should just be more accepting.
var str = css_str.replace(/ /g, '').toLowerCase();
// Color keywords (and transparent) lookup.
if (str in kCSSColorTable) return kCSSColorTable[str].slice(); // dup.
// #abc and #abc123 syntax.
if (str[0] === '#') {
if (str.length === 4) {
var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing.
if (!(iv >= 0 && iv <= 0xfff)) return null; // Covers NaN.
return [((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8),
(iv & 0xf0) | ((iv & 0xf0) >> 4),
(iv & 0xf) | ((iv & 0xf) << 4),
1];
} else if (str.length === 7) {
var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing.
if (!(iv >= 0 && iv <= 0xffffff)) return null; // Covers NaN.
return [(iv & 0xff0000) >> 16,
(iv & 0xff00) >> 8,
iv & 0xff,
1];
}
return null;
}
var op = str.indexOf('('), ep = str.indexOf(')');
if (op !== -1 && ep + 1 === str.length) {
var fname = str.substr(0, op);
var params = str.substr(op+1, ep-(op+1)).split(',');
var alpha = 1; // To allow case fallthrough.
switch (fname) {
case 'rgba':
if (params.length !== 4) return null;
alpha = parse_css_float(params.pop());
// Fall through.
case 'rgb':
if (params.length !== 3) return null;
return [parse_css_int(params[0]),
parse_css_int(params[1]),
parse_css_int(params[2]),
alpha];
case 'hsla':
if (params.length !== 4) return null;
alpha = parse_css_float(params.pop());
// Fall through.
case 'hsl':
if (params.length !== 3) return null;
var h = (((parseFloat(params[0]) % 360) + 360) % 360) / 360; // 0 .. 1
// NOTE(deanm): According to the CSS spec s/l should only be
// percentages, but we don't bother and let float or percentage.
var s = parse_css_float(params[1]);
var l = parse_css_float(params[2]);
var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
var m1 = l * 2 - m2;
return [clamp_css_byte(css_hue_to_rgb(m1, m2, h+1/3) * 255),
clamp_css_byte(css_hue_to_rgb(m1, m2, h) * 255),
clamp_css_byte(css_hue_to_rgb(m1, m2, h-1/3) * 255),
alpha];
default:
return null;
}
}
return null;
}
try { exports.parseCSSColor = parseCSSColor } catch(e) { }

View File

@@ -1,3 +0,0 @@
title: $:/core/modules/utils/dom/csscolorparser.js
type: application/javascript
module-type: utils

View File

@@ -11,19 +11,6 @@ Various static DOM-related utility functions.
var Popup = require("$:/core/modules/utils/dom/popup.js");
/*
Determines whether element 'a' contains element 'b'
Code thanks to John Resig, http://ejohn.org/blog/comparing-document-position/
*/
exports.domContains = function(a,b) {
return a.contains ?
a !== b && a.contains(b) :
!!(a.compareDocumentPosition(b) & 16);
};
exports.domMatchesSelector = function(node,selector) {
return node.matches ? node.matches(selector) : node.msMatchesSelector(selector);
};
/*
Select text in a an input or textarea (setSelectionRange crashes on certain input types)
@@ -49,38 +36,6 @@ exports.removeChildren = function(node) {
}
};
exports.hasClass = function(el,className) {
return el && el.hasAttribute && el.hasAttribute("class") && el.getAttribute("class").split(" ").indexOf(className) !== -1;
};
exports.addClass = function(el,className) {
var c = (el.getAttribute("class") || "").split(" ");
if(c.indexOf(className) === -1) {
c.push(className);
el.setAttribute("class",c.join(" "));
}
};
exports.removeClass = function(el,className) {
var c = (el.getAttribute("class") || "").split(" "),
p = c.indexOf(className);
if(p !== -1) {
c.splice(p,1);
el.setAttribute("class",c.join(" "));
}
};
exports.toggleClass = function(el,className,status) {
if(status === undefined) {
status = !exports.hasClass(el,className);
}
if(status) {
exports.addClass(el,className);
} else {
exports.removeClass(el,className);
}
};
/*
Get the first parent element that has scrollbars or use the body as fallback.
*/
@@ -297,10 +252,6 @@ exports.copyToClipboard = function(text,options) {
document.body.removeChild(textArea);
};
exports.getLocationPath = function() {
return window.location.toString().split("#")[0];
};
/*
Collect DOM variables
*/

View File

@@ -1,7 +1,7 @@
/*\
title: $:/core/modules/utils/escapecss.js
type: application/javascript
module-type: utils
module-type: utils-browser
Provides CSS.escape() functionality.
@@ -9,92 +9,6 @@ Provides CSS.escape() functionality.
"use strict";
// TODO -- resolve this construction
exports.escapeCSS = (function() {
// use browser's native CSS.escape() function if available
if ($tw.browser && window.CSS && window.CSS.escape) {
return window.CSS.escape;
}
// otherwise, a utility method is provided
// see also https://drafts.csswg.org/cssom/#serialize-an-identifier
/*! https://mths.be/cssescape v1.5.1 by @mathias | MIT license */
return function(value) {
if (arguments.length == 0) {
throw new TypeError('`CSS.escape` requires an argument.');
}
var string = String(value);
var length = string.length;
var index = -1;
var codeUnit;
var result = '';
var firstCodeUnit = string.charCodeAt(0);
while (++index < length) {
codeUnit = string.charCodeAt(index);
// Note: theres no need to special-case astral symbols, surrogate
// pairs, or lone surrogates.
// If the character is NULL (U+0000), then the REPLACEMENT CHARACTER
// (U+FFFD).
if (codeUnit == 0x0000) {
result += '\uFFFD';
continue;
}
if (
// If the character is in the range [\1-\1F] (U+0001 to U+001F) or is
// U+007F, […]
(codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit == 0x007F ||
// If the character is the first character and is in the range [0-9]
// (U+0030 to U+0039), […]
(index == 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
// If the character is the second character and is in the range [0-9]
// (U+0030 to U+0039) and the first character is a `-` (U+002D), […]
(
index == 1 &&
codeUnit >= 0x0030 && codeUnit <= 0x0039 &&
firstCodeUnit == 0x002D
)
) {
// https://drafts.csswg.org/cssom/#escape-a-character-as-code-point
result += '\\' + codeUnit.toString(16) + ' ';
continue;
}
if (
// If the character is the first character and is a `-` (U+002D), and
// there is no second character, […]
index == 0 &&
length == 1 &&
codeUnit == 0x002D
) {
result += '\\' + string.charAt(index);
continue;
}
// If the character is not handled by one of the above rules and is
// greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or
// is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to
// U+005A), or [a-z] (U+0061 to U+007A), […]
if (
codeUnit >= 0x0080 ||
codeUnit == 0x002D ||
codeUnit == 0x005F ||
codeUnit >= 0x0030 && codeUnit <= 0x0039 ||
codeUnit >= 0x0041 && codeUnit <= 0x005A ||
codeUnit >= 0x0061 && codeUnit <= 0x007A
) {
// the character itself
result += string.charAt(index);
continue;
}
// Otherwise, the escaped character.
// https://drafts.csswg.org/cssom/#escape-a-character
result += '\\' + string.charAt(index);
}
return result;
};
return window.CSS.escape;
})();

View File

@@ -48,31 +48,6 @@ exports.warning = function(text) {
exports.log(text,"brown/orange");
};
/*
Log a table of name: value or name: [values...] pairs
*/
exports.logTable = function(data) {
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) {
if($tw.utils.isArray(value)) {
for(var t=0; t<value.length; t++) {
console.log(`${name}[${t}]: ${value[t]}`);
}
} else {
console.log(`${name}: ${value}`);
}
});
}
}
/*
Return the integer represented by the str (string).
Return the dflt (default) parameter if str is not a base-10 number.
@@ -80,7 +55,7 @@ Return the dflt (default) parameter if str is not a base-10 number.
exports.getInt = function(str,deflt) {
var i = parseInt(str,10);
return isNaN(i) ? deflt : i;
}
};
/*
Repeatedly replaces a substring within a string. Like String.prototype.replace, but without any of the default special handling of $ sequences in the replace string
@@ -91,52 +66,15 @@ exports.replaceString = function(text,search,replace) {
});
};
/*
Repeats a string
*/
exports.repeat = function(str,count) {
var result = "";
for(var t=0;t<count;t++) {
result += str;
}
return result;
};
/*
Check if a string starts with another string
*/
exports.startsWith = function(str,search) {
return str.substring(0, search.length) === search;
};
/*
Check if a string ends with another string
*/
exports.endsWith = function(str,search) {
return str.substring(str.length - search.length) === search;
};
/*
Trim whitespace from the start and end of a string
Thanks to Steven Levithan, http://blog.stevenlevithan.com/archives/faster-trim-javascript
*/
exports.trim = function(str) {
if(typeof str === "string") {
return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
} else {
return str;
}
};
exports.trimPrefix = function(str,unwanted) {
if(typeof str === "string" && typeof unwanted === "string") {
if(unwanted === "") {
return str.replace(/^\s\s*/, '');
return str.replace(/^\s\s*/, "");
} else {
// Safely regexp-escape the unwanted text
unwanted = unwanted.replace(/[\\^$*+?.()|[\]{}]/g, '\\$&');
var regex = new RegExp('^(' + unwanted + ')+');
return str.replace(regex, '');
unwanted = unwanted.replace(/[\\^$*+?.()|[\]{}]/g, "\\$&");
var regex = new RegExp("^(" + unwanted + ")+");
return str.replace(regex, "");
}
} else {
return str;
@@ -146,12 +84,12 @@ exports.trimPrefix = function(str,unwanted) {
exports.trimSuffix = function(str,unwanted) {
if(typeof str === "string" && typeof unwanted === "string") {
if(unwanted === "") {
return str.replace(/\s\s*$/, '');
return str.replace(/\s\s*$/, "");
} else {
// Safely regexp-escape the unwanted text
unwanted = unwanted.replace(/[\\^$*+?.()|[\]{}]/g, '\\$&');
var regex = new RegExp('(' + unwanted + ')+$');
return str.replace(regex, '');
unwanted = unwanted.replace(/[\\^$*+?.()|[\]{}]/g, "\\$&");
var regex = new RegExp("(" + unwanted + ")+$");
return str.replace(regex, "");
}
} else {
return str;
@@ -163,14 +101,14 @@ Convert a string to sentence case (ie capitalise first letter)
*/
exports.toSentenceCase = function(str) {
return (str || "").replace(/^\S/, function(c) {return c.toUpperCase();});
}
};
/*
Convert a string to title case (ie capitalise each initial letter)
*/
exports.toTitleCase = function(str) {
return (str || "").replace(/(^|\s)\S/g, function(c) {return c.toUpperCase();});
}
};
/*
Find the line break preceding a given position in a string
@@ -212,18 +150,6 @@ exports.count = function(object) {
return Object.keys(object || {}).length;
};
/*
Determine whether an array-item is an object-property
*/
exports.hopArray = function(object,array) {
for(var i=0; i<array.length; i++) {
if($tw.utils.hop(object,array[i])) {
return true;
}
}
return false;
};
/*
Remove entries from an array
array: array to modify
@@ -432,8 +358,8 @@ exports.formatDateString = function(date,template) {
}],
[/^TZD/, function() {
var tz = date.getTimezoneOffset(),
atz = Math.abs(tz);
return (tz < 0 ? '+' : '-') + $tw.utils.pad(Math.floor(atz / 60)) + ':' + $tw.utils.pad(atz % 60);
atz = Math.abs(tz);
return (tz < 0 ? "+" : "-") + $tw.utils.pad(Math.floor(atz / 60)) + ":" + $tw.utils.pad(atz % 60);
}],
[/^wYY/, function() {
return $tw.utils.pad($tw.utils.getYearForWeekNo(date) - 2000);
@@ -642,9 +568,9 @@ exports.unescapeLineBreaks = function(s) {
exports.escape = function(ch) {
var charCode = ch.charCodeAt(0);
if(charCode <= 0xFF) {
return '\\x' + $tw.utils.pad(charCode.toString(16).toUpperCase());
return "\\x" + $tw.utils.pad(charCode.toString(16).toUpperCase());
} else {
return '\\u' + $tw.utils.pad(charCode.toString(16).toUpperCase(),4);
return "\\u" + $tw.utils.pad(charCode.toString(16).toUpperCase(),4);
}
};
@@ -661,11 +587,11 @@ exports.stringify = function(s, rawUnicode) {
*/
var regex = rawUnicode ? /[\x00-\x1f]/g : /[\x00-\x1f\x80-\uFFFF]/g;
return (s || "")
.replace(/\\/g, '\\\\') // backslash
.replace(/\\/g, "\\\\") // backslash
.replace(/"/g, '\\"') // double quote character
.replace(/'/g, "\\'") // single quote character
.replace(/\r/g, '\\r') // carriage return
.replace(/\n/g, '\\n') // line feed
.replace(/\r/g, "\\r") // carriage return
.replace(/\n/g, "\\n") // line feed
.replace(regex, exports.escape); // non-ASCII characters
};
@@ -675,15 +601,15 @@ exports.jsonStringify = function(s, rawUnicode) {
// See http://www.json.org/
var regex = rawUnicode ? /[\x00-\x1f]/g : /[\x00-\x1f\x80-\uFFFF]/g;
return (s || "")
.replace(/\\/g, '\\\\') // backslash
.replace(/\\/g, "\\\\") // backslash
.replace(/"/g, '\\"') // double quote character
.replace(/\r/g, '\\r') // carriage return
.replace(/\n/g, '\\n') // line feed
.replace(/\x08/g, '\\b') // backspace
.replace(/\x0c/g, '\\f') // formfeed
.replace(/\t/g, '\\t') // tab
.replace(/\r/g, "\\r") // carriage return
.replace(/\n/g, "\\n") // line feed
.replace(/\x08/g, "\\b") // backspace
.replace(/\x0c/g, "\\f") // formfeed
.replace(/\t/g, "\\t") // tab
.replace(regex,function(s) {
return '\\u' + $tw.utils.pad(s.charCodeAt(0).toString(16).toUpperCase(),4);
return "\\u" + $tw.utils.pad(s.charCodeAt(0).toString(16).toUpperCase(),4);
}); // non-ASCII characters
};
@@ -691,7 +617,7 @@ exports.jsonStringify = function(s, rawUnicode) {
Escape the RegExp special characters with a preceding backslash
*/
exports.escapeRegExp = function(s) {
return s.replace(/[\-\/\\\^\$\*\+\?\.\(\)\|\[\]\{\}]/g, '\\$&');
return s.replace(/[\-\/\\\^\$\*\+\?\.\(\)\|\[\]\{\}]/g, "\\$&");
};
/*
@@ -774,7 +700,7 @@ exports.parseTextReference = function(textRef) {
}
} else {
// If we couldn't parse it
result.title = textRef
result.title = textRef;
}
return result;
};
@@ -833,60 +759,17 @@ Cryptographic hash function as used by sha256 filter operator
options.length .. number of characters returned defaults to 64
*/
exports.sha256 = function(str, options) {
options = options || {}
options = options || {};
return $tw.sjcl.codec.hex.fromBits($tw.sjcl.hash.sha256.hash(str)).substr(0,options.length || 64);
}
/*
Base64 utility functions that work in either browser or Node.js
*/
if(typeof window !== 'undefined') {
exports.btoa = function(binstr) { return window.btoa(binstr); }
exports.atob = function(b64) { return window.atob(b64); }
} else {
exports.btoa = function(binstr) {
return Buffer.from(binstr, 'binary').toString('base64');
}
exports.atob = function(b64) {
return Buffer.from(b64, 'base64').toString('binary');
}
}
exports.base64ToBytes = function(base64) {
const binString = exports.atob(base64);
return Uint8Array.from(binString, (m) => m.codePointAt(0));
};
exports.bytesToBase64 = function(bytes) {
const binString = Array.from(bytes, (byte) => String.fromCodePoint(byte)).join("");
return exports.btoa(binString);
};
exports.base64EncodeUtf8 = function(str) {
if ($tw.browser) {
return exports.bytesToBase64(new TextEncoder().encode(str));
} else {
const buff = Buffer.from(str, "utf-8");
return buff.toString("base64");
}
};
exports.base64DecodeUtf8 = function(str) {
if ($tw.browser) {
return new TextDecoder().decode(exports.base64ToBytes(str));
} else {
const buff = Buffer.from(str, "base64");
return buff.toString("utf-8");
}
};
/*
Decode a base64 string
*/
exports.base64Decode = function(string64,binary,urlsafe) {
const encoded = urlsafe ? string64.replace(/_/g,'/').replace(/-/g,'+') : string64;
if(binary) return exports.atob(encoded)
else return exports.base64DecodeUtf8(encoded);
const encoded = urlsafe ? string64.replace(/_/g,"/").replace(/-/g,"+") : string64;
if(binary) return $tw.utils.atob(encoded);
else return $tw.utils.base64DecodeUtf8(encoded);
};
/*
@@ -894,10 +777,10 @@ Encode a string to base64
*/
exports.base64Encode = function(string64,binary,urlsafe) {
let encoded;
if(binary) encoded = exports.btoa(string64);
else encoded = exports.base64EncodeUtf8(string64);
if(binary) encoded = $tw.utils.btoa(string64);
else encoded = $tw.utils.base64EncodeUtf8(string64);
if(urlsafe) {
encoded = encoded.replace(/\+/g,'-').replace(/\//g,'_');
encoded = encoded.replace(/\+/g,"-").replace(/\//g,"_");
}
return encoded;
};
@@ -952,44 +835,6 @@ exports.makeDataUri = function(text,type,_canonical_uri) {
return parts.join("");
};
/*
Useful for finding out the fully escaped CSS selector equivalent to a given tag. For example:
$tw.utils.tagToCssSelector("$:/tags/Stylesheet") --> tc-tagged-\%24\%3A\%2Ftags\%2FStylesheet
*/
exports.tagToCssSelector = function(tagName) {
return "tc-tagged-" + encodeURIComponent(tagName).replace(/[!"#$%&'()*+,\-./:;<=>?@[\\\]^`{\|}~,]/mg,function(c) {
return "\\" + c;
});
};
/*
IE does not have sign function
*/
exports.sign = Math.sign || function(x) {
x = +x; // convert to a number
if(x === 0 || isNaN(x)) {
return x;
}
return x > 0 ? 1 : -1;
};
/*
IE does not have an endsWith function
*/
exports.strEndsWith = function(str,ending,position) {
if(str.endsWith) {
return str.endsWith(ending,position);
} else {
if(typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > str.length) {
position = str.length;
}
position -= ending.length;
var lastIndex = str.indexOf(ending, position);
return lastIndex !== -1 && lastIndex === position;
}
};
/*
Return system information useful for debugging
*/
@@ -1016,10 +861,6 @@ exports.parseInt = function(str) {
return parseInt(str,10) || 0;
};
exports.stringifyNumber = function(num) {
return num + "";
};
exports.makeCompareFunction = function(type,options) {
options = options || {};
// set isCaseSensitive to true if not defined in options
@@ -1073,3 +914,56 @@ exports.makeCompareFunction = function(type,options) {
};
return (types[type] || types[options.defaultType] || types.number);
};
/*
Split text into parts (lines or words) for diff operations
Adapted from https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs
*/
exports.diffPartsToChars = function(text1,text2,mode) {
const lineArray = [""],
lineHash = Object.create(null);
function diff_linesToPartsMunge_(text,mode) {
let chars = "",
lineStart = 0,
lineEnd = -1,
lineArrayLength = lineArray.length,
regexpResult;
const searchRegexp = /\W+/g;
while(lineEnd < text.length - 1) {
if(mode === "words") {
regexpResult = searchRegexp.exec(text);
lineEnd = searchRegexp.lastIndex;
if(regexpResult === null) {
lineEnd = text.length;
}
lineEnd = --lineEnd;
} else {
lineEnd = text.indexOf("\n", lineStart);
if(lineEnd === -1) {
lineEnd = text.length - 1;
}
}
let line = text.substring(lineStart, lineEnd + 1);
if(line in lineHash) {
chars += String.fromCharCode(lineHash[line]);
} else {
if(lineArrayLength === maxLines) {
line = text.substring(lineStart);
lineEnd = text.length;
}
chars += String.fromCharCode(lineArrayLength);
lineHash[line] = lineArrayLength;
lineArray[lineArrayLength++] = line;
}
lineStart = lineEnd + 1;
}
return chars;
}
let maxLines = 40000;
const chars1 = diff_linesToPartsMunge_(text1,mode);
maxLines = 65535;
const chars2 = diff_linesToPartsMunge_(text2,mode);
return {chars1, chars2, lineArray};
};

View File

@@ -1,3 +1,4 @@
/* eslint-disable no-unused-vars */
/*\
title: $:/core/modules/widgets/action-log.js
type: application/javascript
@@ -32,7 +33,7 @@ LogWidget.prototype.execute = function(){
this.message = this.getAttribute("$$message","debug");
this.logAll = this.getAttribute("$$all","no") === "yes" ? true : false;
this.filter = this.getAttribute("$$filter");
}
};
/*
Refresh the widget by ensuring our attributes are up to date
@@ -69,10 +70,11 @@ LogWidget.prototype.log = function() {
});
// Collect values of all variables, using the source text for functions
for(var v in this.variables) {
var variableInfo = this.getVariableInfo(v);
if(variableInfo && variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) {
allVars[v] = variableInfo.text;
var variable = this.parentWidget && this.parentWidget.variables[v];
if(variable && variable.isFunctionDefinition) {
allVars[v] = variable.value;
} else {
var variableInfo = this.getVariableInfo(v);
allVars[v] = variableInfo.resultList.length > 1 ? variableInfo.resultList : variableInfo.text;
}
}
@@ -94,6 +96,6 @@ LogWidget.prototype.log = function() {
console.groupEnd();
}
console.groupEnd();
}
};
exports["action-log"] = LogWidget;

View File

@@ -62,8 +62,8 @@ SendMessageWidget.prototype.invokeAction = function(triggeringWidget,event) {
var paramObject = Object.create(null);
// Add names/values pairs if present
if(this.actionNames && this.actionValues) {
var names = this.wiki.filterTiddlers(this.actionNames,this),
values = this.wiki.filterTiddlers(this.actionValues,this);
var names = this.wiki.filterTiddlers(this.actionNames,this,{defaultFilterRunPrefix: "all"}),
values = this.wiki.filterTiddlers(this.actionValues,this,{defaultFilterRunPrefix: "all"});
$tw.utils.each(names,function(name,index) {
paramObject[name] = values[index] || "";
});

View File

@@ -56,10 +56,10 @@ Invoke the action associated with this widget
*/
SetMultipleFieldsWidget.prototype.invokeAction = function(triggeringWidget,event) {
var tiddler = this.wiki.getTiddler(this.actionTiddler),
names, values = this.wiki.filterTiddlers(this.actionValues,this);
names, values = this.wiki.filterTiddlers(this.actionValues,this,{defaultFilterRunPrefix: "all"});
if(this.actionFields) {
var additions = {};
names = this.wiki.filterTiddlers(this.actionFields,this);
names = this.wiki.filterTiddlers(this.actionFields,this,{defaultFilterRunPrefix: "all"});
$tw.utils.each(names,function(fieldname,index) {
additions[fieldname] = values[index] || "";
});
@@ -68,7 +68,7 @@ SetMultipleFieldsWidget.prototype.invokeAction = function(triggeringWidget,event
this.wiki.addTiddler(new $tw.Tiddler(creationFields,tiddler,{title: this.actionTiddler},modificationFields,additions));
} else if(this.actionIndexes) {
var data = this.wiki.getTiddlerData(this.actionTiddler,Object.create(null));
names = this.wiki.filterTiddlers(this.actionIndexes,this);
names = this.wiki.filterTiddlers(this.actionIndexes,this,{defaultFilterRunPrefix: "all"});
$tw.utils.each(names,function(name,index) {
data[name] = values[index] || "";
});

View File

@@ -9,6 +9,8 @@ Button widget
"use strict";
const ALLOWED_SELECTED_ARIA_ATTR = ["aria-checked", "aria-selected", "aria-pressed"];
var Widget = require("$:/core/modules/widgets/widget.js").widget;
var Popup = require("$:/core/modules/utils/dom/popup.js");
@@ -44,9 +46,14 @@ ButtonWidget.prototype.render = function(parent,nextSibling) {
var classes = this["class"].split(" ") || [],
isPoppedUp = (this.popup || this.popupTitle) && this.isPoppedUp();
if(this.selectedClass) {
if((this.set || this.setTitle) && this.setTo && this.isSelected()) {
$tw.utils.pushTop(classes, this.selectedClass.split(" "));
domNode.setAttribute("aria-checked", "true");
if((this.set || this.setTitle) && this.setTo) {
const selectedAria = ALLOWED_SELECTED_ARIA_ATTR.includes(this.selectedAria) ? this.selectedAria : "aria-checked";
if(this.isSelected()) {
$tw.utils.pushTop(classes, this.selectedClass.split(" "));
domNode.setAttribute(selectedAria, "true");
} else {
domNode.setAttribute(selectedAria, "false");
}
}
if(isPoppedUp) {
$tw.utils.pushTop(classes,this.selectedClass.split(" "));
@@ -221,6 +228,7 @@ ButtonWidget.prototype.execute = function() {
this.style = this.getAttribute("style");
this["class"] = this.getAttribute("class","");
this.selectedClass = this.getAttribute("selectedClass");
this.selectedAria = this.getAttribute("selectedAria");
this.defaultSetValue = this.getAttribute("default","");
this.buttonTag = this.getAttribute("tag");
this.dragTiddler = this.getAttribute("dragTiddler");

View File

@@ -9,8 +9,8 @@ Widget to display a diff between two texts
"use strict";
var Widget = require("$:/core/modules/widgets/widget.js").widget,
dmp = require("$:/core/modules/utils/diff-match-patch/diff_match_patch.js");
var Widget = require("$:/core/modules/widgets/widget.js").widget;
const dmp = require("$:/core/modules/utils/diff-match-patch/diff_match_patch.js");
var DiffTextWidget = function(parseTreeNode,options) {
this.initialise(parseTreeNode,options);
@@ -34,19 +34,25 @@ DiffTextWidget.prototype.render = function(parent,nextSibling) {
this.parentDomNode = parent;
this.computeAttributes();
this.execute();
// Create the diff
var dmpObject = new dmp.diff_match_patch(),
diffs = dmpObject.diff_main(this.getAttribute("source",""),this.getAttribute("dest",""));
// Create the diff object
const editCost = $tw.utils.parseNumber(this.getAttribute("editcost","4"));
const mode = this.getAttribute("mode") || "chars";
let diffs;
if(mode === "lines" || mode === "words") {
diffs = diffLineWordMode(this.getAttribute("source",""),this.getAttribute("dest",""),mode,editCost);
} else {
diffs = dmp.diffMain(this.getAttribute("source",""),this.getAttribute("dest",""),{diffEditCost: editCost});
}
// Apply required cleanup
switch(this.getAttribute("cleanup","semantic")) {
case "none":
// No cleanup
break;
case "efficiency":
dmpObject.diff_cleanupEfficiency(diffs);
dmp.diffCleanupEfficiency(diffs, {diffEditCost: editCost});
break;
default: // case "semantic"
dmpObject.diff_cleanupSemantic(diffs);
dmp.diffCleanupSemantic(diffs);
break;
}
// Create the elements
@@ -132,7 +138,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
*/
DiffTextWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(changedAttributes.source || changedAttributes.dest || changedAttributes.cleanup) {
if(changedAttributes.source || changedAttributes.dest || changedAttributes.cleanup || changedAttributes.mode || changedAttributes.editcost) {
this.refreshSelf();
return true;
} else {
@@ -140,4 +146,15 @@ DiffTextWidget.prototype.refresh = function(changedTiddlers) {
}
};
// This function is adapted from https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs
function diffLineWordMode(text1,text2,mode,editCost) {
var a = $tw.utils.diffPartsToChars(text1,text2,mode);
var lineText1 = a.chars1;
var lineText2 = a.chars2;
var lineArray = a.lineArray;
var diffs = dmp.diffMain(lineText1,lineText2,{diffEditCost: editCost});
dmp.diffCharsToLines(diffs,lineArray);
return diffs;
}
exports["diff-text"] = DiffTextWidget;

View File

@@ -72,8 +72,8 @@ GenesisWidget.prototype.execute = function() {
this.attributeNames = [];
this.attributeValues = [];
if(this.genesisNames && this.genesisValues) {
this.attributeNames = this.wiki.filterTiddlers(self.genesisNames,this);
this.attributeValues = this.wiki.filterTiddlers(self.genesisValues,this);
this.attributeNames = this.wiki.filterTiddlers(self.genesisNames,this,{defaultFilterRunPrefix: "all"});
this.attributeValues = this.wiki.filterTiddlers(self.genesisValues,this,{defaultFilterRunPrefix: "all"});
$tw.utils.each(this.attributeNames,function(varname,index) {
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],varname,self.attributeValues[index] || "");
});
@@ -103,8 +103,8 @@ GenesisWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes(),
filterNames = this.getAttribute("$names",""),
filterValues = this.getAttribute("$values",""),
attributeNames = this.wiki.filterTiddlers(filterNames,this),
attributeValues = this.wiki.filterTiddlers(filterValues,this);
attributeNames = this.wiki.filterTiddlers(filterNames,this,{defaultFilterRunPrefix: "all"}),
attributeValues = this.wiki.filterTiddlers(filterValues,this,{defaultFilterRunPrefix: "all"});
if($tw.utils.count(changedAttributes) > 0 || !$tw.utils.isArrayEqual(this.attributeNames,attributeNames) || !$tw.utils.isArrayEqual(this.attributeValues,attributeValues)) {
this.refreshSelf();
return true;

View File

@@ -7,7 +7,6 @@ This widget allows defining multiple variables at once, while allowing
the later variables to depend upon the earlier ones.
```
\define helloworld() Hello world!
<$let currentTiddler="target" value={{!!value}} currentTiddler="different">
{{!!value}} will be different from <<value>>
</$let>
@@ -56,7 +55,7 @@ LetWidget.prototype.computeAttributes = function() {
});
// Run through again, setting variables and looking for differences
$tw.utils.each(this.currentValueFor,function(value,name) {
if(!$tw.utils.isArrayEqual(self.attributes[name],value)) {
if(self.attributes[name] === undefined || !$tw.utils.isArrayEqual(self.attributes[name],value)) {
self.attributes[name] = value;
self.setVariable(name,value);
changedAttributes[name] = true;
@@ -68,7 +67,7 @@ LetWidget.prototype.computeAttributes = function() {
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)) {
if($tw.utils.hop(this.currentValueFor,name)) {
var value = this.currentValueFor[name];
return {
text: value[0] || "",

View File

@@ -89,13 +89,33 @@ RevealWidget.prototype.positionPopup = function(domNode) {
top = this.popup.top + this.popup.height;
break;
}
// if requested, clamp the popup so that it will always be fully inside its parent (the first upstream element with position:relative), as long as the popup is smaller than its parent
// if position is absolute then clamping is done to the canvas boundary, since there is no "parent"
if(this.clampToParent !== "none") {
if(this.popup.absolute) {
var parentWidth = window.innerWidth,
parentHeight = window.innerHeight;
} else {
var parentWidth = domNode.offsetParent.offsetWidth,
parentHeight = domNode.offsetParent.offsetHeight;
}
var right = left + domNode.offsetWidth,
bottom = top + domNode.offsetHeight;
if((this.clampToParent === "both" || this.clampToParent === "right") && right > parentWidth) {
left = parentWidth - domNode.offsetWidth;
}
if((this.clampToParent === "both" || this.clampToParent === "bottom") && bottom > parentHeight) {
top = parentHeight - domNode.offsetHeight;
}
// clamping on left and top sides is taken care of by positionAllowNegative
}
if(!this.positionAllowNegative) {
left = Math.max(0,left);
top = Math.max(0,top);
}
if (this.popup.absolute) {
if(this.popup.absolute) {
// Traverse the offsetParent chain and correct the offset to make it relative to the parent node.
for (var offsetParentDomNode = domNode.offsetParent; offsetParentDomNode; offsetParentDomNode = offsetParentDomNode.offsetParent) {
for(var offsetParentDomNode = domNode.offsetParent; offsetParentDomNode; offsetParentDomNode = offsetParentDomNode.offsetParent) {
left -= offsetParentDomNode.offsetLeft;
top -= offsetParentDomNode.offsetTop;
}
@@ -123,6 +143,7 @@ RevealWidget.prototype.execute = function() {
this.openAnimation = this.animate === "no" ? undefined : "open";
this.closeAnimation = this.animate === "no" ? undefined : "close";
this.updatePopupPosition = this.getAttribute("updatePopupPosition","no") === "yes";
this.clampToParent = this.getAttribute("clamp","none");
// Compute the title of the state tiddler and read it
this.stateTiddlerTitle = this.state;
this.stateTitle = this.getAttribute("stateTitle");
@@ -141,7 +162,7 @@ Read the state tiddler
RevealWidget.prototype.readState = function() {
// Read the information from the state tiddler
var state,
defaultState = this["default"];
defaultState = this["default"];
if(this.stateTitle) {
var stateTitleTiddler = this.wiki.getTiddler(this.stateTitle);
if(this.stateField) {
@@ -252,18 +273,18 @@ RevealWidget.prototype.updateState = function() {
this.renderChildren(domNode,null);
}
// Animate our DOM node
if(!domNode.isTiddlyWikiFakeDom && this.type === "popup" && this.isOpen) {
this.positionPopup(domNode);
$tw.utils.addClass(domNode,"tc-popup"); // Make sure that clicks don't dismiss popups within the revealed content
}
if(this.isOpen) {
domNode.removeAttribute("hidden");
$tw.anim.perform(this.openAnimation,domNode);
// Position popup after making it visible to ensure correct dimensions
if(!domNode.isTiddlyWikiFakeDom && this.type === "popup") {
this.positionPopup(domNode);
$tw.utils.addClass(domNode,"tc-popup"); // Make sure that clicks don't dismiss popups within the revealed content
}
$tw.anim.perform(this.openAnimation,domNode);
} else {
$tw.anim.perform(this.closeAnimation,domNode,{callback: function() {
//make sure that the state hasn't changed during the close animation
self.readState()
self.readState();
if(!self.isOpen) {
domNode.setAttribute("hidden","true");
}

View File

@@ -12,7 +12,7 @@ Widget to set multiple variables at once from a list of names and a list of valu
var Widget = require("$:/core/modules/widgets/widget.js").widget;
var SetMultipleVariablesWidget = function(parseTreeNode,options) {
this.initialise(parseTreeNode,options);
this.initialise(parseTreeNode,options);
};
/*
@@ -24,52 +24,52 @@ SetMultipleVariablesWidget.prototype = new Widget();
Render this widget into the DOM
*/
SetMultipleVariablesWidget.prototype.render = function(parent,nextSibling) {
this.parentDomNode = parent;
this.computeAttributes();
this.execute();
this.renderChildren(parent,nextSibling);
this.parentDomNode = parent;
this.computeAttributes();
this.execute();
this.renderChildren(parent,nextSibling);
};
/*
Compute the internal state of the widget
*/
SetMultipleVariablesWidget.prototype.execute = function() {
// Setup our variables
this.setVariables();
// Construct the child widgets
this.makeChildWidgets();
// Setup our variables
this.setVariables();
// Construct the child widgets
this.makeChildWidgets();
};
SetMultipleVariablesWidget.prototype.setVariables = function() {
// Set the variables
var self = this,
filterNames = this.getAttribute("$names",""),
filterValues = this.getAttribute("$values","");
this.variableNames = [];
this.variableValues = [];
if(filterNames && filterValues) {
this.variableNames = this.wiki.filterTiddlers(filterNames,this);
this.variableValues = this.wiki.filterTiddlers(filterValues,this);
$tw.utils.each(this.variableNames,function(varname,index) {
self.setVariable(varname,self.variableValues[index]);
});
}
// Set the variables
var self = this,
filterNames = this.getAttribute("$names",""),
filterValues = this.getAttribute("$values","");
this.variableNames = [];
this.variableValues = [];
if(filterNames && filterValues) {
this.variableNames = this.wiki.filterTiddlers(filterNames,this,{defaultFilterRunPrefix: "all"});
this.variableValues = this.wiki.filterTiddlers(filterValues,this,{defaultFilterRunPrefix: "all"});
$tw.utils.each(this.variableNames,function(varname,index) {
self.setVariable(varname,self.variableValues[index]);
});
}
};
/*
Refresh the widget by ensuring our attributes are up to date
*/
SetMultipleVariablesWidget.prototype.refresh = function(changedTiddlers) {
var filterNames = this.getAttribute("$names",""),
filterValues = this.getAttribute("$values",""),
variableNames = this.wiki.filterTiddlers(filterNames,this),
variableValues = this.wiki.filterTiddlers(filterValues,this);
if(!$tw.utils.isArrayEqual(this.variableNames,variableNames) || !$tw.utils.isArrayEqual(this.variableValues,variableValues)) {
this.refreshSelf();
return true;
}
return this.refreshChildren(changedTiddlers);
var filterNames = this.getAttribute("$names",""),
filterValues = this.getAttribute("$values",""),
variableNames = this.wiki.filterTiddlers(filterNames,this,{defaultFilterRunPrefix: "all"}),
variableValues = this.wiki.filterTiddlers(filterValues,this,{defaultFilterRunPrefix: "all"});
if(!$tw.utils.isArrayEqual(this.variableNames,variableNames) || !$tw.utils.isArrayEqual(this.variableValues,variableValues)) {
this.refreshSelf();
return true;
}
return this.refreshChildren(changedTiddlers);
};
exports["setmultiplevariables"] = SetMultipleVariablesWidget;

View File

@@ -200,7 +200,7 @@ exports.generateNewTitle = function(baseTitle,options) {
c = (parseInt(options.startCount,10) > 0) ? parseInt(options.startCount,10) : 0,
prefix = (typeof(options.prefix) === "string") ? options.prefix : " ";
if (template) {
if(template) {
// "count" is important to avoid an endless loop in while(...)!!
template = (/\$count:?(\d+)?\$/i.test(template)) ? template : template + "$count$";
// .formatTitleString() expects strings as input
@@ -209,7 +209,7 @@ exports.generateNewTitle = function(baseTitle,options) {
title = $tw.utils.formatTitleString(template,{"base":baseTitle,"separator":prefix,"counter":(++c)+""});
}
} else {
if (c > 0) {
if(c > 0) {
title = baseTitle + prefix + c;
}
while(this.tiddlerExists(title) || this.isShadowTiddler(title) || this.findDraft(title)) {
@@ -369,31 +369,16 @@ Sort an array of tiddler titles by a specified field
isDescending: true if the sort should be descending
isCaseSensitive: true if the sort should consider upper and lower case letters to be different
*/
exports.sortTiddlers = function(titles,sortField,isDescending,isCaseSensitive,isNumeric,isAlphaNumeric) {
exports.sortTiddlers = function(titles,sortField,isDescending,isCaseSensitive,isNumeric,isAlphaNumeric,locale) {
var self = this;
if(sortField === "title") {
if(!isNumeric && !isAlphaNumeric) {
if(isCaseSensitive) {
if(isDescending) {
titles.sort(function(a,b) {
return b.localeCompare(a);
});
} else {
titles.sort(function(a,b) {
return a.localeCompare(b);
});
}
const sorter = new Intl.Collator(locale, { sensitivity: isCaseSensitive ? "variant" : "accent" });
if(isDescending) {
titles.sort((a,b) => sorter.compare(b, a));
} else {
if(isDescending) {
titles.sort(function(a,b) {
return b.toLowerCase().localeCompare(a.toLowerCase());
});
} else {
titles.sort(function(a,b) {
return a.toLowerCase().localeCompare(b.toLowerCase());
});
}
}
titles.sort((a,b) => sorter.compare(a, b));
}
} else {
titles.sort(function(a,b) {
var x,y;
@@ -414,14 +399,8 @@ exports.sortTiddlers = function(titles,sortField,isDescending,isCaseSensitive,is
}
}
}
if(isAlphaNumeric) {
return isDescending ? b.localeCompare(a,undefined,{numeric: true,sensitivity: "base"}) : a.localeCompare(b,undefined,{numeric: true,sensitivity: "base"});
}
if(!isCaseSensitive) {
a = a.toLowerCase();
b = b.toLowerCase();
}
return isDescending ? b.localeCompare(a) : a.localeCompare(b);
const sorter = new Intl.Collator(locale, { numeric: isAlphaNumeric, sensitivity: isAlphaNumeric ? "base" : isCaseSensitive ? "variant" : "accent" });
return isDescending ? sorter.compare(b, a) : sorter.compare(a, b);
});
}
} else {
@@ -463,14 +442,8 @@ exports.sortTiddlers = function(titles,sortField,isDescending,isCaseSensitive,is
}
a = String(a);
b = String(b);
if(isAlphaNumeric) {
return isDescending ? b.localeCompare(a,undefined,{numeric: true,sensitivity: "base"}) : a.localeCompare(b,undefined,{numeric: true,sensitivity: "base"});
}
if(!isCaseSensitive) {
a = a.toLowerCase();
b = b.toLowerCase();
}
return isDescending ? b.localeCompare(a) : a.localeCompare(b);
const sorter = new Intl.Collator(locale, { numeric: isAlphaNumeric, sensitivity: isAlphaNumeric ? "base" : isCaseSensitive ? "variant" : "accent" });
return isDescending ? sorter.compare(b, a) : sorter.compare(a, b);
});
}
};
@@ -532,7 +505,7 @@ exports.getTiddlerLinks = function(title) {
return self.extractLinks(parser.tree);
}
return [];
});
}).slice(0);
};
/*
@@ -551,8 +524,9 @@ exports.getTiddlerBacklinks = function(targetTitle) {
backlinks.push(title);
}
});
return backlinks;
}
return backlinks;
return backlinks.slice(0);
};
@@ -578,7 +552,7 @@ exports.extractTranscludes = function(parseTreeRoot, title) {
}
}
} else if(parseTreeNode.attributes.tiddler) {
if (parseTreeNode.attributes.tiddler.type === "string") {
if(parseTreeNode.attributes.tiddler.type === "string") {
// Old transclude widget usage
value = parseTreeNode.attributes.tiddler.value;
}
@@ -618,7 +592,7 @@ exports.getTiddlerTranscludes = function(title) {
return self.extractTranscludes(parser.tree,title);
}
return [];
});
}).slice(0);
};
/*
@@ -630,9 +604,9 @@ exports.getTiddlerBacktranscludes = function(targetTitle) {
backtranscludes = backIndexer && backIndexer.subIndexers.transclude.lookup(targetTitle);
if(!backtranscludes) {
backtranscludes = [];
return [];
}
return backtranscludes;
return backtranscludes.slice(0);
};
/*
@@ -641,7 +615,7 @@ Return a hashmap of tiddler titles that are referenced but not defined. Each val
exports.getMissingTitles = function() {
var self = this,
missing = [];
// We should cache the missing tiddler list, even if we recreate it every time any tiddler is modified
// We should cache the missing tiddler list, even if we recreate it every time any tiddler is modified
this.forEachTiddler(function(title,tiddler) {
var links = self.getTiddlerLinks(title);
$tw.utils.each(links,function(link) {
@@ -683,7 +657,7 @@ exports.getTiddlersWithTag = function(tag) {
return self.sortByList(tagmap[tag],tag);
});
}
return results;
return results.slice(0);
};
/*
@@ -734,7 +708,7 @@ exports.findListingsOfTiddler = function(targetTitle,fieldName) {
for(var i = 0; i < list.length; i++) {
var listItem = list[i],
listing = listings[listItem] || [];
if (listing.indexOf(title) === -1) {
if(listing.indexOf(title) === -1) {
listing.push(title);
}
listings[listItem] = listing;
@@ -743,7 +717,7 @@ exports.findListingsOfTiddler = function(targetTitle,fieldName) {
});
return listings;
});
return listings[targetTitle] || [];
return (listings[targetTitle] || []).slice(0);
};
/*
@@ -782,7 +756,7 @@ exports.sortByList = function(array,listTitle) {
}
}
// If a new position is specified, let's move it
if (newPos !== -1) {
if(newPos !== -1) {
// get its current Pos, and make sure
// sure that it's _actually_ in the list
// and that it would _actually_ move
@@ -1059,7 +1033,7 @@ Options include:
exports.parseText = function(type,text,options) {
text = text || "";
options = options || {};
var Parser = $tw.utils.getParser(type,options)
var Parser = $tw.utils.getParser(type,options);
// Return the parser instance
return new Parser(type,text,{
parseAsInline: options.parseAsInline,
@@ -1078,11 +1052,11 @@ exports.parseTiddler = function(title,options) {
tiddler = this.getTiddler(title),
self = this;
return tiddler ? this.getCacheForTiddler(title,cacheType,function() {
if(tiddler.hasField("_canonical_uri")) {
options._canonical_uri = tiddler.fields._canonical_uri;
}
return self.parseText(tiddler.fields.type,tiddler.fields.text,options);
}) : null;
if(tiddler.hasField("_canonical_uri")) {
options._canonical_uri = tiddler.fields._canonical_uri;
}
return self.parseText(tiddler.fields.type,tiddler.fields.text,options);
}) : null;
};
exports.parseTextReference = function(title,field,index,options) {
@@ -1138,7 +1112,7 @@ exports.getTextReferenceParserInfo = function(title,field,index,options) {
parserInfo.parserType = null;
}
return parserInfo;
}
};
/*
Parse a block of text of a specified MIME type
@@ -1164,7 +1138,7 @@ exports.getSubstitutedText = function(text,widget,options) {
});
// Substitute any variable references with their values
return output.replace(/\$\((.+?)\)\$/g, function(match,varname) {
return widget.getVariable(varname,{defaultValue: ""})
return widget.getVariable(varname,{defaultValue: ""});
});
};
@@ -1242,7 +1216,7 @@ exports.makeTranscludeWidget = function(title,options) {
name: "recursionMarker",
type: "string",
value: options.recursionMarker || "yes"
},
},
tiddler: {
name: "tiddler",
type: "string",
@@ -1385,7 +1359,7 @@ exports.search = function(text,options) {
}
}
}
// Accumulate the array of fields to be searched or excluded from the search
// Accumulate the array of fields to be searched or excluded from the search
var fields = [];
if(options.field) {
if($tw.utils.isArray(options.field)) {
@@ -1518,7 +1492,7 @@ exports.checkTiddlerText = function(title,targetText,options) {
targetText = targetText.toLowerCase();
}
return text === targetText;
}
};
/*
Execute an action string without an associated context widget
@@ -1642,7 +1616,7 @@ exports.findDraft = function(targetTitle) {
}
});
return draftTitle;
}
};
/*
Check whether the specified draft tiddler has been modified.
@@ -1669,7 +1643,7 @@ historyTitle: title of history tiddler (defaults to $:/HistoryList)
exports.addToHistory = function(title,fromPageRect,historyTitle) {
var story = new $tw.Story({wiki: this, historyTitle: historyTitle});
story.addToHistory(title,fromPageRect);
console.log("$tw.wiki.addToHistory() is deprecated since V5.1.23! Use the this.story.addToHistory() from the story-object!")
console.log("$tw.wiki.addToHistory() is deprecated since V5.1.23! Use the this.story.addToHistory() from the story-object!");
};
/*
@@ -1682,7 +1656,7 @@ options: see story.js
exports.addToStory = function(title,fromTitle,storyTitle,options) {
var story = new $tw.Story({wiki: this, storyTitle: storyTitle});
story.addToStory(title,fromTitle,options);
console.log("$tw.wiki.addToStory() is deprecated since V5.1.23! Use the this.story.addToStory() from the story-object!")
console.log("$tw.wiki.addToStory() is deprecated since V5.1.23! Use the this.story.addToStory() from the story-object!");
};
/*

View File

@@ -0,0 +1,10 @@
title: $:/palettes/AutoToggle
name: AutoToggle
description: Automatically switch between dark and light modes
tags: $:/tags/Palette
type: application/x-tiddler-dictionary
color-scheme: [{$:/info/browser/darkmode}!match[yes]then[light]else[dark]]
settings: $:/palettes/AutoToggle/Settings
palette-import@light: $:/palettes/TwentyTwenties
palette-import@dark: $:/palettes/TwentyTwentiesDark
category: 2026

View File

@@ -0,0 +1,19 @@
title: $:/palettes/AutoToggle/Settings
\procedure set-imported-palette(field)
<$select field=<<field>>>
<$list filter="[all[shadows+tiddlers]tag[$:/tags/Palette]sort[name]] -[<currentTiddler>]">
<option value=<<currentTiddler>>><$view field="name"><$view field="title"/></$view></option>
</$list>
</$select>
\end set-imported-palette
This palette can be used to automatically switch between two palettes based on the browser's dark mode setting.
<$tiddler tiddler={{$:/palette}}>
Light palette: <<set-imported-palette field:"palette-import@light">>
Dark palette: <<set-imported-palette field:"palette-import@dark">>
</$tiddler>

View File

@@ -0,0 +1,226 @@
title: $:/palettes/TwentyTwenties
name: TwentyTwenties
description: Modern and flexible
tags: $:/tags/Palette
type: application/x-tiddler-dictionary
color-scheme: light
settings: $:/palettes/TwentyTwenties/Settings
palette-import: $:/palettes/Vanilla
category: 2026
# Background and foreground colours, which are interpolated as required
base-paper: #FFFCF0
base-background: #edcec1
base-ink: #333344
?base-paper-ink: [tf.check-colour-contrast[base-paper],[base-ink],[45]]
?base-background-ink: [tf.check-colour-contrast[base-background],[base-ink],[45]]
# Primary colour, used for links and other accented elements
base-primary: #5778d8
?base-paper-primary: [tf.check-colour-contrast[base-paper],[base-primary],[45]]
?base-background-primary: [tf.check-colour-contrast[base-background],[base-primary],[45]]
# Secondary colour, used for alerts and other secondary elements
base-secondary: #f0e48a
?base-ink-secondary: [tf.check-colour-contrast[base-ink],[base-secondary],[45]]
# Tertiary base colour, used for monospaced text and other tertiary elements
base-tertiary: rgb(183, 95, 95)
?base-paper-tertiary: [tf.check-colour-contrast[base-paper],[base-tertiary],[45]]
# Basic spectrum colours
base-black: #100F0F
base-red: #D14D41
base-orange: #DA702C
base-yellow: #D0A215
base-green: #879A39
base-cyan: #3AA99F
base-blue: #4385BE
base-purple: #8B7EC8
base-magenta: #CE5D97
base-white: #FFFCF0
# Darker variants
# base-red: #AF3029
# base-orange: #BC5215
# base-yellow: #AD8301
# base-green: #66800B
# base-cyan: #24837B
# base-blue: #205EA6
# base-purple: #5E409D
# base-magenta: #A02F6F
# Palette definitions
alert-background: [tf.colour[base-secondary]]
alert-border: [tf.interpolate-colours[base-ink],[alert-background],[0.6]]
alert-highlight: [tf.interpolate-colours[base-ink],[base-primary],[0.3]]
alert-muted-foreground: [tf.interpolate-colours[base-ink],[alert-background],[0.4]]
background: [tf.colour[base-paper]]
blockquote-bar: [tf.colour[muted-foreground]]
button-background:
button-border:
button-foreground:
code-background: [tf.interpolate-colours[base-paper],[base-tertiary],[0.1]]
code-border: [tf.interpolate-colours[base-paper],[base-tertiary],[0.6]]
code-foreground: [tf.colour[base-tertiary]]
diff-delete-background: [tf.colour[base-red]]
diff-delete-foreground: [tf.colour[foreground]]
diff-equal-background:
diff-equal-foreground: [tf.colour[foreground]]
diff-insert-background: [tf.colour[base-green]]
diff-insert-foreground: [tf.colour[foreground]]
diff-invisible-background:
diff-invisible-foreground: [tf.colour[muted-foreground]]
dirty-indicator: [tf.colour[base-tertiary]]
download-background: [tf.interpolate-colours[base-paper],[base-green],[0.6]]
download-foreground: [tf.interpolate-colours[base-ink],[base-green],[0.1]]
dragger-background: [tf.colour[foreground]]
dragger-foreground: [tf.colour[background]]
dropdown-background: [tf.colour[background]]
dropdown-border: [tf.colour[muted-foreground]]
dropdown-tab-background-selected: [tf.colour[background]]
dropdown-tab-background: [tf.interpolate-colours[base-paper],[base-ink],[0.9]]
dropzone-background: [tf.colour[base-secondary]colour-set-alpha[0.7]]
external-link-background-hover: inherit
external-link-background-visited: inherit
external-link-background: inherit
external-link-foreground-hover: inherit
external-link-foreground-visited: [tf.colour[primary]]
external-link-foreground: [tf.colour[primary]]
footnote-target-background: [tf.interpolate-colours[base-paper],[base-ink],[0.2]]
foreground: [tf.colour[base-ink]]
highlight-background: [tf.interpolate-colours[base-paper],[base-yellow],[0.5]]
highlight-foreground: [tf.interpolate-colours[base-yellow],[base-ink],[0.8]]
menubar-background: #5778d8
menubar-foreground: #fff
message-background: [tf.interpolate-colours[base-paper],[base-blue],[0.2]]
message-border: [tf.interpolate-colours[base-blue],[base-ink],[0.5]]
message-foreground: [tf.interpolate-colours[base-blue],[base-ink],[0.8]]
modal-backdrop: [tf.colour[foreground]]
modal-background: [tf.colour[background]]
modal-border: #999999
modal-footer-background: #f5f5f5
modal-footer-border: #dddddd
modal-header-border: #eeeeee
muted-foreground: [tf.interpolate-colours[base-paper],[base-ink],[0.3]]
network-activity-foreground: #448844
notification-background: [tf.colour[base-tertiary]colour-set-oklch:l[0.9]]
notification-border: [tf.colour[base-tertiary]colour-set-oklch:l[0.2]]
page-background: [tf.colour[base-background]]
pre-background: [tf.interpolate-colours[base-paper],[base-tertiary],[0.1]]
pre-border: [tf.interpolate-colours[base-paper],[base-tertiary],[0.6]]
primary: [tf.colour[base-primary]]
select-tag-background:
select-tag-foreground:
selection-background:
selection-foreground:
sidebar-button-foreground: [tf.colour[sidebar-controls-foreground]]
sidebar-controls-foreground-hover: [tf.interpolate-colours[base-ink],[base-background],[0.2]]
sidebar-controls-foreground: [tf.interpolate-colours[base-ink],[base-background],[0.8]]
sidebar-foreground-shadow: inherit
sidebar-foreground: =[tf.colour[base-ink]] =[tf.colour[base-paper]] =[tf.colour[base-background]] +[colour-best-contrast:DeltaPhi[]]
sidebar-muted-foreground-hover: [tf.colour[sidebar-muted-foreground]colour-set-oklch:l[0.3]]
sidebar-muted-foreground: [tf.interpolate-colours[foreground],[page-background],[0.6]]
sidebar-tab-background-selected: [tf.colour[tab-background-selected]]
sidebar-tab-background: [tf.colour[tab-background]]
sidebar-tab-border-selected: [tf.colour[tab-border-selected]]
sidebar-tab-border: [tf.colour[tab-border]]
sidebar-tab-divider: [tf.colour[tab-divider]]
sidebar-tab-foreground-selected: [tf.colour[tab-foreground-selected]]
sidebar-tab-foreground: [tf.colour[tab-foreground]]
sidebar-tiddler-link-foreground-hover: [tf.colour[sidebar-tiddler-link-foreground]colour-set-oklch:l[0.5]]
sidebar-tiddler-link-foreground: =[tf.colour[base-primary]] =[tf.colour[base-secondary]] =[tf.colour[base-tertiary]] =[tf.colour[base-background]] +[colour-best-contrast:DeltaPhi[]]
site-title-foreground: [tf.colour[tiddler-title-foreground]]
stability-deprecated: #ff0000
stability-experimental: #c07c00
stability-legacy: #0000ff
stability-stable: #008000
static-alert-foreground: #aaaaaa
tab-background-selected: [tf.colour[background]]
tab-background: [tf.interpolate-colours[base-paper],[base-ink],[0.2]]
tab-border-selected: [tf.colour[muted-foreground]]
tab-border: [tf.colour[muted-foreground]]
tab-divider: [tf.colour[muted-foreground]]
tab-foreground-selected: [tf.colour[tab-foreground]]
tab-foreground: [tf.colour[foreground]]
table-border: [tf.colour[foreground]]
table-footer-background: [tf.interpolate-colours[background],[foreground],[0.2]]
table-header-background: [tf.interpolate-colours[background],[foreground],[0.1]]
tag-background: [tf.interpolate-colours[base-paper],[base-yellow],[0.9]]
tag-foreground: [tf.interpolate-colours[base-yellow],[base-ink],[0.8]]
testcase-accent-level-1: #c1eaff
testcase-accent-level-2: #E3B740
testcase-accent-level-3: #5FD564
tiddler-background: [tf.colour[background]]
tiddler-border: [tf.interpolate-colours[base-paper],[base-background],[0.5]]
tiddler-controls-foreground-hover: [tf.interpolate-colours[background],[foreground],[0.7]]
tiddler-controls-foreground-selected: [tf.interpolate-colours[background],[foreground],[0.9]]
tiddler-controls-foreground: [tf.interpolate-colours[background],[foreground],[0.5]]
tiddler-editor-background: #f8f8f8
tiddler-editor-border-image: #ffffff
tiddler-editor-border: #cccccc
tiddler-editor-fields-even: #e0e8e0
tiddler-editor-fields-odd: #f0f4f0
tiddler-info-background: #f8f8f8
tiddler-info-border: #dddddd
tiddler-info-tab-background: #f8f8f8
tiddler-link-background: [tf.colour[background]]
tiddler-link-foreground: [tf.colour[primary]]
tiddler-subtitle-foreground: [tf.interpolate-colours[background],[foreground],[0.6]]
tiddler-title-foreground: [tf.interpolate-colours[background],[foreground],[0.9]]
toolbar-cancel-button:
toolbar-close-button:
toolbar-delete-button:
toolbar-done-button:
toolbar-edit-button:
toolbar-info-button:
toolbar-new-button:
toolbar-options-button:
toolbar-save-button:
tour-chooser-button-foreground: <<colour very-muted-foreground>>
tour-chooser-button-hover-background: <<colour muted-foreground>>
tour-chooser-button-hover-foreground: : <<colour background>>
tour-chooser-button-selected-background: <<colour primary>>
tour-chooser-button-selected-foreground: <<colour background>>
tour-chooser-dropdown-foreground: <<colour very-muted-foreground>>
tour-chooser-item-background: <<colour background>>
tour-chooser-item-border: <<colour muted-foreground>>
tour-chooser-item-foreground: <<colour foreground>>
tour-chooser-item-shadow: <<colour muted-foreground>>
tour-chooser-item-start-background: <<colour download-background>>
tour-chooser-item-start-foreground: <<colour background>>
tour-chooser-item-start-hover-background: <<colour primary>>
tour-chooser-item-start-hover-foreground: <<colour background>>
tour-fullscreen-background: <<colour page-background>>
tour-fullscreen-controls-foreground: <<colour muted-foreground>>
tour-navigation-buttons-back-background: red
tour-navigation-buttons-back-foreground: white
tour-navigation-buttons-hint-background: purple
tour-navigation-buttons-hint-foreground: white
tour-navigation-buttons-hover-background: <<colour foreground>>
tour-navigation-buttons-hover-foreground: <<colour background>>
tour-navigation-buttons-next-background: purple
tour-navigation-buttons-next-foreground: white
tour-overlay-background: #cbfff8
tour-overlay-border: #228877
tour-step-heading-background: none
tour-step-task-background: <<colour download-background>>
tour-step-task-foreground: <<colour download-foreground>>
untagged-background: #999999
very-muted-foreground: #888888
wikilist-background: #e5e5e5
wikilist-button-background: #acacac
wikilist-button-foreground: #000000
wikilist-button-open-hover: green
wikilist-button-open: #4fb82b
wikilist-button-remove-hover: red
wikilist-button-remove: #d85778
wikilist-button-reveal-hover: blue
wikilist-button-reveal: #5778d8
wikilist-droplink-dragover: [tf.colour[base-secondary]colour-set-alpha[0.7]]
wikilist-info: #000000
wikilist-item: #ffffff
wikilist-title-svg: [tf.colour[wikilist-title]]
wikilist-title: #666666
wikilist-toolbar-background: #d3d3d3
wikilist-toolbar-foreground: #888888
wikilist-url: #aaaaaa

View File

@@ -0,0 +1,12 @@
title: $:/palettes/TwentyTwenties/Dark
name: TwentyTwenties Dark
description: Modern and flexible, Darkish
tags: $:/tags/Palette
type: application/x-tiddler-dictionary
color-scheme: dark
palette-import: $:/palettes/TwentyTwenties
category: 2026
base-paper: #111122
base-background: #f5f0f9
base-ink: #8C8F80

View File

@@ -0,0 +1,12 @@
title: $:/palettes/TwentyTwenties/Green
name: TwentyTwenties (Green)
description: Modern and flexible, Greenish
tags: $:/tags/Palette
type: application/x-tiddler-dictionary
color-scheme: light
palette-import: $:/palettes/TwentyTwenties
category: 2026
base-paper: rgb(188, 255, 161)
base-background: rgb(94, 192, 145)
base-primary: #6e803c

View File

@@ -0,0 +1,13 @@
title: $:/palettes/TwentyTwenties/GreenP3
name: TwentyTwenties (Green P3)
description: Modern and flexible, Greenish and super bright
tags: $:/tags/Palette
type: application/x-tiddler-dictionary
color-scheme: light
palette-import: $:/palettes/TwentyTwenties
category: 2026
base-paper: color(display-p3 0.281 1 0.584 / 1)
base-background: color(display-p3 1 1 0 / 1)
base-primary: color(display-p3 1 0.563 1 / 1)

View File

@@ -0,0 +1,17 @@
title: $:/palettes/TwentyTwenties/Settings
\procedure entry(name,description)
<$text text=<<description>>/>: <$edit-text tiddler={{$:/palette}} index=<<name>> type="color" tag="input" default={{{ [function[colour],<name>] }}}/>
\end entry
<<entry name:"base-paper" description:"Paper">>
<<entry name:"base-background" description:"Page background">>
<<entry name:"base-ink" description:"Ink">>
<<entry name:"base-primary" description:"Primary">>
<<entry name:"base-secondary" description:"Secondary">>
<<entry name:"base-tertiary" description:"Tertiary">>

View File

@@ -0,0 +1,12 @@
title: $:/palettes/VanillaCherry
name: Vanilla Cherry
category: 2026
description: Pale and unobtrusive with a cherry on top
tags: $:/tags/Palette
type: application/x-tiddler-dictionary
color-scheme: light
palette-import: $:/palettes/Vanilla
primary:rgb(224, 32, 86);
menubar-foreground: #fff
menubar-background: <<colour primary>>

View File

@@ -0,0 +1,7 @@
title: $:/palettes/background/contrast-tests
type: application/x-tiddler-dictionary
tags: $:/tags/BackgroundPalette
?background-foreground-contrast: [tf.check-colour-contrast[background],[foreground],[45]]
?alert-contrast: [tf.check-colour-contrast[alert-background],[foreground],[45]]
?code-contrast: [tf.check-colour-contrast[code-background],[code-foreground],[45]]

View File

@@ -2,20 +2,4 @@ title: $:/core/ui/ControlPanel/Palette
tags: $:/tags/ControlPanel/Appearance
caption: {{$:/language/ControlPanel/Palette/Caption}}
\define lingo-base() $:/language/ControlPanel/Palette/
{{$:/snippets/paletteswitcher}}
<$reveal type="nomatch" state="$:/state/ShowPaletteEditor" text="yes">
<$button set="$:/state/ShowPaletteEditor" setTo="yes"><<lingo ShowEditor/Caption>></$button>
</$reveal>
<$reveal type="match" state="$:/state/ShowPaletteEditor" text="yes">
<$button set="$:/state/ShowPaletteEditor" setTo="no"><<lingo HideEditor/Caption>></$button>
{{$:/PaletteManager}}
</$reveal>
{{$:/PaletteManager}}

View File

@@ -46,7 +46,6 @@ tags: $:/tags/EditTemplate
<$list filter="[<currentTiddler>get<tagField>enlist-input[]sort[title]]" storyview="pop">
<$macrocall $name="tag-body"
colour={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}}
palette={{$:/palette}}
icon={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerIconFilter]!is[draft]get[text]] }}}
tagField=<<tagField>>
/>

View File

@@ -49,6 +49,27 @@ title: $:/core/ui/ImportListing
\end
\whitespace trim
<$let importJson={{{ [{$:/Import}] }}}>
<$let importTitles={{{ [<importJson>jsonindexes[tiddlers]] }}}>
<$let importTypes={{{ [(importTitles)] :map[<importJson>jsonget[tiddlers],<currentTiddler>,[type]] }}}>
<$let anyMatch={{{ [all[shadows+tiddlers]tag[$:/tags/ImportOptions]get[condition]] :map[(importTypes)subfilter<currentTiddler>] +[!is[blank]limit[1]] }}}>
<%if [<anyMatch>!is[blank]] %>
<div class="tc-import-option">
<$list filter="[all[shadows+tiddlers]tag[$:/tags/ImportOptions]]" variable="importOption">
<$let condition={{{ [<importOption>get[condition]] }}}>
<$let hasMatch={{{ [(importTypes)subfilter<condition>limit[1]] }}}>
<%if [<hasMatch>!is[blank]] %>
<$transclude tiddler=<<importOption>>/>
<%endif%>
</$let>
</$let>
</$list>
</div>
<%endif%>
</$let>
</$let>
</$let>
</$let>
<div class="tc-table-wrapper">
<table class="tc-import-table">
<tbody>

View File

@@ -1,7 +1,7 @@
title: $:/core/ui/PageTemplate/alerts
tags: $:/tags/PageTemplate
<div class="tc-alerts" role="region" aria-label="Alerts">
<div class="tc-alerts" role="region" aria-label={{$:/language/Alerts}}>
<$list filter="[all[shadows+tiddlers]tag[$:/tags/Alert]!is[draft]]" template="$:/core/ui/AlertTemplate" storyview="pop"/>

94
core/ui/PaletteEditor.tid Normal file
View File

@@ -0,0 +1,94 @@
title: $:/PaletteEditor
\define lingo-base() $:/language/ControlPanel/Palette/Editor/
\define describePaletteColour(colour)
<$transclude tiddler="$:/language/Docs/PaletteColours/$colour$"><$text text="$colour$"/></$transclude>
\end
\define edit-colour-placeholder()
edit $(colourName)$
\end
\define colour-tooltip(showhide) $showhide$ editor for $(newColourName)$
\define resolve-colour(macrocall)
\import $:/core/macros/utils
\whitespace trim
<$wikify name="name" text="""$macrocall$""">
<<name>>
</$wikify>
\end
\define delete-colour-index-actions() <$action-setfield $index=<<colourName>>/>
\define palette-manager-colour-row-segment()
\whitespace trim
<$edit-text index=<<colourName>> tag="input" placeholder=<<edit-colour-placeholder>> default=""/>
<br>
<$edit-text index=<<colourName>> type="color" tag="input" class="tc-palette-manager-colour-input"/>
<$list filter="[<currentTiddler>getindex<colourName>removeprefix[<<]removesuffix[>>]] [<currentTiddler>getindex<colourName>removeprefix[<$]removesuffix[/>]]" variable="ignore">
<$set name="state" value={{{ [[$:/state/palettemanager/]addsuffix<currentTiddler>addsuffix[/]addsuffix<colourName>] }}}>
<$wikify name="newColourName" text="""<$macrocall $name="resolve-colour" macrocall={{{ [<currentTiddler>getindex<colourName>] }}}/>""">
<$reveal state=<<state>> type="nomatch" text="show">
<$button tooltip=<<colour-tooltip show>> aria-label=<<colour-tooltip show>> class="tc-btn-invisible" set=<<state>> setTo="show">{{$:/core/images/down-arrow}}<$text text=<<newColourName>> class="tc-small-gap-left"/></$button><br>
</$reveal>
<$reveal state=<<state>> type="match" text="show">
<$button tooltip=<<colour-tooltip hide>> aria-label=<<colour-tooltip show>> class="tc-btn-invisible" actions="""<$action-deletetiddler $tiddler=<<state>>/>""">{{$:/core/images/up-arrow}}<$text text=<<newColourName>> class="tc-small-gap-left"/></$button><br>
</$reveal>
<$reveal state=<<state>> type="match" text="show">
<$set name="colourName" value=<<newColourName>>>
<br>
<<palette-manager-colour-row-segment>>
<br><br>
</$set>
</$reveal>
</$wikify>
</$set>
</$list>
\end
\define palette-manager-colour-row()
\whitespace trim
<tr>
<td>
<span style="float:right;">
<$button tooltip={{$:/language/ControlPanel/Palette/Editor/Delete/Hint}} aria-label={{$:/language/ControlPanel/Palette/Editor/Delete/Hint}} class="tc-btn-invisible" actions=<<delete-colour-index-actions>>>
{{$:/core/images/delete-button}}</$button>
</span>
''<$macrocall $name="describePaletteColour" colour=<<colourName>>/>''<br/>
<$macrocall $name="colourName" $output="text/plain"/>
</td>
<td>
<<palette-manager-colour-row-segment>>
</td>
</tr>
\end
\define palette-manager-table()
\whitespace trim
<table>
<tbody>
<$set name="colorList" filter="[{$:/state/palettemanager/showexternal}match[yes]]"
value="[all[shadows+tiddlers]tag[$:/tags/Palette]indexes[]]" emptyValue="[<currentTiddler>indexes[]]">
<$list filter=<<colorList>> variable="colourName"> <<palette-manager-colour-row>> </$list>
</$set>
</tbody>
</table>
\end
\whitespace trim
<$set name="currentTiddler" value={{$:/palette}}>
<<lingo Prompt>>&#32;<$link to={{$:/palette}}><$macrocall $name="currentTiddler" $output="text/plain"/></$link>
<$list filter="[all[current]is[shadow]is[tiddler]]" variable="listItem">
<<lingo Prompt/Modified>>
&#32;
<$button message="tm-delete-tiddler" param={{$:/palette}}><<lingo Reset/Caption>></$button>
</$list>
<$list filter="[all[current]is[shadow]!is[tiddler]]" variable="listItem">
<<lingo Clone/Prompt>>
</$list>
<$button message="tm-new-tiddler" param={{$:/palette}}><<lingo Clone/Caption>></$button>
<$checkbox tiddler="$:/state/palettemanager/showexternal" field="text" checked="yes" unchecked="no"><span class="tc-small-gap-left"><<lingo Names/External/Show>></span></$checkbox>
<<palette-manager-table>>

View File

@@ -1,94 +1,43 @@
title: $:/PaletteManager
\define lingo-base() $:/language/ControlPanel/Palette/Editor/
\define describePaletteColour(colour)
<$transclude tiddler="$:/language/Docs/PaletteColours/$colour$"><$text text="$colour$"/></$transclude>
\end
\define edit-colour-placeholder()
edit $(colourName)$
\end
\define colour-tooltip(showhide) $showhide$ editor for $(newColourName)$
\define lingo-base() $:/language/ControlPanel/Palette/
\define resolve-colour(macrocall)
\import $:/core/macros/utils
\whitespace trim
<$wikify name="name" text="""$macrocall$""">
<<name>>
</$wikify>
\end
<!-- Used by the language string CustomSettings/Prompt -->
\procedure palette-link()
<$tiddler tiddler={{$:/palette}}>
<$link to={{!!title}}>
<$view field="name" format="text">
<$view field="title" format="text"/>
</$view>
</$link>
</$tiddler>
\end palette-link
<$transclude $tiddler="$:/snippets/paletteswitcher" thumbnails="yes"/>
{{$:/snippets/palettetests}}
<$let
paletteSettings={{{ [[$:/temp/palette-consolidated]get[settings]] }}}
>
<%if [<paletteSettings>!match[]] %>
<div>
<<lingo CustomSettings/Prompt>>
<$transclude $tiddler=<<paletteSettings>> $mode="block"/>
</div>
<%endif%>
</$let>
<$reveal type="nomatch" state="$:/state/ShowPaletteEditor" text="yes">
<$button set="$:/state/ShowPaletteEditor" setTo="yes"><<lingo ShowEditor/Caption>></$button>
\define delete-colour-index-actions() <$action-setfield $index=<<colourName>>/>
\define palette-manager-colour-row-segment()
\whitespace trim
<$edit-text index=<<colourName>> tag="input" placeholder=<<edit-colour-placeholder>> default=""/>
<br>
<$edit-text index=<<colourName>> type="color" tag="input" class="tc-palette-manager-colour-input"/>
<$list filter="[<currentTiddler>getindex<colourName>removeprefix[<<]removesuffix[>>]] [<currentTiddler>getindex<colourName>removeprefix[<$]removesuffix[/>]]" variable="ignore">
<$set name="state" value={{{ [[$:/state/palettemanager/]addsuffix<currentTiddler>addsuffix[/]addsuffix<colourName>] }}}>
<$wikify name="newColourName" text="""<$macrocall $name="resolve-colour" macrocall={{{ [<currentTiddler>getindex<colourName>] }}}/>""">
<$reveal state=<<state>> type="nomatch" text="show">
<$button tooltip=<<colour-tooltip show>> aria-label=<<colour-tooltip show>> class="tc-btn-invisible" set=<<state>> setTo="show">{{$:/core/images/down-arrow}}<$text text=<<newColourName>> class="tc-small-gap-left"/></$button><br>
</$reveal>
<$reveal state=<<state>> type="match" text="show">
<$button tooltip=<<colour-tooltip hide>> aria-label=<<colour-tooltip show>> class="tc-btn-invisible" actions="""<$action-deletetiddler $tiddler=<<state>>/>""">{{$:/core/images/up-arrow}}<$text text=<<newColourName>> class="tc-small-gap-left"/></$button><br>
<$reveal type="match" state="$:/state/ShowPaletteEditor" text="yes">
<$button set="$:/state/ShowPaletteEditor" setTo="no"><<lingo HideEditor/Caption>></$button>
{{$:/PaletteEditor}}
</$reveal>
<$reveal state=<<state>> type="match" text="show">
<$set name="colourName" value=<<newColourName>>>
<br>
<<palette-manager-colour-row-segment>>
<br><br>
</$set>
</$reveal>
</$wikify>
</$set>
</$list>
\end
\define palette-manager-colour-row()
\whitespace trim
<tr>
<td>
<span style="float:right;">
<$button tooltip={{$:/language/ControlPanel/Palette/Editor/Delete/Hint}} aria-label={{$:/language/ControlPanel/Palette/Editor/Delete/Hint}} class="tc-btn-invisible" actions=<<delete-colour-index-actions>>>
{{$:/core/images/delete-button}}</$button>
</span>
''<$macrocall $name="describePaletteColour" colour=<<colourName>>/>''<br/>
<$macrocall $name="colourName" $output="text/plain"/>
</td>
<td>
<<palette-manager-colour-row-segment>>
</td>
</tr>
\end
\define palette-manager-table()
\whitespace trim
<table>
<tbody>
<$set name="colorList" filter="[{$:/state/palettemanager/showexternal}match[yes]]"
value="[all[shadows+tiddlers]tag[$:/tags/Palette]indexes[]]" emptyValue="[<currentTiddler>indexes[]]">
<$list filter=<<colorList>> variable="colourName"> <<palette-manager-colour-row>> </$list>
</$set>
</tbody>
</table>
\end
\whitespace trim
<$set name="currentTiddler" value={{$:/palette}}>
<<lingo Prompt>>&#32;<$link to={{$:/palette}}><$macrocall $name="currentTiddler" $output="text/plain"/></$link>
<$list filter="[all[current]is[shadow]is[tiddler]]" variable="listItem">
<<lingo Prompt/Modified>>
&#32;
<$button message="tm-delete-tiddler" param={{$:/palette}}><<lingo Reset/Caption>></$button>
</$list>
<$list filter="[all[current]is[shadow]!is[tiddler]]" variable="listItem">
<<lingo Clone/Prompt>>
</$list>
<$button message="tm-new-tiddler" param={{$:/palette}}><<lingo Clone/Caption>></$button>
<$checkbox tiddler="$:/state/palettemanager/showexternal" field="text" checked="yes" unchecked="no"><span class="tc-small-gap-left"><<lingo Names/External/Show>></span></$checkbox>
<<palette-manager-table>>

View File

@@ -0,0 +1,19 @@
title: $:/core/ui/Palettes/Preview/Alert
tags: $:/tags/Preview/Page
\whitespace trim
<div class="tc-palette-preview-thumbnail-alert" style.background-color=<<colour alert-background>>>
<div class="tc-palette-preview-thumbnail-alert-border" style.border-color=<<colour alert-border>>>
<div style.color=<<colour foreground>>>
<div class="tc-palette-preview-thumbnail-alert-subtitle" style.color=<<colour alert-muted-foreground>>>
Lorem Ipsum
<div class="tc-palette-preview-thumbnail-alert-highlight" style.color=<<colour alert-highlight>>>
(Count: 1)
</div>
</div>
<div class="tc-palette-preview-thumbnail-alert-body">
Lorem Ipsum Dolor Sit Amet Consectetur Adipiscing Elit Sed Do Eiusmod Tempor Incididunt.
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,55 @@
title: $:/core/ui/Palettes/Preview/Helpers
tags: $:/tags/Preview/Helpers
\whitespace trim
\procedure palette-preview-component-list(tag)
<$list filter="[all[shadows+tiddlers]tag<tag>!has[draft.of]]" variable="componentTitle">
<$transclude $tiddler=<<componentTitle>> title=<<title>>/>
</$list>
\end palette-preview-component-list
\procedure tab-set(tabTitles,colourPrefix:"")
<div class="tc-palette-preview-thumbnail-tab-set">
<div class="tc-palette-preview-thumbnail-tab-buttons">
<$list filter="[enlist<tabTitles>]" variable="tabTitle" counter="tabIndex">
<%if [<tabIndex>match[1]] %>
<span
class="tc-palette-preview-thumbnail-tab-button"
style.border-color={{{ [<colourPrefix>addsuffix[tab-border-selected]] :map[function[colour],<currentTiddler>] }}}
style.color={{{ [<colourPrefix>addsuffix[tab-foreground-selected]] :map[function[colour],<currentTiddler>] }}}
style.background-color={{{ [<colourPrefix>addsuffix[tab-background-selected]] :map[function[colour],<currentTiddler>] }}}
>
<$text text=<<tabTitle>>/>
</span>
<%else%>
<span
class="tc-palette-preview-thumbnail-tab-button"
style.border-color={{{ [<colourPrefix>addsuffix[tab-border]] :map[function[colour],<currentTiddler>] }}}
style.color={{{ [<colourPrefix>addsuffix[tab-foreground]] :map[function[colour],<currentTiddler>] }}}
style.background-color={{{ [<colourPrefix>addsuffix[tab-background]] :map[function[colour],<currentTiddler>] }}}
>
<$text text=<<tabTitle>>/>
</span>
<%endif%>
</$list>
</div>
<div
class="tc-palette-preview-thumbnail-tab-divider"
style.border-color={{{ [<colourPrefix>addsuffix[tab-divider]] :map[function[colour],<currentTiddler>] }}}
>
</div>
</div>
\end tab-set
\procedure link(text)
<span class="tc-palette-preview-thumbnail-tiddler-link" style.color=<<colour primary>>>
<$text text=<<text>>/>
</span>
\end link
\procedure sidebar-link(text)
<span class="tc-palette-preview-thumbnail-tiddler-link" style.color=<<colour sidebar-tiddler-link-foreground>>>
<$text text=<<text>>/>
</span>
\end sidebar-link

View File

@@ -0,0 +1,11 @@
title: $:/core/ui/Palettes/Preview/Notification
tags: $:/tags/Preview/PageOptional
\whitespace trim
<div class="tc-palette-preview-thumbnail-notification" style.background-color=<<colour notification-background>>>
<div class="tc-palette-preview-thumbnail-notification-border" style.border-color=<<colour notification-border>>>
<div class="tc-palette-preview-thumbnail-notification-body" style.color=<<colour foreground>>>
Lorem Ipsum Dolor Sit Amet Consectetur
</div>
</div>
</div>

View File

@@ -0,0 +1,8 @@
title: $:/core/ui/Palettes/Preview/Sidebar/Search
tags: $:/tags/Preview/SideBar
\whitespace trim
<div class="tc-palette-preview-thumbnail-sidebar-search" style.background-color=<<colour background>>>
<div class="tc-palette-preview-thumbnail-sidebar-search-box">
</div>
</div>

View File

@@ -0,0 +1,7 @@
title: $:/core/ui/Palettes/Preview/Sidebar/Subtitle
tags: $:/tags/Preview/SideBar
\whitespace trim
<div class="tc-palette-preview-thumbnail-sidebar-subtitle">
a non-linear personal web notebook
</div>

View File

@@ -0,0 +1,18 @@
title: $:/core/ui/Palettes/Preview/Sidebar/Tabs
tags: $:/tags/Preview/SideBar
\whitespace trim
\procedure recent-links()
HelloThere Community Portal GettingStarted Development Download Filters Palettes Plugins Macros Templates Themes Stylesheets SystemTiddlers
\end recent-links
<<tab-set "Magna Placerat Ligula Imperdiet" "sidebar-">>
<div class="tc-palette-preview-thumbnail-sidebar-list">
<$list filter="[enlist<recent-links>]">
<div>
<$transclude $variable="sidebar-link" text=<<currentTiddler>>/>
</div>
</$list>
</div>

View File

@@ -0,0 +1,7 @@
title: $:/core/ui/Palettes/Preview/Sidebar/Title
tags: $:/tags/Preview/SideBar
\whitespace trim
<div class="tc-palette-preview-thumbnail-sidebar-title" style.color=<<colour site-title-foreground>>>
~TiddlyWiki
</div>

View File

@@ -0,0 +1,7 @@
title: $:/core/ui/Palettes/Preview/SideBar
tags: $:/tags/Preview/Page
\whitespace trim
<div class="tc-palette-preview-thumbnail-sidebar" style.color=<<colour sidebar-foreground>>>
<<palette-preview-component-list "$:/tags/Preview/SideBar">>
</div>

View File

@@ -0,0 +1,9 @@
title: $:/core/ui/Palettes/Preview/Story
tags: $:/tags/Preview/Page
\whitespace trim
<div class="tc-palette-preview-thumbnail-story">
<$list filter="HelloThere GettingStarted" variable="title">
<<palette-preview-component-list "$:/tags/Preview/Story">>
</$list>
</div>

View File

@@ -0,0 +1,10 @@
title: $:/core/ui/Palettes/Preview/Tiddler
tags: $:/tags/Preview/Story
\parameters (title)
\whitespace trim
<div class="tc-palette-preview-thumbnail-tiddler-border" style.border-color=<<colour tiddler-border>>>
<div class="tc-palette-preview-thumbnail-tiddler" style.background-color=<<colour tiddler-background>>>
<<palette-preview-component-list "$:/tags/Preview/Tiddler">>
</div>
</div>

View File

@@ -0,0 +1,15 @@
title: $:/core/ui/Palettes/Preview/Tiddler/Body
tags: $:/tags/Preview/Tiddler
\whitespace trim
<div class="tc-palette-preview-thumbnail-tiddler-body" style.color=<<colour foreground>>>
<%if [<title>match[HelloThere]] %>
Lorem ipsum dolor sit amet, <<link "consectetur adipiscing elit">>. Cras non arcu ultricies, egestas odio tempus, vestibulum ipsum. Praesent diam lorem, elementum in venenatis eget, tincidunt quis lacus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Etiam efficitur velit tortor, sit amet tristique felis viverra sit amet. <<link "Nullam posuere facilisis purus sed">> consectetur. Integer vel elit euismod, posuere ligula et, dictum tellus. Donec in odio diam. Sed metus magna, placerat at ligula et, imperdiet sagittis ex.
<%else%>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<<tab-set "Sed Metus Magna Placerat Ligula Imperdiet">>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras non arcu ultricies, egestas odio tempus, vestibulum ipsum. Praesent diam lorem, elementum in venenatis eget, tincidunt quis lacus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
<%endif%>
</div>

View File

@@ -0,0 +1,14 @@
title: $:/core/ui/Palettes/Preview/Tiddler/Header
tags: $:/tags/Preview/Tiddler
\whitespace trim
<div class="tc-palette-preview-thumbnail-tiddler-header">
<div class="tc-palette-preview-thumbnail-tiddler-title" style.color=<<colour tiddler-title-foreground>>>
<$text text=<<title>>/>
</div>
<div class="tc-palette-preview-thumbnail-tiddler-toolbar" style.fill=<<colour tiddler-controls-foreground>>>
{{$:/core/images/down-arrow}}
{{$:/core/images/edit-button}}
{{$:/core/images/close-button}}
</div>
</div>

View File

@@ -0,0 +1,7 @@
title: $:/core/ui/Palettes/Preview/Tiddler/Subtitle
tags: $:/tags/Preview/Tiddler
\whitespace trim
<div class="tc-palette-preview-thumbnail-tiddler-subtitle" style.color=<<colour tiddler-subtitle-foreground>>>
Motovun Jack
</div>

View File

@@ -0,0 +1,11 @@
title: $:/core/ui/Palettes/ViewTemplateBody
<div style.width="220px">
{{||$:/snippets/currpalettepreview}}
</div>
''<$view field="name" format="text"/>''
<br>
<$view field="description" format="text"/>

View File

@@ -15,7 +15,7 @@ caption: {{$:/language/SideBar/Open/Caption}}
\define droppable-item(button)
\whitespace trim
<$droppable actions=<<drop-actions>> enable=<<tv-allow-drag-and-drop>> tag="div">
<$droppable actions=<<drop-actions>> enable=<<tv-enable-drag-and-drop>> tag="div">
<<placeholder>>
<div>
$button$

View File

@@ -7,7 +7,6 @@ title: $:/core/ui/TagTemplate
tag=<<currentTiddler>>
icon={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerIconFilter]!is[draft]get[text]] }}}
colour={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}}
palette={{$:/palette}}
element-tag="$button"
element-attributes="""popup=<<qualify "$:/state/popup/tag">> dragFilter="[subfilter{$:/core/config/TagPillDragFilter}]" tag='span'"""
/>

View File

@@ -2,7 +2,7 @@ title: $:/core/ui/TiddlerInfo
\whitespace trim
<div style="position:relative;">
<div class="tc-tiddler-controls" style="position:absolute;right:0;">
<div class="tc-tiddler-controls tc-tiddler-info-controls">
<$reveal state="$:/config/TiddlerInfo/Mode" type="match" text="sticky">
<$button set=<<tiddlerInfoState>> setTo="" tooltip={{$:/language/Buttons/Info/Hint}} aria-label={{$:/language/Buttons/Info/Caption}} class="tc-btn-invisible">
{{$:/core/images/close-button}}

View File

@@ -11,9 +11,13 @@ tags: $:/tags/ViewTemplate
storyview="pop"
variable="listItem"
>
<$set name="tv-config-toolbar-class" filter="[<tv-config-toolbar-class>] [<listItem>encodeuricomponent[]addprefix[tc-btn-]]">
<$transclude tiddler=<<listItem>>/>
</$set>
<$let condition={{{ [<listItem>get[condition]] }}}>
<%if [<condition>!is[blank]] :and[<currentTiddler>subfilter<condition>limit[1]] :else[<condition>is[blank]then[true]] %>
<$set name="tv-config-toolbar-class" filter="[<tv-config-toolbar-class>] [<listItem>encodeuricomponent[]addprefix[tc-btn-]]">
<$transclude tiddler=<<listItem>>/>
</$set>
<%endif%>
</$let>
</$list>
</span>
<$set name="tv-wikilinks" value={{$:/config/Tiddlers/TitleLinks}}>

View File

@@ -41,6 +41,8 @@ Drag this link to copy this tool to another wiki
</$wikify>
\end capture-item-wikified
\function get.shadow.source() [shadowsource[]]
\procedure capture-wiki-info(tempWikiInfo)
<$transclude $variable="capture-item-wikified" label="TiddlyWiki Version" value="<<version>>"/>
<$transclude $variable="capture-item" label="Current palette" value={{$:/palette}}/>
@@ -64,6 +66,7 @@ Drag this link to copy this tool to another wiki
<$transclude $variable="capture-item" label="Keyboard shortcuts that have been customised" value={{{ [all[tiddlers]prefix[$:/config/shortcuts]] +[join[,]] }}}/>
<$transclude $variable="capture-item" label="Disabled plugins" value={{{ [all[tiddlers]prefix[$:/config/Plugins/Disabled/]] :filter[{!!text}match[yes]] :map[<currentTiddler>removeprefix[$:/config/Plugins/Disabled/]] +[join[,]] }}}/>
<$transclude $variable="capture-item" label="Plugins" value={{{ [has[plugin-type]sort[]] :filter[<currentTiddler>addprefix[$:/config/Plugins/Disabled/]get[text]else[no]!match[yes]] :map[{!!version}addprefix[ - ]addprefix<currentTiddler>] +[addprefix[ ]addprefix<crlf>join[]] }}}/>
<$transclude $variable="capture-item" label="Stylesheets" value={{{ [all[shadows+tiddlers]tag[$:/tags/Stylesheet]!is[draft]] :map[is[shadow]addsuffix[ ∈ ]addsuffix<get.shadow.source>else<currentTiddler>] +[addprefix[ ]addprefix<crlf>join[]] }}}/>
\end capture-wiki-info
\procedure template-header()

View File

@@ -0,0 +1,7 @@
title: $:/core/background-actions/AutoCompilePalette
tags: $:/tags/BackgroundAction $:/tags/StartupAction
platforms: browser
track-filter: [{$:/palette}get[color-scheme]] :map[subfilter<currentTiddler>] [{$:/palette}changecount[]addprefix{$:/palette}] [[$:/palette]changecount[]]
\import [subfilter{$:/core/config/GlobalImportFilter}]
<<actions-recompile-current-palette>>

View File

@@ -0,0 +1,5 @@
title: $:/core/wiki/config/MediaQueryTrackers/DarkLightPreferred
tags: $:/tags/MediaQueryTracker
media-query: (prefers-color-scheme: dark)
info-tiddler: $:/info/browser/darkmode
info-tiddler-alt: $:/info/darkmode

View File

@@ -10,4 +10,5 @@ code-body: [field:code-body[yes]then[$:/core/ui/ViewTemplate/body/code]]
import: [field:plugin-type[import]then[$:/core/ui/ViewTemplate/body/import]]
plugin: [has[plugin-type]then[$:/core/ui/ViewTemplate/body/plugin]]
hide-body: [field:hide-body[yes]then[$:/core/ui/ViewTemplate/body/blank]]
palette: [tag[$:/tags/Palette]then[$:/core/ui/Palettes/ViewTemplateBody]]
default: [[$:/core/ui/ViewTemplate/body/default]]

View File

@@ -1,52 +1,12 @@
title: $:/snippets/currpalettepreview
\define resolve-colour(macrocall)
\import $:/core/macros/utils
\whitespace trim
<$wikify name="name" text="""$macrocall$""">
<<name>>
</$wikify>
\end
\define swatchStyle()
background-color: $(swatchColour)$;
\end
\define swatch-inner()
\whitespace trim
<$set name="swatchColour" value={{##$(colourResolved)$}}>
<$list filter="[<swatchColour>!prefix[<<colour ]!suffix[>>]]" variable="ignore">
<div class="tc-swatch" style=<<swatchStyle>> title=<<swatchTitle>>/>
&#32;
</$list>
<$list filter="[<swatchColour>prefix[<<colour ]suffix[>>]]" variable="ignore">
<$wikify name="colourResolved" text="<$macrocall $name='resolve-colour' macrocall=<<swatchColour>>/>">
<<swatch-inner>>
</$wikify>
</$list>
</$set>
\end
\define swatch()
\whitespace trim
<$set name="swatchColour" value={{##$(colour)$}}>
<$set name="swatchTitle" value=<<colour>>>
<$list filter="[<swatchColour>!prefix[<<colour ]!suffix[>>]]" variable="ignore">
<div class="tc-swatch" style=<<swatchStyle>> title=<<swatchTitle>>/>
&#32;
</$list>
<$list filter="[<swatchColour>prefix[<<colour ]suffix[>>]]" variable="ignore">
<$wikify name="colourResolved" text="<$macrocall $name='resolve-colour' macrocall=<<swatchColour>>/>">
<<swatch-inner>>
</$wikify>
</$list>
</$set>
</$set>
\end
\whitespace trim
<div class="tc-swatches-horiz"><$list filter="
foreground
background
muted-foreground
primary
page-background
tab-background
tiddler-info-background
" variable="colour"><<swatch>></$list></div>
\import [all[shadows+tiddlers]tag[$:/tags/Preview/Helpers]!is[draft]sort[title]]
<!-- currentTiddler is the palette to use -->
<$palette.preview paletteTitle=<<currentTiddler>>>
<div class="tc-palette-preview-thumbnail" style.background-color=<<colour page-background>>>
<<palette-preview-component-list tag:"$:/tags/Preview/Page">>
</div>
</$palette.preview>

View File

@@ -0,0 +1,39 @@
title: $:/core/macros/CSS/property
<!-- CSS property macros -->
<!-- TODO: Deprecate the following CSS macros once 2020 baseline is supported -->
\procedure margin-start(size)
-webkit-margin-start: <<size>>;
margin-inline-start: <<size>>;
\end
\procedure margin-end(size)
-webkit-margin-end: <<size>>;
margin-inline-end: <<size>>;
\end
\procedure padding-start(size)
-webkit-padding-start: <<size>>;
padding-inline-start: <<size>>;
\end
\procedure padding-end(size)
-webkit-padding-end: <<size>>;
padding-inline-end: <<size>>;
\end
\procedure margin-inline(start,end)
-webkit-margin-start: <<start>>;
margin-inline-start: <<start>>;
-webkit-margin-end: <<end>>;
margin-inline-end: <<end>>;
\end
\procedure padding-inline(start,end)
-webkit-padding-start: <<start>>;
padding-inline-start: <<start>>;
-webkit-padding-end: <<end>>;
padding-inline-end: <<end>>;
\end

View File

@@ -1,17 +1,157 @@
title: $:/core/macros/CSS
tags: $:/tags/Macro
<!-- Needs to stay that way for backwards compatibility. See GH issue: #8326 -->
\define colour(name)
\whitespace trim
<$transclude tiddler={{$:/palette}} index="$name$">
<$transclude tiddler="$:/palettes/Vanilla" index="$name$">
<$transclude tiddler="$:/config/DefaultColourMappings/$name$"/>
</$transclude>
</$transclude>
\end
\procedure actions-compile-palette-filtered(consolidatedPalette,outputPalette)
<!-- Note the join, needed to cope with palette entries containing spaces -->
\function tf.colour(name) [<consolidatedPalette>getindex<name>] :else[[$:/config/DefaultColourMappings/]addsuffix<name>get[text]] :map[tf.colour-inner-transform-classic-palette-entry<currentTiddler>] :map[subfilter:all<currentTiddler>join[ ]]
\function colour(name) [tf.colour<name>]
\function color(name) [tf.colour<name>]
<!-- Make the colour function use the input palette -->
<$list filter="[<consolidatedPalette>indexes[]sort[]]" variable="colour-name">
<$let filter-text={{{ [<consolidatedPalette>getindex<colour-name>] :else[[$:/config/DefaultColourMappings/]addsuffix<colour-name>get[text]] :map[tf.colour-inner-transform-classic-palette-entry<currentTiddler>] }}}>
<!-- Note the join, needed to cope with palette entries containing spaces -->
<$action-setfield $tiddler=<<outputPalette>> $index=<<colour-name>> $value={{{ [subfilter:all<filter-text>join[ ]] +[join[ ]] }}}/>
</$let>
</$list>
\end actions-compile-palette-filtered
\define color(name) <<colour $name$>>
\procedure actions-compile-palette-import(inputPalette,outputPalette,exclusions:"",scheme)
<%if [enlist<exclusions>!match<inputPalette>count[]] :map[enlist<exclusions>count[]compare:number:eq<currentTiddler>] +[!match[]] %>
<$set name="exclusions" filter="[enlist<exclusions>] [<inputPalette>]">
<!-- Recursively import any imported palette -->
<$let
prefixed-palette-import={{{ [[palette-import@]addsuffix<scheme>] }}}
inputPalette={{{ [<inputPalette>get<prefixed-palette-import>has[title]] :else[<inputPalette>get[palette-import]] }}}
>
<%if [<inputPalette>has[title]] %>
<$transclude $variable="actions-compile-palette-import" inputPalette=<<inputPalette>> outputPalette=<<outputPalette>> exclusions=<<exclusions>> scheme=<<scheme>>/>
<%endif%>
</$let>
<!-- Copy the suffixed palette entries with the suffix stripped -->
<%if [<scheme>!is[blank]] %>
<$let
prefixed-scheme={{{ [<scheme>addprefix[@]] }}}
>
<$action-setmultiplefields $tiddler=<<outputPalette>> $indexes="[<inputPalette>indexes[]suffix<prefixed-scheme>removesuffix<prefixed-scheme>sort[]]" $values="[<inputPalette>indexes[]suffix<prefixed-scheme>sort[]] :map[<inputPalette>getindex<currentTiddler>]"/>
</$let>
<%endif%
<!-- Copy the unsuffixed palette entries -->
<$action-setmultiplefields $tiddler=<<outputPalette>> $indexes="[<inputPalette>indexes[]!regexp[@]sort[]]" $values="[<inputPalette>indexes[]!regexp[@]sort[]] :map[<inputPalette>getindex<currentTiddler>]"/>
<!-- Copy the fields from the palette -->
<$action-setmultiplefields $tiddler=<<outputPalette>> $fields="[<inputPalette>fields[]sort[]] -title -tags -text" $values="[<inputPalette>fields[]sort[]] -title -tags -text :map[<inputPalette>get<currentTiddler>]"/>
</$set>
<%endif%>
\end actions-compile-palette-import
\procedure actions-compile-palette(inputPalette,outputPalette)
\procedure tv-action-refresh-policy() always
<$let
consolidatedPalette="$:/temp/palette-consolidated"
>
<!-- Compute the current scheme -->
<$let
color-scheme-filter={{{ [<inputPalette>get[color-scheme]] :else[[light]] }}}
scheme={{{ [subfilter:all<color-scheme-filter>] }}}
>
<!-- Clear the consolidated palette that stores the result of flattening the chain of imported input palettes -->
<$action-deletetiddler $tiddler=<<consolidatedPalette>>/>
<$action-setfield $tiddler=<<consolidatedPalette>> type="application/x-tiddler-dictionary"/>
<!-- Clear the output palette that stores the plain CSS values of palette entries -->
<$action-deletetiddler $tiddler=<<outputPalette>>/>
<$action-setfield $tiddler=<<outputPalette>> type="application/x-tiddler-dictionary"/>
<!-- Import the background palettes -->
<$list filter="[all[shadows+tiddlers]tag[$:/tags/BackgroundPalette]sort[]]" variable="palette-name">
<$transclude $variable="actions-compile-palette-import" inputPalette=<<palette-name>> outputPalette=<<consolidatedPalette>> scheme=<<scheme>>/>
</$list>
<!-- Consolidate the chain of palettes -->
<$transclude $variable="actions-compile-palette-import" inputPalette=<<inputPalette>> outputPalette=<<consolidatedPalette>> scheme=<<scheme>>/>
<!-- Save the current scheme in the output tiddler -->
<$action-setfield $tiddler=<<outputPalette>> $field="color-scheme" $value=<<scheme>>/>
<!-- Compile the temporary palette to the output palette -->
<$transclude $variable="actions-compile-palette-filtered" consolidatedPalette=<<consolidatedPalette>> outputPalette=<<outputPalette>>/>
</$let>
</$let>
\end actions-compile-palette
\procedure actions-recompile-current-palette()
\procedure tv-action-refresh-policy() always
<$transclude $variable="actions-compile-palette" inputPalette={{$:/palette}} outputPalette="$:/temp/palette-colours"/>
\end actions-recompile-current-palette
\procedure actions-switch-colour-palette(paletteTitle)
\procedure tv-action-refresh-policy() always
<$action-deletetiddler $tiddler="$:/temp/palette-colours"/>
<<actions-recompile-current-palette>>
\end actions-switch-colour-palette
\procedure tv-palette-name() $:/temp/palette-colours
\function tf.colour(name)
[<tv-palette-name>getindex<name>] :else[[$:/config/DefaultColourMappings/]addsuffix<name>get[text]]
\end tf.colour
\function colour(name)
[tf.colour<name>]
\end colour
\function color(name)
[tf.colour<name>]
\end color
\procedure colour-function-prefix()
[tf.colour[
\end colour-function-prefix
\procedure colour-function-suffix()
]]
\end colour-function-suffix
\widget $palette.preview(paletteTitle)
\whitespace trim
\function colour-inner-get-imported-palette(name,paletteTitle)
[[palette-import@]addsuffix<scheme>] :map[<paletteTitle>get<currentTiddler>has[title]] +[!match[]] :else[<paletteTitle>get[palette-import]has[title]] :map[function[colour-inner-get-palette-entry],<name>,<currentTiddler>] +[!match[]]
\end colour-inner-get-imported-palette
\function colour-inner-get-palette-entry(name,paletteTitle)
[<name>addprefix[@]addprefix<scheme>] :map[<paletteTitle>getindex<currentTiddler>] +[!match[]] :else[<paletteTitle>getindex<name>] :map[tf.colour-inner-transform-classic-palette-entry<currentTiddler>] :else[function[colour-inner-get-imported-palette],<name>,<paletteTitle>]
\end colour-inner-get-palette-entry
<!-- Note the join, needed to cope with palette entries containing spaces -->
\function tf.colour(name)
[function[colour-inner-get-palette-entry],<name>,<paletteTitle>] :else[[$:/config/DefaultColourMappings/]addsuffix<name>get[text]] :map[subfilter:all<currentTiddler>join[ ]]
\end tf.colour
\function colour(name)
[tf.colour<name>]
\end colour
\function color(name)
[tf.colour<name>]
\end color
<$let
color-scheme-filter={{{ [<paletteTitle>get[color-scheme]] :else[[light]] }}}
scheme={{{ [subfilter:all<color-scheme-filter>] }}}
>
<$slot $name="ts-raw"/>
</$let>
\end $palette.preview
\function tf.colour-inner-transform-classic-palette-entry(colour-result)
[<colour-result>prefix[<<colour ]suffix[>>]removeprefix[<<colour ]removesuffix[>>]addprefix<colour-function-prefix>addsuffix<colour-function-suffix>] :else[<colour-result>]
\end tf.colour-inner-transform-classic-palette-entry
\function tf.check-colour-contrast-subfunction()
[function[colour],<paletteEntryA>] [function[colour],<paletteEntryB>] +[colour-contrast:DeltaPhi[]fixed[3]]
\end tf.check-colour-contrast-subfunction
\function tf.check-colour-contrast(paletteEntryA,paletteEntryB,threshold)
[function[tf.check-colour-contrast-subfunction]compare:number:gt<threshold>then[ok]] :else[function[tf.check-colour-contrast-subfunction]addsuffix[: ]addsuffix<paletteEntryA>addsuffix[/]addsuffix<paletteEntryB>addsuffix[ contrast is too low]]
\end tf.check-colour-contrast
\function tf.interpolate-colours(paletteEntryA,paletteEntryB,weight)
[function[colour],<paletteEntryA>] =>colourA [function[colour],<paletteEntryB>] =>colourB [<weight>colour-interpolate:oklch<colourA>,<colourB>]
\end tf.interpolate-colours
\procedure datauri(title)
<$macrocall $name="makedatauri" type={{{ [<title>get[type]] }}} text={{{ [<title>get[text]] }}} _canonical_uri={{{ [<title>get[_canonical_uri]] }}}/>

View File

@@ -6,5 +6,5 @@ $:/language/
\end
\define lingo(title)
{{$(lingo-base)$$title$}}
{{||$(lingo-base)$$title$}}
\end

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