Compare commits

..
Author SHA1 Message Date
Jeremy Ruston 4d0d28ab96 Merge branch 'master' into colour-improvements 2026-09-10 17:48:38 +01:00
Jeremy Ruston 2b60da0dcf Updated Catalan translation 2026-09-10 14:00:07 +01:00
Jeremy Ruston 5d3f2685a3 Merge branch 'tiddlywiki-com' 2026-09-08 11:40:21 +01:00
Joshua FontanyandGitHub 685a80dfc6 Document that an unquoted parameter value cannot contain a colon (#9999)
Calls stated that a value needs no delimiters if it contains no spaces or
quotes. A colon also requires them: name:value is call syntax, so the text
before the colon is read as a parameter name and the positional parameter
receives nothing, silently.

Call Syntax already has this right - param-name ":" value is one of its
productions - so this brings the prose into line with the grammar, and adds
a warning naming the two ways round it.
2026-09-08 09:39:22 +01:00
Théophile DesmedtandGitHub 3aec096a07 Add $:/core/templates/plain-text and $:/core/templates/rendered-text (#10017)
* Add $:/core/templates/plain-text core template

This template renders a single `text` variable as plain text using the
<$text> widget. It enables exporting and downloading dynamic strings
via messages like `tm-download-file` using the `variables` paramObject,
eliminating the need to create temporary tiddlers in the wiki store
purely for rendering purposes.

* add release note

* add rendered text template

* update change note
2026-09-08 09:38:27 +01:00
Théophile DesmedtandGitHub ddc59ee77b add examples using the new templates $:/core/templates/plain-text and $:/core/templates/rendered-text (#10019) 2026-09-08 09:38:05 +01:00
Zaid AlmomaniandGitHub e0e6ab69b1 Fix LinkWidget href fallback when tv-filter-export-link returns empty (#10009) 2026-09-08 09:31:24 +01:00
Zaid AlmomaniandGitHub e4ce6b3433 Sign CLA: Zaid (#10010) 2026-09-08 09:30:56 +01:00
Théophile DesmedtandGitHub 47806feb85 Fix plugin library iframes hanging if onload never fires by adding readiness handshake (#10003)
* Fix race condition in plugin library iframe message handling

Introduce a PLUGIN-LIBRARY-READY handshake verb for plugin library iframes. 
This ensures that postMessage commands are not prematurely dropped before 
the iframe's internal script has fully bound its message listeners. 

Retains iframe.onload as a backward-compatible fallback for legacy or 
external libraries that do not emit the ready signal.

* add changenote

* feat(plugin-library): add PLUGIN-LIBRARY-READY handshake to plugin library iframe

    Add PLUGIN-LIBRARY-READY message emission in libraryserver.js

    Signals to parent window when iframe is ready to receive messages

    Complements browser-messaging.js PLUGIN-LIBRARY-READY handler

    Includes comprehensive tests for callback queueing and handshake flow

    Fixes race condition in plugin library iframe message handling

* fix(plugin-library): emit PLUGIN-LIBRARY-READY message on iframe initialization

Emit the PLUGIN-LIBRARY-READY message after the message listener is bound,

signaling to the parent window that the iframe is ready to receive postMessage

commands. This completes the handshake implementation to eliminate the race

condition where messages could be lost before the iframe's script initialization

completes.

* fix indentation

* remove extra space

* remove typo

* refactor(test-browser-messaging): update test descriptions and remove unused code

* lint fix

* fix: correct the update note
2026-09-08 09:28:15 +01:00
Mario PietschandGitHub 5577a3d07b Allow colons in variable names in call syntax (#10015)
Accept a colon in the variable name of a `<<...>>` call and of an
`((...))` attribute reference, restoring the v5.3.8 name character
class. Call Syntax already describes the callee name as any
non-whitespace character other than `(` or `>`, and the colon is only
meaningful at the parameter name position.

* Add test cases for a colon name standalone, as an attribute value, as
  an =-separated parameter value, and with named parameters
* Add the 5.5.0 change note
2026-09-08 09:12:02 +01:00
Théophile DesmedtandGitHub d62b75cceb Fix #10004 iframe DOM leak in unloadIFrame loop (#10018)
* Fix unloadIFrame loop condition for proper iframe unloading

* add changenote for #10018

* add missing field to changenote
2026-09-08 12:11:06 +04:00
30a51be168 Bump @humanfs/node from 0.16.7 to 0.16.8 (#10008)
Bumps [@humanfs/node](https://github.com/humanwhocodes/humanfs/tree/HEAD/packages/node) from 0.16.7 to 0.16.8.
- [Release notes](https://github.com/humanwhocodes/humanfs/releases)
- [Changelog](https://github.com/humanwhocodes/humanfs/blob/main/packages/node/CHANGELOG.md)
- [Commits](https://github.com/humanwhocodes/humanfs/commits/node-v0.16.8/packages/node)

---
updated-dependencies:
- dependency-name: "@humanfs/node"
  dependency-version: 0.16.8
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-07 15:00:46 +04:00
Jeremy Ruston 38fa4486e4 Merge branch 'tiddlywiki-com' 2026-09-05 14:58:11 +01:00
Théophile DesmedtandGitHub d0a0198f49 Fix image reference for TiddlyWiki architecture (#10000)
Fix the broken reference to "TiddlyWiki Architecture.svg"
2026-08-29 23:26:01 +01:00
Jeremy Ruston 52c60be3a0 Merge branch 'tiddlywiki-com' 2026-08-25 17:24:38 +01:00
Jeremy Ruston b3279e348d Update Quine URL 2026-08-25 17:24:25 +01:00
Jeremy Ruston 516135e1b2 Merge branch 'master' of https://github.com/TiddlyWiki/TiddlyWiki5 2026-08-24 22:13:04 +01:00
Jeremy Ruston 6845f1c47e Merge branch 'tiddlywiki-com' 2026-08-24 22:12:30 +01:00
Théophile DesmedtandGitHub 6680967875 Improve discoverability of the style attribute feature (#9975)
* Improve discoverability of the style attribute feature

* Add missing tiddler  "Style Attributes"
2026-08-21 09:04:34 +02:00
Mario PietschandGitHub f9d5ee0ad7 Add ButtonWidget examples and execution order (#9945)
Document the button widget with runnable test cases and explain how its
attributes combine.

* Add eleven test case tiddlers, listed by title prefix so a new one needs
  no edit to the widget page
* Describe the order in which a button runs body actions, to, message,
  popup, set and actions, whatever order they are written in
* Prefer class over a hardcoded style
2026-08-16 13:17:33 +02:00
Mario PietschandGitHub 68630c939d Docs only Groupedlists examples (#9954)
* Add GroupedLists examples

Document grouped lists with runnable test cases over a shared
bibliography sample.

* Cover plain grouping, search, tabs by first character, both combined,
  and grouping on one field while searching another
* Name the grouped field in one procedure, so the grouping changes in
  one line
* Import it with import-compound, and list the examples by tag so a new
  one needs no page edit
* Rewrite the opening paragraph, which described only the sidebar tabs

* Replace GroupedLists code dumps with examples

Turn the two sidebar tabs the page describes into runnable test cases,
and tighten how the examples handle an empty result.

* Give each new example its own dummy payload, and point the page at the
  live tabs instead of quoting core source
* Carry the values through the filter from the start, so an All tab with
  no matches yields nothing rather than the literal All
* Use the list-empty widget

* use tc-tiny-gap instead of -left -right
2026-08-16 13:16:04 +02:00
Simon HuberandGitHub 5e5937838b Fix: #9879 change release note for version 5.5.0 (#9968) 2026-08-14 12:56:09 +02:00
Simon HuberandGitHub 6c8ac9b6a7 Refactor class attributes for edit field wrapper (#9879)
* Refactor class attributes for edit field wrapper

The problem here is, that if we have an existing field with name - say - "field1" and we want to add a field with name "field12345", at the moment we insert "field1" the edit-text widget re-renders because its classes change. The `tc-edit-field-exists` class must go outside to the container div.

* Add changenote for bugfix in field-name inputs

Fixes the field-name input re-rendering issue that caused loss of focus when inserting existing field-names.
2026-08-14 08:31:20 +02:00
Simon HuberandGitHub 0c60e1fa7a Add class assignment to DOM node in editors (#9965)
* Add class assignment to DOM node in editor

* Fix indentation in assignDomNodeClasses method

* Fix conditional check for changedAttributes class

* Improve class assignment in EditTextWidget

Refactor class assignment logic to avoid duplicates and handle whitespace.

* Refactor DOM class assignment in EditTextWidget

* Refactor updateDomNodeClasses for clarity and efficiency

* Add changenote for enhancement in editor refresh behavior
2026-08-13 12:21:20 +02:00
Mario PietschandGitHub b40b6de3ae Make sort order using sortby more consistent (#9747)
* make sort order more consistent

* Fix tests, as they are to be different due to the change.

* Add ChangeNote

* sortby: add "end" operand to place unlisted last

Give the sortby filter operator an optional second operand `end` that
places input titles missing from the reference list after the listed
ones, while the default still keeps them at the start.

* x-listops: read a second operand; `end` sorts unlisted titles to the
  end of the list, the default (no operand) leaves them at the start
* Document the optional `place` parameter on the sortby operator and
  rename the operator and examples tiddlers to match their titles
* Add a `sortby[...],[end]` example to the operator examples
* Replace the 5.5.0 change note with one describing the new `end`
  operand and its unchanged default
* Extend the sortby tests to cover both default (unlisted first) and
  `end` (unlisted last) placement

* Make the sortby end flag a suffix

Specify the placement of unlisted titles with an operator suffix.
Eg: `sortby:end[a b c]`

* Read the flag from `operator.suffix`
* Move the example and the change note to the suffix syntax
* Switch the sortby tests to the suffix syntax

* Cover the sortby guard and unlisted ordering

Close gaps found by mutating the sortby operator

* Sort two titles to catch an off-by-one in the length guard
* Keep unlisted titles in input order under the `end` suffix to catch a
  comparator tie break
* Return a single title untouched, exercising the early return
2026-08-13 11:18:38 +01:00
aac08a6252 Bump js-yaml from 4.3.0 to 4.3.1 (#9966)
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.3.0 to 4.3.1.
- [Changelog](https://github.com/nodeca/js-yaml/blob/4.3.1/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.3.0...4.3.1)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 4.3.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-12 15:19:57 +02:00
Simon HuberandGitHub 40b353bd0e Touch a temp tiddler when removing a tag (#9889)
* Touch a temp tiddler when removing a tag

Like that removing a tag doesn't make TiddlyWiki5 wait for the Typing timeout to remove it but removes it instantly, because also a non-draft Tiddler has changed

* Add release notes for bugfix in Edit Mode tags

Fixes an issue where tags of Tiddlers in Edit Mode don't animate instantly when they get removed.

* Add inline comment

* Update release version to 5.5.0 in changenote
2026-08-11 10:50:55 +02:00
superuser-doesandGitHub 7efbd01801 update 5.4.0 release notes: there was no change to default code wrap (#9962) 2026-08-08 11:46:30 +02:00
Théophile DesmedtandGitHub 5143138c16 [DOCS] Update [[Custom Widgets]] with new example for overriding LinkWidget (#9961)
* [DOCS] Update [[Custom Widgets]] with new example for overriding LinkWidget

* fix: non-svg link icon are now sizing properly

* replace HelloThere by list-link in the example for robustness

* Add class to span for better styling in Custom Widgets

* Fix typo in Custom Widgets.tid documentation

* Improve doc by using the proper macro to link widgets

* Change font size style to important for title icon

Updated the font size style for the title icon to be important.

* Update transclude and slot elements in Custom Widgets

Provide a fallback if no body for the link is given

* Enhance link widget to show icons conditionally

Updated the link widget to conditionally display an icon next to the link based on the target tiddler's icon field. Modified the rendering logic to use a conditional statement for the icon display.

* Change transclude height to size in Custom Widgets
2026-08-07 22:05:06 +02:00
Jeremy Ruston 251871f9cf Merge branch 'master' into colour-improvements 2026-08-06 10:53:18 +01:00
Jeremy Ruston 8e65fdc06a Merge branch 'tiddlywiki-com' 2026-08-06 10:50:33 +01:00
Jeremy Ruston ceb0817176 Update Quine instructions 2026-08-06 10:48:53 +01:00
Simon HuberandGitHub 2f578589a0 Fix typo in story width variable name (#9958)
* Fix typo in story width variable name

* Add change note for release 5.5.0

Fixes a typo in the CSS custom properties
2026-08-06 11:20:13 +02:00
916df90cc9 Fix $tw.utils.pulseElement() (#9741)
* Fix $tw.utils.pulseElement() by replacing missing $tw.browser.animationEnd

Use $tw.utils.convertEventName("animationEnd") and add pulse CSS animation

Fixes #6835

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add ChangeNote

* add tc- prefix to pulse class

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-07-30 06:15:38 +02:00
ea18c85a4c Bump brace-expansion from 1.1.12 to 1.1.16 (#9953)
Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 1.1.12 to 1.1.16.
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v1.1.12...v1.1.16)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 1.1.16
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 21:53:27 +02:00
Mario PietschandGitHub abf134afe4 Fix regression introduced by PR #9815 (#9944)
* Keep retained tiddler subdirectories on save

Make generateTiddlerFilepath keep a retained tiddler's recorded subdirectory
and produce a valid filename for every path segment.

* Normalise originalpath separators to "/" before the sanitiser, so recorded
  subdirectories survive
* Validate each path segment (reserved names, trailing dots or spaces), so
  "sub/CON" is valid on every OS
* Run that pass after transliteration, which can itself create a reserved name
* Add filesystem and adversarial specs, covering generateTiddlerFileInfo and
  generateTiddlerExtension

* update changenote
2026-07-28 11:34:09 +01:00
Saq ImtiazandGitHub 45d2ca948a Optimizes filterrunprefixes & filters using ES2017 (#9943)
* refactor: use ES2017 to optimize filter run prefixes

* chore: eslint

* chore: eslint

* fix: added missing file

* refactor: prefix, suffix and subfilter operators

* refactor: further improvements using new String methods

* docs: added changenote

* chore: renamed docs tiddler

* chore: renamed docs tiddler
2026-07-28 12:32:48 +02:00
Mario PietschandGitHub 94b046b8cb Fix button widget selection state with setTitle (#9951)
Make the button widget apply selectedClass correctly when the state is
addressed with setTitle instead of set.

Issue #9949, the button could not be rendered at all

* Guard the setField read in isSelected(), which dereferenced the result of
  getTiddler() directly. A missing state tiddler threw a TypeError, and that
  tiddler is normally only created by the first click

Issue #9950, every button of a set reported itself as selected

* Rewrite isSelected() as an if/else. The nested ternary mixed `||` with
  `?:`, so the setTitle branch returned a fallback value instead of a
  comparison
* Watch the setTitle state tiddler in refresh(), so the selected class
  follows the state as it already does for set

Documentation and tests

* Document that setField takes preference over setIndex. That has always
  been the behaviour, it was never written down
* Document that selectedClass and default apply to setTitle as well as to set
* Add wiki based test cases for the set, setTitle, setField and setIndex
  selection paths. Six of the seven fail against v5.4.1, the one that passes
  uses set, which was never broken

Fixes #9949
Fixes #9950
2026-07-28 11:19:12 +01:00
Saq ImtiazandGitHub c710a56b21 Optimizes x-listops using ES2017 (#9942)
* refactor: use ES2017 to optimize x-listops

* chore: eslint

* refactor: optimize listops.js using ES2017

* refactor: prefix operator

* docs: added changenote

* feat: added tests
2026-07-28 11:12:35 +01:00
saqimtiaz e681096127 fix: remove duplicate widget evaluation for multi-valued variables 2026-07-24 21:04:54 +02:00
24b433211d feat: add JSON output support to makepatches operator (#9940)
* feat: enhance makepatches function to support multiple suffixes and JSON output format

* feat: add release notes for makepatches JSON output enhancement

* doc: update makepatches operator documentation to document new json output

* doc: add JSON output examples to makepatches and applypatches documentation

* chore: convert space indentations to tabs

* test: add tests for makepatches operator with JSON output support

* chore: change space to tab indentation

* test: add DiffMergePatch4 integration test for testing makepatches json output

* fix: remove superfluous heading character

* refactor the parsing of the makepatches suffixes

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

* refactor: improve makepatches function for better readability and performance

* chore: apply style guide by collapsing consecutive variable assignments

* chore: remove extraneous tabs

---------

Co-authored-by: Saq Imtiaz <saq.imtiaz@gmail.com>
2026-07-24 16:17:34 +02:00
Jeremy Ruston a64bbeaf52 Merge branch 'master' into colour-improvements 2026-07-23 17:27:52 +01:00
Jeremy Ruston 9fecc7b1d9 Increase browser test timeout for slower CI runners 2026-07-23 17:20:13 +01:00
Jeremy Ruston 2bc7826b5e Add missing from-version markers to colour docs 2026-07-23 13:45:21 +01:00
Jeremy Ruston d6174f0c46 Add tests for colour utilities, sendmessage duplicate values and interpolation suffixes 2026-07-23 13:45:21 +01:00
Jeremy Ruston c6c29ec68e Docs corrections from review
Updates the palette customisation docs for the layer model, fixes a mangled
sentence in the change note, and tidies wording
2026-07-23 13:32:19 +01:00
Jeremy Ruston fd96e5c8cb Add tests for changecount, scheme imports, derived palette entries and readability tests 2026-07-23 13:32:19 +01:00
Jeremy Ruston 52a36c01ff Fix customisations panel selection when the palette redirects its imports
The panel is now chosen from the current palette's own customisations field
before falling back to the consolidated palette, so AutoToggle keeps its
panel after a palette has been chosen in it. Also mirrors the colour scheme
inheritance hop in the palette preview, and removes two leftover DeltaPhi
suffixes
2026-07-23 13:32:19 +01:00
Jeremy Ruston 002fd550a8 Store palette customisations in a layer above the palette itself
Customisations no longer modify the palette tiddler. They are stored in a
tiddler titled $:/config/PaletteCustomisations/<palette-title> that imports
the palette and is compiled in its place, so plugin palettes continue to
receive updates. Adds a reset button to the palette chooser, and a warning
banner and per-entry markers to the palette editor. Also excludes the type
field from palette field inheritance, which previously broke compilation of
JSON data tiddler palettes
2026-07-22 18:18:34 +01:00
Jeremy Ruston c1d9b07b20 Standardise on customisations terminology for palette customisation panels
Renames the settings field to customisations, the panel tiddlers from
Settings to Customisations, and updates the docs to match
2026-07-22 17:36:20 +01:00
Jeremy Ruston 187849a6a9 Fix palette fallback wrongly treating shadow palettes as missing
The is[missing] operator returns true for shadow tiddlers, so the fallback
introduced for missing palettes made every plugin palette compile as Vanilla
2026-07-22 17:36:20 +01:00
Jeremy Ruston d5f8a04230 Fall back to the Vanilla palette when the current palette is missing 2026-07-22 17:11:02 +01:00
Jeremy Ruston 73ad4f633b Document the palette plugins
Adds an impact note for the palettes moving to the Legacy Palettes plugin,
readmes for both palette plugins, and official plugin doc tiddlers on tw5.com
2026-07-22 17:03:29 +01:00
Jeremy Ruston ea549f6e26 Lighten TwentyTwenties page background to pass its own contrast test 2026-07-22 16:19:31 +01:00
Jeremy Ruston df80a42d13 Restrict filter pragmas to the start of a filter
Pragmas appearing after a filter run now return an error. This removes the
ambiguity where the subfilter and filter operators saw the final pragma value
regardless of their position. Adds generic docs for filter pragmas and an
impact note for titles beginning with double colons
2026-07-22 16:13:46 +01:00
Jeremy Ruston 07d5f55abe Impact notes for the colour macro alpha suffix idiom and the lingo macro 2026-07-22 15:43:20 +01:00
Jeremy Ruston 1dfa09267b Clean up palette and filter run prefix code
Removes the unused actions-switch-colour-palette procedure, fixes the filter
and subfilter operators to fall back to the default filter run prefix when
given an empty suffix, and notes that the DefaultColourMappings fallback is
retained for third party compatibility
2026-07-22 15:36:44 +01:00
Jeremy Ruston a0ee17989b Remove the unimplemented DeltaPhi suffix from colour contrast callers 2026-07-22 15:33:41 +01:00
Jeremy Ruston 2578eac294 Refactor 2026 palettes to use inheritance 2026-07-22 15:16:32 +01:00
Jeremy Ruston 60f0d22777 Fix tour plugin palette typo 2026-07-22 15:10:06 +01:00
Jeremy Ruston 503c60f666 Add colour-composite operator and use it to fix contrast tests for translucent colours 2026-07-22 15:05:49 +01:00
abf8ffdbca Fix 9814 file system paths "pragma: \ define" tiddler (#9815)
* New test for all "forbidden" directory characters

* Fix for 9814 Pragma: \define creates dir problem

* improve handling of tiddlers in root/community directory

* Add ChangeNote

* Apply suggestion from @saqimtiaz

* Apply suggestion from @saqimtiaz

---------

Co-authored-by: Saq Imtiaz <saq.imtiaz@gmail.com>
2026-07-22 13:45:25 +02:00
Mario PietschandGitHub 97e27d0dad Fix regex for code block end detection in wikiparser (#9739)
* Fix regex for code block end detection in wikiparser

* Anchor the codeblock end fence to its own line

Close an empty code block without consuming the tiddler, while a fence
only counts when it stands alone on its line.

* Match the end fence with (^|\r?\n) so the empty block case closes at
  the line start and a code line ending in ``` stays inside the block
* Add test-codeblock-parser.js covering empty blocks with language and
  CRLF, delimiter newline handling, mid-line fences and a missing fence
2026-07-22 13:12:55 +02:00
Jeremy Ruston 4259ce4979 Docs updates 2026-07-21 18:49:42 +01:00
Jeremy Ruston 855dc2fbb9 Merge branch 'master' of https://github.com/TiddlyWiki/TiddlyWiki5 2026-07-21 17:24:39 +01:00
lin onetwoandGitHub e1e34f4907 Move background action console.log inside platforms check (#9891)
* Move background action log inside platforms check

* Add change note for #9891

* Shorten change note

* Release notes: move #9891 from 5.4.1 to 5.5.0

PR #9891 is still open, so the background-action log change is planned for 5.5.0 instead of 5.4.1.

* Fix change-category for #9891 release note

 is not a valid category; use  instead.
2026-07-21 18:16:33 +02:00
Jeremy Ruston 7196b9ee9b Merge branch 'tiddlywiki-com' 2026-07-21 12:11:44 +01:00
Jeremy Ruston c0b3403f87 Remove extraneous github ribbon in the dev edition 2026-07-21 12:11:14 +01:00
Jeremy Ruston 7f808bc64f Fix default colour space for colour-interpolate filter 2026-07-20 18:48:29 +01:00
Jeremy Ruston be9dc59f73 Fix test for recursive palettes 2026-07-20 18:47:56 +01:00
Jeremy Ruston 3a2a64b877 Recompile palette on changes to all tagged palette tiddlers 2026-07-20 18:47:36 +01:00
Jeremy Ruston 4c193f5d21 Pass the palette to the tag-body 2026-07-20 18:46:44 +01:00
Jeremy Ruston 7322f0c024 Fix reference to current palette
Hardcoding was a bug
2026-07-20 18:46:12 +01:00
Jeremy Ruston 9c2a36cefe Ship the Vanilla palette in the core for backwards compatibility
Also fixes typos in the Vanilla Cherry and TwentyTwenties palettes
2026-07-19 15:57:28 +01:00
Jeremy Ruston 0ce2cba0b1 Fix convertCSSColorToRGBString to always return a six digit hex value 2026-07-19 15:57:28 +01:00
Jeremy Ruston 1103b733fe Guard colour operators against achromatic hues and unparseable target colours 2026-07-19 15:57:28 +01:00
Jeremy Ruston 81d7283990 Fix editor colour scheme lookup and refreshing of the default attribute 2026-07-19 15:57:28 +01:00
Jeremy Ruston 3dba0f2938 Report palette entries with recursive definitions
Recursive definitions are blanked and reported via the readability test mechanism
2026-07-19 15:57:28 +01:00
Jeremy Ruston ff8b077b0e Fix palette preview handling of scheme-suffixed entries and circular imports
The scheme-suffixed lookup key was constructed backwards, and imported
palettes are now tracked in an exclusions list to guard against loops
2026-07-19 15:57:28 +01:00
Jeremy Ruston 66b5d3f0fd Fix compilation of scheme-suffixed palette entries
Suffixed entries are now copied after unsuffixed entries so that they override
them as documented, the names and values are sorted consistently, and a
malformed <%endif%> is corrected
2026-07-19 15:57:28 +01:00
Jeremy Ruston ec5d73c3a8 Fix incorrect signature for calling wiki.filterTiddlers method 2026-07-19 15:57:27 +01:00
Jeremy Ruston b71c3044ce Docs 2026-07-19 15:57:27 +01:00
Jeremy Ruston f09cc75fb6 Another palette typo 2026-07-19 13:23:45 +01:00
Jeremy Ruston df9074773c Fix palette typo 2026-07-19 11:16:16 +01:00
Jeremy Ruston 6a2670592f Update release notes 2026-07-18 15:00:22 +01:00
Jeremy Ruston 35ede09e0f Fix the old, deprecated parseCSSColorObject function to return 0-255 2026-07-18 11:57:45 +01:00
Jeremy Ruston 3862082f64 Update release note 2026-07-18 11:55:13 +01:00
Jeremy Ruston b7d2cb9b78 Remove typo 2026-07-18 11:55:01 +01:00
Jeremy Ruston 29c3a8d412 Improve introduction 2026-07-18 11:54:43 +01:00
Jeremy Ruston 546f95ad07 Guard against an unknown default filter run prefix 2026-07-18 11:51:08 +01:00
Jeremy Ruston 6d8bfe269c Only initialise performance framework once 2026-07-18 11:49:54 +01:00
Jeremy Ruston ed71c8262a Merge branch 'tiddlywiki-com' 2026-07-18 11:36:50 +01:00
Jeremy Ruston 2ececf21c7 Cherry pick docs update from master 2026-07-18 11:36:37 +01:00
7516d96eed Update docs: running a custom Node.js TiddlyWiki version with npx (#9886)
* Update docs: running a custom Node.js TiddlyWiki version with npx

* Update docs: document different options of running multiple TW versions

---------

Co-authored-by: shadow <shadow@darkstar.localdomain>
Co-authored-by: me <me@localhost>
2026-07-18 10:31:42 +01:00
4bf7e9d192 [Bug] Fix the CSV parser blanking a table (#9919)
* Fix the CSV parser blanking a table

getCellInfo read the loop variable i before its declaration, so hoisting made
it undefined and charAt(undefined) read the first character of the whole text.
Any CSV opening with a separator therefore returned every cell empty.

- Test the cell against the start of the cell rather than the hoisted i
- Keep a separator inside a quoted cell literal
- Cover the parser with tests

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add a change note for the CSV fix

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 17:26:59 +02:00
Bram ChenandGitHub 04cd3081e4 Update chinese language files (#9933)
* Update chinese language files

* Add WikiInformation/Generate/Caption in ControlPanel.multids

* Add ChangeNote
2026-07-14 15:50:37 +01:00
saqimtiaz d27716c1e0 chore: fixed erroneous changenote number 2026-07-14 13:17:58 +02:00
Mario PietschandGitHub 7f7f36d986 Skip system tiddlers in back-indexer updates (#9917)
* Skip system tiddlers in back-indexer updates

Fix #9916: editing a system tiddler that links or transcludes another
tiddler made it a backlinks[]/backtranscludes[] source until the next
index rebuild (shipped since v5.3.4).

* Guard BackSubIndexer.update() with isSystemTiddler() on both sides,
  matching the initial scan; this also skips parsing $:/StoryList on
  every navigation
* Add test-back-indexer.js: regression guards plus adversarial probes
  for shadow reveals and hostile __proto__ titles

* Add ChangeNote
2026-07-14 13:10:21 +02:00
Mario PietschandGitHub 86a4e18134 diff-text change newline to ↲ (#9736)
* diff-text change newline to ↲

* Add ChangeNote
2026-07-14 13:04:50 +02:00
Mario PietschandGitHub 304858c7c5 Make Click to generate wiki info button translatable (#9737)
* Make Click to generate wiki info button translatable

* Add ChangeNote
2026-07-14 12:53:23 +02:00
Mario PietschandGitHub 7ececf9e0f Align fakedom tagname with HTML spec (#9843)
* Fakedom: uppercase tagName for HTML elements per DOM spec

Browsers uppercase tagName for HTML elements. Other namespaces (SVG,
MathML, XML) preserve case. Fakedom now matches, so widget code can use
the natural `node.tagName === "OPTGROUP"` form in both environments.

https://dom.spec.whatwg.org/#dom-element-tagname

* Tests: cover fakedom tagName spec compliance

Three specs pin the DOM contract for tagName: uppercase for HTML
elements, case preservation for other namespaces, and non-mutating
reads. Includes the OPTGROUP predicate the select widget will rely on
once it can drop its defensive .toUpperCase() call.

https://dom.spec.whatwg.org/#dom-element-tagname

* Fakedom: empty or null namespace creates a no-namespace element

Per DOM spec, createElementNS("") and createElementNS(null) have to produce
elements with no namespace. Fakedom previously defaults both to HTML,
which made tagName incorrectly uppercase non-HTML elements.
createElement without arguments still defaults to HTML.

https://dom.spec.whatwg.org/#dom-document-createelementns

* Add ChangeNote
2026-07-14 12:22:45 +02:00
saqimtiaz 521e530e11 docs: added size comparison of empty.html from prerelease and latest release 2026-07-14 07:55:48 +02:00
Jeremy Ruston 46ffd7ebf3 Default to the hire jeremy banner in the sidebar 2026-07-13 16:02:28 +01:00
Jeremy Ruston 65605d7b72 Typo 2026-07-13 15:56:51 +01:00
Jeremy Ruston b3f9690941 Linting 2026-07-13 15:42:50 +01:00
XLBillyandGitHub 4c29dae4af Replace some wikify widget with call dynamic attribute syntax (#9816)
* Replace some wikify widget with call dynamic attribute syntax

* Apply suggestions

* Add change note
2026-07-13 15:57:04 +02:00
Jeremy Ruston 038d9e474e Merge branch 'master' into colour-improvements 2026-07-13 14:47:21 +01:00
Mario PietschandGitHub f5317dc225 Fix Node.js deprecated warning (#9742)
* Fix Node.js deprecated warning

* Add ChangeNote

* Change deprecation to bugfix for DEP0169 warning

Updated change type from 'deprecation' to 'bugfix' for the Node.js server's handling of the DEP0169 warning.
2026-07-13 13:43:07 +02:00
lin onetwoandGitHub c952450c2e fix(filter): optimize tag[] and !tag[] with Set for O(1) lookup (#9715)
* fix(filter): optimize tag[] and !tag[] with Set for O(1) lookup, resolves performance gap vs search:tags[]

* useless test

* note

* note
2026-07-13 13:37:05 +02:00
Rob HoelzandGitHub 1eb7ec4402 Convert deeply nested if/else to do/while(false) with breaks for parseAttribute (#9770)
01d3cde96 refactored parseMacroParameterAsAttribute to use a
do/while(false) construct with break statements - this updates
parseAttribute with an analogous refactoring for consistency
2026-07-13 08:15:26 +02:00
9a0bbf831b Signing the CLA (#9923)
* Signing the CLA

* Apply suggestion from @saqimtiaz

* Apply suggestion from @saqimtiaz

* Apply suggestion from @saqimtiaz

---------

Co-authored-by: Saq Imtiaz <saq.imtiaz@gmail.com>
2026-07-12 19:37:43 +02:00
superuser-doesandGitHub a30854a4c7 [DOCS] Corrected Description Lists tiddler (#9819)
* [DOCS] Corrected Description Lists tiddler

* Correct modified, remove trailing whitespace
2026-07-12 09:48:53 +02:00
869557f7d1 Bump js-yaml from 4.1.1 to 4.3.0 (#9903)
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.1 to 4.3.0.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.1.1...4.3.0)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 4.3.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-12 09:22:08 +02:00
Mario PietschandGitHub 856057be81 Add 8972 field names with parens impact note (#9875) 2026-07-12 09:20:31 +02:00
saqimtiaz c9f1154643 chore: preparing for v5.5.0 2026-07-10 12:41:53 +02:00
Jeremy Ruston 748f04c9e3 Update TiddlyWiki release checklist 2026-07-10 11:01:22 +01:00
saqimtiaz d391595836 Version number update for 5.4.1 2026-07-10 11:39:38 +02:00
saqimtiaz 967140a148 Merge branch 'master' of github.com:TiddlyWiki/TiddlyWiki5 2026-07-10 11:37:07 +02:00
saqimtiaz 95e68b0437 chore: added missing changenotes 2026-07-10 11:36:59 +02:00
Jeremy Ruston 8ddede1611 Missing v5.4.1 release notes 2026-07-10 10:36:20 +01:00
saqimtiaz ceb200b08c chore: update license and readme 2026-07-10 11:17:53 +02:00
saqimtiaz 00e5f48a59 chore: preparing for v5.4.1 release 2026-07-10 11:12:01 +02:00
saqimtiaz ea0e9105bc Merge branch 'tiddlywiki-com' 2026-07-10 11:00:56 +02:00
Jeremy Ruston 51459815ba Fix CI errors 2026-07-09 16:16:36 +01:00
Jeremy Ruston 8c62935a01 Fix incorrect reference to BurningTreeC's website 2026-07-09 14:14:47 +01:00
Jeremy Ruston 839fa2417d Fix CI issues
1. Remove references to obsolete plugins
2. Fix duplicate language string entry
2026-07-09 14:12:10 +01:00
Saq ImtiazandGitHub 1b8610e4d8 Fixes issue with copying cross window event properties (#9905)
* fix: issue with copying cross window event properties

* docs: added changenote
2026-07-09 12:50:12 +01:00
Mario PietschandGitHub ca063cbe90 Add quick test runner (#9743) 2026-07-08 13:56:16 +01:00
Jeremy Ruston 7d4328b2fb Merge branch 'master' of https://github.com/TiddlyWiki/TiddlyWiki5 2026-07-08 12:02:55 +01:00
Jeremy Ruston 1ae224ab74 Merge branch 'tiddlywiki-com' 2026-07-08 12:02:50 +01:00
Mario PietschandGitHub c308fc44c4 Fix invalid source positions in parse tree nodes (#9882)
* Fix invalid source positions in parse tree nodes

Several wikitext rules recorded start and end offsets that did not match
the source they represent. The offsets are only used for source position
mapping, not rendering or serialization, so output is unchanged and
source.slice(start, end) now equals the node text.

* import: the filter attribute start was set to the whole source string
  instead of the numeric offset. It used this.parser.source where it
  meant this.parser.pos, so every \import node carried a copy of the
  entire source as its start.

* codeinline: the text node end pointed past the closing backtick, so the
  span included the closing delimiter and could read one or two characters
  too far. It now ends where the content ends.

* wikilinkprefix and extlink: a suppressed link such as ~SomeLink or
  ~http://example.com produced a text node whose span included the
  leading ~ while its text did not. The span now starts after the ~.

* Add 5.4.1 change note for #9882

Document the parse tree source position fixes: inline code, suppressed
external links, suppressed wikilinks, and the import filter rule.

* Add tests for parse tree source positions

Cover the four wikitext parser rules whose `start` and `end` offsets are
corrected in this PR: codeinline, extlink, wikilinkprefix, and import.
2026-07-08 10:03:11 +01:00
Jeremy Ruston aa3fb85919 BurningTreeC is leader of the TiddlyDesktop team
@BurningTreeC please check the details of your card,
2026-07-08 09:58:40 +01:00
Jeremy Ruston bd052a33f8 Correct v5.4.1 banner 2026-07-08 09:45:29 +01:00
superuser-doesandGitHub bb766c36c3 [DOCS] Fix survey image link (#9897) 2026-07-01 15:55:49 +02:00
Jeremy Ruston 9c1f69e9c1 Revert "[ja_JP] Update of Japanese translations (#9853)"
Reverting as was inadvertently merged to tiddlywiki-com

This reverts commit 14b11575d0.
2026-06-04 08:28:09 +01:00
IchijikuIchigoandGitHub 14b11575d0 [ja_JP] Update of Japanese translations (#9853)
* [ja_JP] Japanese translation update from commit: 3ed481b

* [ja_JP] Japanese translation update from commit: ad3587b

* [ja_JP] Japanese translation update from commit: 6bc77cf

* [ja_JP] Japanese translation update from commit: 5d1c1ea, c4991a5

* [ja_JP] Japanese translation update from commit: 37a4613

* [ja_JP] Japanese translation update from commit: 748ef8a

* [ja_JP] Japanese translation update from commit: 92caa73

* [ja_JP] Japanese translation update from commit: 87ba87b

* [ja_JP] Japanese translation update from commit: 15ba415

* [ja_JP] Japanese translation update from commit: 64ee20e

* [ja_JP] Japanese translation update from commit: 5d1c1ea,add3d42

* [ja_JP] Japanese translation update from commit: 64ee20e

* [ja_JP] Japanese translation update from commit: b001333

* [ja_JP] Japanese translation update from commit: b434b9d

* [ja_JP] Japanese translation update from commit: 3983086
2026-06-04 08:25:12 +01:00
Jeremy Ruston 2dd9a2006d Impact note about wikified palettes 2026-01-30 20:57:13 +00:00
Jeremy Ruston ebff5aa3db Make Palette be the default Appearance tab in control panel 2026-01-30 20:56:59 +00:00
Jeremy Ruston 3bc2c5fabc Remove appply filter run prefix 2026-01-30 20:56:38 +00:00
Jeremy Ruston d423aa926e Tweak lingo 2026-01-30 20:56:14 +00:00
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
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
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 421ecff30f Merge branch 'master' into colour-improvements 2026-01-17 17:52:34 +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
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 5b0c923a82 Merge branch 'master' into colour-improvements 2026-01-08 11:23:43 +00: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
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
417 changed files with 9450 additions and 1429 deletions
-69
View File
@@ -1,69 +0,0 @@
# Repository Contribution Guidelines & Automation
This document establishes the single source of truth for repository contribution guidelines, optimized for AI context consumption, alongside a TiddlyWiki-based automation strategy to prevent source-of-truth drift.
---
## 📄 The High-Density Ruleset
*Copy the content below into a single master tiddler titled `ProjectContributionRules` with the type `text/x-markdown`.*
```markdown
# CONTRIBUTING
## AI & Comment Rules
* Banned: Conversational filler, verbose explanations of standard APIs, or teaching basic programming logic.
* Required: Prune all AI-generated comment blocks down to a single sentence maximum.
* Focus: Comments must only state the "why" behind non-obvious engineering decisions, never the "how".
* PR Descriptions: Write concise, bulleted summaries of what changed. Do not use AI-generated marketing/polite filler text.
### Code Comment Examples
* BAD: // Use behavior:"instant" so that this animation's per-frame position updates are applied immediately and are NOT re-animated by a CSS `scroll-behavior: smooth` on the scrolling element (which would make the page lag behind and stutter).
* GOOD: // Use 'instant' to prevent conflicts with CSS scroll-behavior: smooth.
## Workflow Rules
* Branching: Base all feature branches on `main`. Use `feat/` or `fix/` prefixes.
* Scope: Keep pull requests limited to a single logical change. Massive, multi-feature PRs will be rejected.
* Testing: Run local tests and formatting checks before pushing.
* Merging: All commits are squashed on merge.
```
---
## 🛠️ TiddlyWiki Build Integration
To ensure this file is distributed automatically to all required locations without duplication, use TiddlyWiki's native command-line rendering tool.
### 1. Update `tiddlywiki.info`
Add a dedicated `repo-configs` target to your project's `tiddlywiki.info` JSON file. This instructs TiddlyWiki to compile the master tiddler out to both human-facing and AI-facing configuration endpoints:
```json
{
"description": "Project build targets",
"plugins": [
"tiddlywiki/markdown"
],
"build": {
"repo-configs": [
"--rendertiddler", "ProjectContributionRules", "./CONTRIBUTING.md", "text/plain",
"--rendertiddler", "ProjectContributionRules", "./.claudecode", "text/plain"
]
}
}
```
### 2. Update `package.json`
Integrate the rendering command into your existing Node.js project lifecycle scripts so it triggers automatically during development builds or pre-commit checks:
```json
{
"scripts": {
"build:configs": "tiddlywiki --build repo-configs"
}
}
```
---
## 🚀 Why This Works
* **Zero Duplication:** Edits are made exclusively within `ProjectContributionRules` inside the wiki workspace.
* **Human Visibility:** Browsing GitHub automatically surfaces `CONTRIBUTING.md` to human contributors.
* **Claude Code Ready:** When **Claude Code** sweeps the project root, it instantly reads the identical, strict engineering boundaries outputted to `.claudecode`.
+1 -1
View File
@@ -5,7 +5,7 @@
# Default to the current version number for building the plugin library # Default to the current version number for building the plugin library
if [ -z "$TW5_BUILD_VERSION" ]; then if [ -z "$TW5_BUILD_VERSION" ]; then
TW5_BUILD_VERSION=v5.4.1. TW5_BUILD_VERSION=v5.5.0.
fi fi
echo "Using TW5_BUILD_VERSION as [$TW5_BUILD_VERSION]" echo "Using TW5_BUILD_VERSION as [$TW5_BUILD_VERSION]"
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
title: TiddlyWiki Team
tags: Community/Team
modified: 20260708095630754
created: 20260708095630754
leader: @BurningTreeC
team:
The TiddlyDesktop development repository is at https://github.com/TiddlyWiki/TiddlyDesktop
+33 -5
View File
@@ -217,6 +217,11 @@ exports.generateTiddlerFileInfo = function(tiddler,options) {
fileInfo.isEditableFile = true; fileInfo.isEditableFile = true;
fileInfo.originalpath = options.fileInfo.originalpath; fileInfo.originalpath = options.fileInfo.originalpath;
} }
// Propagate the pinFilepath flag (so generateTiddlerFilepath can short-circuit
// FileSystemPaths and write back to the original location)
if(options.fileInfo && !!options.fileInfo.pinFilepath) {
fileInfo.pinFilepath = true;
}
// Check if the tiddler has any unsafe fields that can't be expressed in a .tid or .meta file: containing control characters, or leading/trailing whitespace // Check if the tiddler has any unsafe fields that can't be expressed in a .tid or .meta file: containing control characters, or leading/trailing whitespace
var hasUnsafeFields = false; var hasUnsafeFields = false;
$tw.utils.each(tiddler.getFieldStrings(),function(value,fieldName) { $tw.utils.each(tiddler.getFieldStrings(),function(value,fieldName) {
@@ -319,9 +324,16 @@ exports.generateTiddlerFilepath = function(title,options) {
extension = options.extension || "", extension = options.extension || "",
originalpath = (options.fileInfo && options.fileInfo.originalpath) ? options.fileInfo.originalpath : "", originalpath = (options.fileInfo && options.fileInfo.originalpath) ? options.fileInfo.originalpath : "",
overwrite = options.fileInfo && options.fileInfo.overwrite || false, overwrite = options.fileInfo && options.fileInfo.overwrite || false,
pinFilepath = !!(options.fileInfo && options.fileInfo.pinFilepath),
filepath; filepath;
// If the tiddler's filepath is pinned via tiddlywiki.files, the originalpath
// wins and the path filters are skipped entirely.
if(pinFilepath && originalpath) {
var pinnedExt = path.extname(originalpath);
filepath = originalpath.substring(0,originalpath.length - pinnedExt.length);
}
// Check if any of the pathFilters applies // Check if any of the pathFilters applies
if(options.pathFilters && options.wiki) { if(!filepath && options.pathFilters && options.wiki) {
$tw.utils.each(options.pathFilters,function(filter) { $tw.utils.each(options.pathFilters,function(filter) {
if(!filepath) { if(!filepath) {
var source = options.wiki.makeTiddlerIterator([title]), var source = options.wiki.makeTiddlerIterator([title]),
@@ -336,13 +348,14 @@ exports.generateTiddlerFilepath = function(title,options) {
//Use the originalpath without the extension //Use the originalpath without the extension
var ext = path.extname(originalpath); var ext = path.extname(originalpath);
filepath = originalpath.substring(0,originalpath.length - ext.length); filepath = originalpath.substring(0,originalpath.length - ext.length);
// normalise "\" to "/" so the sanitiser keeps subdirectories
// (it strips "\", not "/") instead of flattening them.
filepath = filepath.split(path.sep).join("/");
} else if(!filepath) { } else if(!filepath) {
filepath = title; filepath = title;
// Remove any forward or backward slashes so we don't create directories // Remove any forward or backward slashes so we don't create directories
filepath = filepath.replace(/\/|\\/g,"_"); filepath = filepath.replace(/\/|\\/g,"_");
} }
// Replace any Windows control codes
filepath = filepath.replace(/^(con|prn|aux|nul|com[0-9]|lpt[0-9])$/i,"_$1_");
// Replace any leading spaces with the same number of underscores // Replace any leading spaces with the same number of underscores
filepath = filepath.replace(/^ +/,function (u) { return u.replace(/ /g, "_");}); filepath = filepath.replace(/^ +/,function (u) { return u.replace(/ /g, "_");});
//If the path does not start with "." or ".." && a path seperator, then //If the path does not start with "." or ".." && a path seperator, then
@@ -352,8 +365,23 @@ exports.generateTiddlerFilepath = function(title,options) {
} }
// Replace any Unicode control codes // Replace any Unicode control codes
filepath = filepath.replace(/[\x00-\x1f\x80-\x9f]/g,"_"); filepath = filepath.replace(/[\x00-\x1f\x80-\x9f]/g,"_");
// Replace any characters that can't be used in cross-platform filenames // Replace any characters that can't be used in cross-platform filenames.
filepath = $tw.utils.transliterate(filepath.replace(/<|>|~|\:|\"|\||\?|\*|\^/g,"_")); if(!pinFilepath) {
filepath = $tw.utils.transliterate(filepath.replace(/<|>|~|\:|\"|\||\?|\*|\^|\\/g,"_"));
}
// Per segment (catches "sub/CON"), MUST be after transliterate which can create a
// reserved name. Reserved names are wrapped on every path incl pinned;
// trailing dots/spaces (Windows strips) fixed for generated names, not pinned.
filepath = filepath.split("/").map(function(segment) {
if(segment === "" || segment === "." || segment === "..") {
return segment;
}
segment = segment.replace(/^(con|prn|aux|nul|com[0-9]|lpt[0-9])$/i,"_$1_");
if(!pinFilepath) {
segment = segment.replace(/[. ]+$/,function(u) { return u.replace(/[. ]/g,"_"); });
}
return segment;
}).join("/");
// Replace any dots or spaces at the end of the extension with the same number of underscores // Replace any dots or spaces at the end of the extension with the same number of underscores
extension = extension.replace(/[\. ]+$/, function (u) { return u.replace(/[\. ]/g, "_");}); extension = extension.replace(/[\. ]+$/, function (u) { return u.replace(/[\. ]/g, "_");});
// Truncate the extension if it is too long // Truncate the extension if it is too long
+4 -5
View File
@@ -9,15 +9,14 @@ Serve tiddlers over http
"use strict"; "use strict";
let fs, url, path, querystring, crypto, zlib; let fs, path, crypto, zlib, URL;
if($tw.node) { if($tw.node) {
fs = require("fs"), fs = require("fs"),
url = require("url"),
path = require("path"), path = require("path"),
querystring = require("querystring"),
crypto = require("crypto"), crypto = require("crypto"),
zlib = require("zlib"); zlib = require("zlib");
URL = require("url").URL;
} }
/* /*
@@ -260,8 +259,8 @@ Server.prototype.requestHandler = function(request,response,options) {
state.wiki = options.wiki || self.wiki; state.wiki = options.wiki || self.wiki;
state.boot = options.boot || self.boot; state.boot = options.boot || self.boot;
state.server = self; state.server = self;
state.urlInfo = url.parse(request.url); state.urlInfo = new URL(request.url, "http://localhost");
state.queryParameters = querystring.parse(state.urlInfo.query); state.queryParameters = Object.fromEntries(state.urlInfo.searchParams);
state.pathPrefix = options.pathPrefix || this.get("path-prefix") || ""; state.pathPrefix = options.pathPrefix || this.get("path-prefix") || "";
// Enable CORS // Enable CORS
if(this.corsEnable) { if(this.corsEnable) {
+7 -2
View File
@@ -58,10 +58,14 @@ LayoutSwitcher/Caption: Layout
LoadedModules/Caption: Loaded Modules 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. 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/Caption: Palette
Palette/Customisations/Prompt: Customisations for the current palette: <<palette-link>>
Palette/Customisations/Reset/Caption: reset customisations
Palette/Editor/Customised/Prompt: This palette has customisations that override the entries marked below
Palette/Editor/Customised/Entry: overridden by a customisation
Palette/Editor/Clone/Caption: clone Palette/Editor/Clone/Caption: clone
Palette/Editor/Clone/Prompt: It is recommended that you clone this shadow palette before editing it 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 Palette/Editor/Delete/Hint: delete this entry from the current palette
Palette/Editor/Names/External/Show: Show color names that are not part of the current palette Palette/Editor/Names/External/Show: Show inherited palette entries
Palette/Editor/Prompt/Modified: This shadow palette has been modified Palette/Editor/Prompt/Modified: This shadow palette has been modified
Palette/Editor/Prompt: Editing Palette/Editor/Prompt: Editing
Palette/Editor/Reset/Caption: reset Palette/Editor/Reset/Caption: reset
@@ -255,4 +259,5 @@ ViewTemplateTags/Caption: View Template Tags
ViewTemplateTags/Hint: This rule cascade is used by the default view template to dynamically choose the template for displaying the tags area of a tiddler. ViewTemplateTags/Hint: This rule cascade is used by the default view template to dynamically choose the template for displaying the tags area of a tiddler.
WikiInformation/Caption: Wiki Information WikiInformation/Caption: Wiki Information
WikiInformation/Hint: This page summarises high level information about the configuration of this ~TiddlyWiki. It is designed to enable users to quickly share relevant aspects of the configuration of their ~TiddlyWiki with others, for example when seeking help in one of the forums. No private or personal information is included, and nothing is shared without being explicitly copied and pasted elsewhere WikiInformation/Hint: This page summarises high level information about the configuration of this ~TiddlyWiki. It is designed to enable users to quickly share relevant aspects of the configuration of their ~TiddlyWiki with others, for example when seeking help in one of the forums. No private or personal information is included, and nothing is shared without being explicitly copied and pasted elsewhere
WikiInformation/Drag/Caption: Drag this link to copy this tool to another wiki WikiInformation/Drag/Caption: Drag this link to copy this tool to another wiki
WikiInformation/Generate/Caption: Click to generate wiki information report
+2
View File
@@ -30,6 +30,8 @@ Error/DeserializeOperator/MissingOperand: Filter Error: Missing operand for 'des
Error/DeserializeOperator/UnknownDeserializer: Filter Error: Unknown deserializer provided as operand for the 'deserialize' operator Error/DeserializeOperator/UnknownDeserializer: Filter Error: Unknown deserializer provided as operand for the 'deserialize' operator
Error/Filter: Filter error Error/Filter: Filter error
Error/FilterSyntax: Syntax error in filter expression Error/FilterSyntax: Syntax error in filter expression
Error/FilterPragma: Filter Error: Unknown filter pragma
Error/FilterPragmaPosition: Filter Error: Filter pragmas are only allowed at the start of a filter
Error/FilterRunPrefix: Filter Error: Unknown prefix for filter run Error/FilterRunPrefix: Filter Error: Unknown prefix for filter run
Error/IsFilterOperator: Filter Error: Unknown parameter for the 'is' filter operator Error/IsFilterOperator: Filter Error: Unknown parameter for the 'is' filter operator
Error/FormatFilterOperator: Filter Error: Unknown suffix for the 'format' filter operator Error/FormatFilterOperator: Filter Error: Unknown suffix for the 'format' filter operator
+1 -1
View File
@@ -78,7 +78,6 @@ class BackgroundActionTracker {
fnProcess: (changes) => { fnProcess: (changes) => {
if(this.hasChanged) { if(this.hasChanged) {
this.hasChanged = false; this.hasChanged = false;
console.log("Processing background action", this.title);
const tiddler = this.wiki.getTiddler(this.title); const tiddler = this.wiki.getTiddler(this.title);
let doActions = true; let doActions = true;
if(tiddler && tiddler.fields.platforms) { if(tiddler && tiddler.fields.platforms) {
@@ -89,6 +88,7 @@ class BackgroundActionTracker {
} }
} }
if(doActions) { if(doActions) {
console.log("Processing background action", this.title);
this.wiki.invokeActionString( this.wiki.invokeActionString(
this.actions, this.actions,
null, null,
+1 -2
View File
@@ -31,8 +31,7 @@ function FramedEngine(options) {
this.parentNode.insertBefore(this.iframeNode,this.nextSibling); this.parentNode.insertBefore(this.iframeNode,this.nextSibling);
this.iframeDoc = this.iframeNode.contentWindow.document; this.iframeDoc = this.iframeNode.contentWindow.document;
// (Firefox requires us to put some empty content in the iframe) // (Firefox requires us to put some empty content in the iframe)
var paletteTitle = this.widget.wiki.getTiddlerText("$:/palette"); var colorScheme = (this.widget.wiki.getTiddler("$:/temp/palette-colours") || {fields: {}}).fields["color-scheme"] || "light";
var colorScheme = (this.widget.wiki.getTiddler(paletteTitle) || {fields: {}}).fields["color-scheme"] || "light";
this.iframeDoc.open(); this.iframeDoc.open();
this.iframeDoc.write("<!DOCTYPE html><html><head><meta name='color-scheme' content='" + colorScheme + "'></head><body></body></html>"); this.iframeDoc.write("<!DOCTYPE html><html><head><meta name='color-scheme' content='" + colorScheme + "'></head><body></body></html>");
this.iframeDoc.close(); this.iframeDoc.close();
+9 -1
View File
@@ -28,7 +28,12 @@ function SimpleEngine(options) {
if(this.widget.editTag === "textarea") { if(this.widget.editTag === "textarea") {
this.domNode.appendChild(this.widget.document.createTextNode(this.value)); this.domNode.appendChild(this.widget.document.createTextNode(this.value));
} else { } 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 // Set the attributes
if(this.widget.editType && this.widget.editTag !== "textarea") { if(this.widget.editType && this.widget.editTag !== "textarea") {
@@ -83,6 +88,9 @@ Update the DomNode with the new text
*/ */
SimpleEngine.prototype.updateDomNodeText = function(text) { SimpleEngine.prototype.updateDomNodeText = function(text) {
try { try {
if(this.widget.editType === "color") {
text = $tw.utils.convertCSSColorToRGBString(text);
}
this.domNode.value = text; this.domNode.value = text;
} catch(e) { } catch(e) {
// Ignore // Ignore
+29 -2
View File
@@ -210,6 +210,29 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
this.editShowToolbar = this.wiki.getTiddlerText(ENABLE_TOOLBAR_TITLE,"yes"); this.editShowToolbar = this.wiki.getTiddlerText(ENABLE_TOOLBAR_TITLE,"yes");
this.editShowToolbar = (this.editShowToolbar === "yes") && !!(this.children && this.children.length > 0) && (!this.document.isTiddlyWikiFakeDom); this.editShowToolbar = (this.editShowToolbar === "yes") && !!(this.children && this.children.length > 0) && (!this.document.isTiddlyWikiFakeDom);
}; };
EditTextWidget.prototype.updateDomNodeClasses = function() {
var domNodeClasses = this.engine.domNode.className.split(/\s+/).filter(Boolean),
oldClasses = this.editClass.split(/\s+/).filter(Boolean),
newClasses;
this.editClass = this.getAttribute("class","");
newClasses = this.editClass.split(/\s+/).filter(Boolean);
// Remove classes assigned from the old value of the class attribute
domNodeClasses = domNodeClasses.filter(function(className) {
return !oldClasses.includes(className);
});
// Add new classes from the updated class attribute
domNodeClasses = domNodeClasses.concat(
newClasses.filter(function(className) {
return !domNodeClasses.includes(className);
})
);
this.engine.domNode.className = domNodeClasses.join(" ");
};
/* /*
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
@@ -217,15 +240,19 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
EditTextWidget.prototype.refresh = function(changedTiddlers) { EditTextWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes(); var changedAttributes = this.computeAttributes();
// Completely rerender if any of our attributes have changed // 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.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(); this.refreshSelf();
return true; return true;
} else if(changedTiddlers[this.editRefreshTitle]) { } else if(changedAttributes["default"] || changedTiddlers[this.editRefreshTitle]) {
this.editDefault = this.getAttribute("default");
this.engine.updateDomNodeText(this.getEditInfo().value); this.engine.updateDomNodeText(this.getEditInfo().value);
} else if(changedTiddlers[this.editTitle]) { } else if(changedTiddlers[this.editTitle]) {
var editInfo = this.getEditInfo(); var editInfo = this.getEditInfo();
this.updateEditor(editInfo.value,editInfo.type); this.updateEditor(editInfo.value,editInfo.type);
} }
if(changedAttributes["class"]) {
this.updateDomNodeClasses();
}
this.engine.fixHeight(); this.engine.fixHeight();
if(this.editShowToolbar) { if(this.editShowToolbar) {
return this.refreshChildren(changedTiddlers); return this.refreshChildren(changedTiddlers);
@@ -13,14 +13,15 @@ Export our filter prefix function
exports.intersection = function(operationSubFunction) { exports.intersection = function(operationSubFunction) {
return function(results,source,widget) { return function(results,source,widget) {
if(results.length !== 0) { if(results.length !== 0) {
var secondRunResults = operationSubFunction(source,widget); const secondRunResults = operationSubFunction(source,widget),
var firstRunResults = results.toArray(); secondRunSet = new Set(secondRunResults),
firstRunResults = results.toArray();
results.clear(); results.clear();
$tw.utils.each(firstRunResults,function(title) { firstRunResults.forEach((title) => {
if(secondRunResults.indexOf(title) !== -1) { if(secondRunSet.has(title)) {
results.push(title); results.push(title);
} }
}); });
} }
}; };
}; };
+15 -20
View File
@@ -13,33 +13,28 @@ Export our filter prefix function
exports.sort = function(operationSubFunction,options) { exports.sort = function(operationSubFunction,options) {
return function(results,source,widget) { return function(results,source,widget) {
if(results.length > 0) { if(results.length > 0) {
var suffixes = options.suffixes, const suffixes = options.suffixes,
sortType = (suffixes[0] && suffixes[0][0]) ? suffixes[0][0] : "string", sortType = (suffixes[0] && suffixes[0][0]) ? suffixes[0][0] : "string",
invert = suffixes[1] ? (suffixes[1].indexOf("reverse") !== -1) : false, invert = suffixes[1] ? suffixes[1].includes("reverse") : false,
isCaseSensitive = suffixes[1] ? (suffixes[1].indexOf("casesensitive") !== -1) : false, isCaseSensitive = suffixes[1] ? suffixes[1].includes("casesensitive") : false,
inputTitles = results.toArray(), inputTitles = results.toArray(),
sortKeys = [], sortKeys = [],
indexes = new Array(inputTitles.length), compareFn = $tw.utils.makeCompareFunction(sortType,{defaultType: "string", invert:invert, isCaseSensitive:isCaseSensitive});
compareFn; results.each((title) => {
results.each(function(title) { const key = operationSubFunction(
var key = operationSubFunction(options.wiki.makeTiddlerIterator([title]),widget.makeFakeWidgetWithVariables({ options.wiki.makeTiddlerIterator([title]),
"currentTiddler": "" + title, widget.makeFakeWidgetWithVariables({
"..currentTiddler": widget.getVariable("currentTiddler",{defaultValue:""}) "currentTiddler": "" + title,
})); "..currentTiddler": widget.getVariable("currentTiddler",{defaultValue:""})
})
);
sortKeys.push(key[0] || ""); sortKeys.push(key[0] || "");
}); });
results.clear(); results.clear();
// Prepare an array of indexes to sort // Prepare an array of indexes to sort
for(var t=0; t<inputTitles.length; t++) { let indexes = Array.from(inputTitles.keys());
indexes[t] = t; indexes.sort((a,b) => compareFn(sortKeys[a],sortKeys[b]));
} indexes.forEach((index) => {
// Sort the indexes
compareFn = $tw.utils.makeCompareFunction(sortType,{defaultType: "string", invert:invert, isCaseSensitive:isCaseSensitive});
indexes = indexes.sort(function(a,b) {
return compareFn(sortKeys[a],sortKeys[b]);
});
// Add to results in correct order
$tw.utils.each(indexes,function(index) {
results.push(inputTitles[index]); results.push(inputTitles[index]);
}); });
} }
+88 -46
View File
@@ -146,14 +146,16 @@ exports.parseFilter = function(filterString) {
match; match;
var whitespaceRegExp = /(\s+)/mg, var whitespaceRegExp = /(\s+)/mg,
// Groups: // Groups:
// 1 - entire filter run prefix // 1 - pragma
// 2 - filter run prefix itself // 2 - pragma suffix
// 3 - filter run prefix suffixes // 3 - entire filter run prefix
// 4 - opening square bracket following filter run prefix // 4 - filter run prefix name
// 5 - double quoted string following filter run prefix // 5 - filter run prefix suffixes
// 6 - single quoted string following filter run prefix // 6 - opening square bracket following filter run prefix
// 7 - anything except for whitespace and square brackets // 7 - double quoted string following filter run prefix
operandRegExp = /((?:\+|\-|~|(?:=>?)|\:(\w+)(?:\:([\w\:, ]*))?)?)(?:(\[)|(?:"([^"]*)")|(?:'([^']*)')|([^\s\[\]]+))/mg; // 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) { while(p < filterString.length) {
// Skip any whitespace // Skip any whitespace
whitespaceRegExp.lastIndex = p; whitespaceRegExp.lastIndex = p;
@@ -170,18 +172,23 @@ exports.parseFilter = function(filterString) {
}; };
match = operandRegExp.exec(filterString); match = operandRegExp.exec(filterString);
if(match && match.index === p) { if(match && match.index === p) {
// If there is a filter run prefix
if(match[1]) { 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; p = p + operation.prefix.length;
// Name for named prefixes // Name for named prefixes
if(match[2]) { if(match[4]) {
operation.namedPrefix = match[2]; operation.namedPrefix = match[4];
} }
// Suffixes for filter run prefix // Suffixes for filter run prefix
if(match[3]) { if(match[5]) {
operation.suffixes = []; operation.suffixes = [];
$tw.utils.each(match[3].split(":"),function(subsuffix) { $tw.utils.each(match[5].split(":"),function(subsuffix) {
operation.suffixes.push([]); operation.suffixes.push([]);
$tw.utils.each(subsuffix.split(","),function(entry) { $tw.utils.each(subsuffix.split(","),function(entry) {
entry = $tw.utils.trim(entry); entry = $tw.utils.trim(entry);
@@ -193,7 +200,7 @@ exports.parseFilter = function(filterString) {
} }
} }
// Opening square bracket // Opening square bracket
if(match[4]) { if(match[6]) {
p = parseFilterOperation(operation.operators,filterString,p); p = parseFilterOperation(operation.operators,filterString,p);
} else { } else {
p = match.index + match[0].length; p = match.index + match[0].length;
@@ -203,9 +210,9 @@ exports.parseFilter = function(filterString) {
p = parseFilterOperation(operation.operators,filterString,p); p = parseFilterOperation(operation.operators,filterString,p);
} }
// Quoted strings and unquoted title // 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( 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); results.push(operation);
@@ -230,23 +237,26 @@ exports.getFilterRunPrefixes = function() {
return this.filterRunPrefixes; return this.filterRunPrefixes;
}; };
exports.filterTiddlers = function(filterString,widget,source) { exports.filterTiddlers = function(filterString,widget,source,options) {
var fn = this.compileFilter(filterString); var fn = this.compileFilter(filterString,options);
return fn.call(this,source,widget); return fn.call(this,source,widget);
}; };
/* /*
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) 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. 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",
seenNonPragmaOperation = false;
var cacheKey = filterString + "|" + defaultFilterRunPrefix;
if(!this.filterCache) { if(!this.filterCache) {
this.filterCache = Object.create(null); this.filterCache = Object.create(null);
this.filterCacheCount = 0; this.filterCacheCount = 0;
} }
if(this.filterCache[filterString] !== undefined) { if(this.filterCache[cacheKey] !== undefined) {
return this.filterCache[filterString]; return this.filterCache[cacheKey];
} }
var filterParseTree; var filterParseTree;
try { try {
@@ -296,7 +306,7 @@ exports.compileFilter = function(filterString) {
var varTree = $tw.utils.parseFilterVariable(operand.text); var varTree = $tw.utils.parseFilterVariable(operand.text);
var resultList = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source}); var resultList = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source});
if((resultList.length > 0 && resultList[0] !== undefined) || resultList.length === 0) { if((resultList.length > 0 && resultList[0] !== undefined) || resultList.length === 0) {
operand.multiValue = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source}) || []; operand.multiValue = resultList || [];
operand.value = operand.multiValue[0] || ""; operand.value = operand.multiValue[0] || "";
} else { } else {
operand.value = ""; operand.value = "";
@@ -325,7 +335,8 @@ exports.compileFilter = function(filterString) {
regexp: operator.regexp regexp: operator.regexp
},{ },{
wiki: self, wiki: self,
widget: widget widget: widget,
defaultFilterRunPrefix: defaultFilterRunPrefix
}); });
if($tw.utils.isArray(results)) { if($tw.utils.isArray(results)) {
accumulator = self.makeTiddlerIterator(results); accumulator = self.makeTiddlerIterator(results);
@@ -346,29 +357,60 @@ exports.compileFilter = function(filterString) {
var filterRunPrefixes = self.getFilterRunPrefixes(); var filterRunPrefixes = self.getFilterRunPrefixes();
// Wrap the operator functions in a wrapper function that depends on the prefix // Wrap the operator functions in a wrapper function that depends on the prefix
operationFunctions.push((function() { operationFunctions.push((function() {
var options = {wiki: self, suffixes: operation.suffixes || []}; if(operation.pragma) {
switch(operation.prefix || "") { // Pragmas are only allowed at the start of a filter, before any runs
case "": // No prefix means that the operation is unioned into the result if(seenNonPragmaOperation) {
return filterRunPrefixes["or"](operationSubFunction, options); return function(results,source,widget) {
case "=": // The results of the operation are pushed into the result without deduplication results.clear();
return filterRunPrefixes["all"](operationSubFunction, options); results.push($tw.language.getString("Error/FilterPragmaPosition"));
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 switch(operation.pragma) {
return filterRunPrefixes["and"](operationSubFunction, options); case "defaultprefix":
case "~": // This operation is unioned into the result only if the main result so far is empty defaultFilterRunPrefix = operation.suffix || "or";
return filterRunPrefixes["else"](operationSubFunction, options); break;
case "=>": // This operation is applied to the main results so far, and the results are assigned to a variable default:
return filterRunPrefixes["let"](operationSubFunction, options);
default:
if(operation.namedPrefix && filterRunPrefixes[operation.namedPrefix]) {
return filterRunPrefixes[operation.namedPrefix](operationSubFunction, options);
} else {
return function(results,source,widget) { return function(results,source,widget) {
results.clear(); results.clear();
results.push($tw.language.getString("Error/FilterRunPrefix")); results.push($tw.language.getString("Error/FilterPragma"));
}; };
} }
return function(results,source,widget) {
// Dummy response
};
} else {
seenNonPragmaOperation = true;
var options = {wiki: self, suffixes: operation.suffixes || []};
switch(operation.prefix || "") {
case "": // Use the default filter run prefix if none is specified
if(filterRunPrefixes[defaultFilterRunPrefix]) {
return filterRunPrefixes[defaultFilterRunPrefix](operationSubFunction, options);
} else {
return function(results,source,widget) {
results.clear();
results.push($tw.language.getString("Error/FilterRunPrefix"));
};
}
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 +449,7 @@ exports.compileFilter = function(filterString) {
this.filterCache = Object.create(null); this.filterCache = Object.create(null);
this.filterCacheCount = 0; this.filterCacheCount = 0;
} }
this.filterCache[filterString] = fnMeasured; this.filterCache[cacheKey] = fnMeasured;
this.filterCacheCount++; this.filterCacheCount++;
return fnMeasured; return fnMeasured;
}; };
+21
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;
};
+158
View File
@@ -0,0 +1,158 @@
/*\
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];
// Hue is null for achromatic colours
if(colour === null || colour === undefined || Number.isNaN(colour)) {
return "";
}
}
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-composite"] = makeSerialColourOperator(function (colour, operator, options) {
// Composite each translucent input colour over the backdrop given in the operand
var backdrop = $tw.utils.parseCSSColorObject(operator.operand);
if(backdrop && colour.alpha < 1) {
var alpha = colour.alpha;
colour.alpha = 1;
colour = backdrop.mix(colour,alpha,{space: "srgb"});
}
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];
if(!targetColour) {
return [];
}
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);
};
}
+3 -1
View File
@@ -13,7 +13,9 @@ Filter operator returning those input titles that pass a subfilter
Export our filter function Export our filter function
*/ */
exports.filter = function(source,operator,options) { exports.filter = function(source,operator,options) {
var filterFn = options.wiki.compileFilter(operator.operand), var suffixes = operator.suffixes || [],
defaultFilterRunPrefix = (suffixes[0] && suffixes[0][0]) || options.defaultFilterRunPrefix || "or",
filterFn = options.wiki.compileFilter(operator.operand,{defaultFilterRunPrefix}),
results = [], results = [],
target = operator.prefix !== "!"; target = operator.prefix !== "!";
source(function(tiddler,title) { source(function(tiddler,title) {
+30 -54
View File
@@ -9,69 +9,56 @@ Filter operators for manipulating the current selection list
"use strict"; "use strict";
/*
Fetch titles from the current list
*/
const prepare_results = (source) => {
const results = [];
source((tiddler,title) => results.push(title));
return results;
};
/* /*
Order a list Order a list
*/ */
exports.order = function(source,operator,options) { exports.order = function(source,operator,options) {
var results = []; const results = prepare_results(source);
if(operator.operand.toLowerCase() === "reverse") { return operator.operand.toLowerCase() === "reverse" ?
source(function(tiddler,title) { results.reverse() :
results.unshift(title); results;
});
} else {
source(function(tiddler,title) {
results.push(title);
});
}
return results;
}; };
/* /*
Reverse list Reverse list
*/ */
exports.reverse = function(source,operator,options) { exports.reverse = function(source,operator,options) {
var results = []; return prepare_results(source).reverse();
source(function(tiddler,title) {
results.unshift(title);
});
return results;
}; };
/* /*
First entry/entries in list First entry/entries in list
*/ */
exports.first = function(source,operator,options) { exports.first = function(source,operator,options) {
var count = $tw.utils.getInt(operator.operand,1), const count = $tw.utils.getInt(operator.operand,1);
results = []; return prepare_results(source).slice(0,count);
source(function(tiddler,title) {
results.push(title);
});
return results.slice(0,count);
}; };
/* /*
Last entry/entries in list Last entry/entries in list
*/ */
exports.last = function(source,operator,options) { exports.last = function(source,operator,options) {
var count = $tw.utils.getInt(operator.operand,1), const count = $tw.utils.getInt(operator.operand,1);
results = []; return count === 0 ?
if(count === 0) return results; [] :
source(function(tiddler,title) { prepare_results(source).slice(-count);
results.push(title);
});
return results.slice(-count);
}; };
/* /*
All but the first entry/entries of the list All but the first entry/entries of the list
*/ */
exports.rest = function(source,operator,options) { exports.rest = function(source,operator,options) {
var count = $tw.utils.getInt(operator.operand,1), const count = $tw.utils.getInt(operator.operand,1);
results = []; return prepare_results(source).slice(count);
source(function(tiddler,title) {
results.push(title);
});
return results.slice(count);
}; };
exports.butfirst = exports.rest; exports.butfirst = exports.rest;
exports.bf = exports.rest; exports.bf = exports.rest;
@@ -80,12 +67,9 @@ exports.bf = exports.rest;
All but the last entry/entries of the list All but the last entry/entries of the list
*/ */
exports.butlast = function(source,operator,options) { exports.butlast = function(source,operator,options) {
var count = $tw.utils.getInt(operator.operand,1), const count = $tw.utils.getInt(operator.operand,1),
results = []; results = prepare_results(source),
source(function(tiddler,title) { index = count === 0 ? results.length : -count;
results.push(title);
});
var index = count === 0 ? results.length : -count;
return results.slice(0,index); return results.slice(0,index);
}; };
exports.bl = exports.butlast; exports.bl = exports.butlast;
@@ -94,22 +78,14 @@ exports.bl = exports.butlast;
The nth member of the list The nth member of the list
*/ */
exports.nth = function(source,operator,options) { exports.nth = function(source,operator,options) {
var count = $tw.utils.getInt(operator.operand,1), const count = $tw.utils.getInt(operator.operand,1);
results = []; return prepare_results(source).slice(count - 1,count);
source(function(tiddler,title) {
results.push(title);
});
return results.slice(count - 1,count);
}; };
/* /*
The zero based nth member of the list The zero based nth member of the list
*/ */
exports.zth = function(source,operator,options) { exports.zth = function(source,operator,options) {
var count = $tw.utils.getInt(operator.operand,0), const count = $tw.utils.getInt(operator.operand,0);
results = []; return prepare_results(source).slice(count,count + 1);
source(function(tiddler,title) { };
results.push(title);
});
return results.slice(count,count + 1);
};
+17 -32
View File
@@ -13,37 +13,22 @@ Filter operator for checking if a title starts with a prefix
Export our filter function Export our filter function
*/ */
exports.prefix = function(source,operator,options) { exports.prefix = function(source,operator,options) {
var results = [], const results = [],
suffixes = (operator.suffixes || [])[0] || []; suffixes = (operator.suffixes || [])[0] || [],
if(suffixes.indexOf("caseinsensitive") !== -1) { caseInsensitive = suffixes.includes("caseinsensitive"),
var operand = operator.operand.toLowerCase(); negate = operator.prefix === "!";
if(operator.prefix === "!") {
source(function(tiddler,title) { const operand = caseInsensitive ?
if(title.toLowerCase().substr(0,operand.length) !== operand) { operator.operand.toLowerCase() :
results.push(title); operator.operand;
}
}); source((tiddler,title) => {
} else { const value = caseInsensitive ? title.toLowerCase() : title,
source(function(tiddler,title) { matches = value.startsWith(operand);
if(title.toLowerCase().substr(0,operand.length) === operand) { if(negate ? !matches : matches) {
results.push(title); results.push(title);
}
});
} }
} else { });
if(operator.prefix === "!") {
source(function(tiddler,title) {
if(title.substr(0,operator.operand.length) !== operator.operand) {
results.push(title);
}
});
} else {
source(function(tiddler,title) {
if(title.substr(0,operator.operand.length) === operator.operand) {
results.push(title);
}
});
}
}
return results; return results;
}; };
+7 -12
View File
@@ -14,13 +14,13 @@ Export our filter function
*/ */
exports.sortsub = function(source,operator,options) { exports.sortsub = function(source,operator,options) {
// Compile the subfilter // Compile the subfilter
var filterFn = options.wiki.compileFilter(operator.operand); let filterFn = options.wiki.compileFilter(operator.operand);
// Collect the input titles and the corresponding sort keys // Collect the input titles and the corresponding sort keys
var inputTitles = [], let inputTitles = [],
sortKeys = []; sortKeys = [];
source(function(tiddler,title) { source(function(tiddler,title) {
inputTitles.push(title); inputTitles.push(title);
var r = filterFn.call(options.wiki,function(iterator) { let r = filterFn.call(options.wiki,function(iterator) {
iterator(options.wiki.getTiddler(title),title); iterator(options.wiki.getTiddler(title),title);
},options.widget.makeFakeWidgetWithVariables({ },options.widget.makeFakeWidgetWithVariables({
"currentTiddler": "" + title, "currentTiddler": "" + title,
@@ -29,17 +29,12 @@ exports.sortsub = function(source,operator,options) {
sortKeys.push(r[0] || ""); sortKeys.push(r[0] || "");
}); });
// Rather than sorting the titles array, we'll sort the indexes so that we can consult both arrays // Rather than sorting the titles array, we'll sort the indexes so that we can consult both arrays
var indexes = new Array(inputTitles.length); let indexes = Array.from(inputTitles.keys());
for(var t=0; t<inputTitles.length; t++) {
indexes[t] = t;
}
// Sort the indexes // Sort the indexes
var compareFn = $tw.utils.makeCompareFunction(operator.suffix,{defaultType: "string",invert: operator.prefix === "!"}); let compareFn = $tw.utils.makeCompareFunction(operator.suffix,{defaultType: "string",invert: operator.prefix === "!"});
indexes = indexes.sort(function(a,b) { indexes = indexes.sort((a,b) => compareFn(sortKeys[a],sortKeys[b]));
return compareFn(sortKeys[a],sortKeys[b]);
});
// Make the results array in order // Make the results array in order
var results = []; let results = [];
$tw.utils.each(indexes,function(index) { $tw.utils.each(indexes,function(index) {
results.push(inputTitles[index]); results.push(inputTitles[index]);
}); });
+26 -13
View File
@@ -91,22 +91,35 @@ function diffLineWordMode(text1,text2,mode) {
return diffs; return diffs;
} }
exports.makepatches = function(source,operator,options) { exports.makepatches = function(source, operator, options) {
var suffix = operator.suffix || "", const suffixes = operator.suffixes || [],
result = []; [modeArg = [], formatArg = []] = suffixes,
modeSuffix = modeArg[0] || operator.suffix || "",
source(function(tiddler,title) { mode = ["lines", "words"].includes(modeSuffix) ? modeSuffix : "",
let diffs, patches; isJson = formatArg[0] === "json",
if(suffix === "lines" || suffix === "words") { results = [];
diffs = diffLineWordMode(title,operator.operand,suffix);
patches = dmp.patchMake(title,diffs); source((tiddler, title) => {
if (isJson) {
const diffs = (mode === "lines" || mode === "words")
? diffLineWordMode(title, operator.operand, mode)
: dmp.diffMain(title, operator.operand);
const jsonOutput = diffs.map(([typeCode, text]) => ({
type: typeCode === 1 ? "insert" : (typeCode === -1 ? "delete" : "equal"),
text
}));
results.push(JSON.stringify(jsonOutput));
} else { } else {
patches = dmp.patchMake(title,operator.operand); const patches = (mode === "lines" || mode === "words")
? dmp.patchMake(title, diffLineWordMode(title, operator.operand, mode))
: dmp.patchMake(title, operator.operand);
results.push(dmp.patchToText(patches));
} }
Array.prototype.push.apply(result,[dmp.patchToText(patches)]);
}); });
return result; return results;
}; };
exports.applypatches = makeStringBinaryOperator( exports.applypatches = makeStringBinaryOperator(
@@ -235,4 +248,4 @@ exports.charcode = function(source,operator,options) {
} }
}); });
return [chars.join("")]; return [chars.join("")];
}; };
+7 -4
View File
@@ -13,11 +13,14 @@ Filter operator returning its operand evaluated as a filter
Export our filter function Export our filter function
*/ */
exports.subfilter = function(source,operator,options) { exports.subfilter = function(source,operator,options) {
var list = options.wiki.filterTiddlers(operator.operand,options.widget,source); var suffixes = operator.suffixes || [],
defaultFilterRunPrefix = (suffixes[0] && suffixes[0][0]) || options.defaultFilterRunPrefix || "or";
var list = options.wiki.filterTiddlers(operator.operand,options.widget,source,{defaultFilterRunPrefix});
if(operator.prefix === "!") { if(operator.prefix === "!") {
var results = []; const results = [],
source(function(tiddler,title) { listSet = new Set(list);
if(list.indexOf(title) === -1) { source((tiddler,title) => {
if(!listSet.has(title)) {
results.push(title); results.push(title);
} }
}); });
+17 -31
View File
@@ -13,41 +13,27 @@ Filter operator for checking if a title ends with a suffix
Export our filter function Export our filter function
*/ */
exports.suffix = function(source,operator,options) { exports.suffix = function(source,operator,options) {
var results = [], const results = [],
suffixes = (operator.suffixes || [])[0] || []; suffixes = (operator.suffixes || [])[0] || [];
if(!operator.operand) { if(!operator.operand) {
source(function(tiddler,title) { source((tiddler,title) => {
results.push(title); results.push(title);
}); });
} else if(suffixes.indexOf("caseinsensitive") !== -1) { return results;
var operand = operator.operand.toLowerCase();
if(operator.prefix === "!") {
source(function(tiddler,title) {
if(title.toLowerCase().substr(-operand.length) !== operand) {
results.push(title);
}
});
} else {
source(function(tiddler,title) {
if(title.toLowerCase().substr(-operand.length) === operand) {
results.push(title);
}
});
}
} else {
if(operator.prefix === "!") {
source(function(tiddler,title) {
if(title.substr(-operator.operand.length) !== operator.operand) {
results.push(title);
}
});
} else {
source(function(tiddler,title) {
if(title.substr(-operator.operand.length) === operator.operand) {
results.push(title);
}
});
}
} }
const caseInsensitive = suffixes.indexOf("caseinsensitive") !== -1,
negate = operator.prefix === "!",
operand = caseInsensitive ? operator.operand.toLowerCase() : operator.operand;
source((tiddler,title) => {
const value = caseInsensitive ? title.toLowerCase() : title,
matches = value.endsWith(operand);
if(negate ? !matches : matches) {
results.push(title);
}
});
return results; return results;
}; };
+4 -5
View File
@@ -22,12 +22,11 @@ exports.tag = function(source,operator,options) {
}); });
} else { } else {
// Old semantics: // Old semantics:
var tiddlers;
if(operator.prefix === "!") { if(operator.prefix === "!") {
// Returns a copy of the input if operator.operand is missing // Returns a copy of the input if operator.operand is missing
tiddlers = options.wiki.getTiddlersWithTag(operator.operand); const excludeTagSet = new Set(options.wiki.getTiddlersWithTag(operator.operand));
source(function(tiddler,title) { source(function(tiddler,title) {
if(tiddlers.indexOf(title) === -1) { if(!excludeTagSet.has(title)) {
results.push(title); results.push(title);
} }
}); });
@@ -39,9 +38,9 @@ exports.tag = function(source,operator,options) {
return indexedResults; return indexedResults;
} }
} else { } else {
tiddlers = options.wiki.getTiddlersWithTag(operator.operand); const includeTagSet = new Set(options.wiki.getTiddlersWithTag(operator.operand));
source(function(tiddler,title) { source(function(tiddler,title) {
if(tiddlers.indexOf(title) !== -1) { if(includeTagSet.has(title)) {
results.push(title); results.push(title);
} }
}); });
+76 -81
View File
@@ -11,20 +11,18 @@ Extended filter operators to manipulate the current list.
/* /*
Fetch titles from the current list Fetch titles from the current list
*/ */
var prepare_results = function (source) { const prepare_results = (source) => {
var results = []; const results = [];
source(function (tiddler, title) { source((tiddler,title) => results.push(title));
results.push(title);
});
return results; return results;
}; };
/* /*
Moves a number of items from the tail of the current list before the item named in the operand Moves a number of items from the tail of the current list before the item named in the operand
*/ */
exports.putbefore = function (source, operator) { exports.putbefore = function(source,operator) {
var results = prepare_results(source), const results = prepare_results(source),
index = results.indexOf(operator.operand), index = results.indexOf(operator.operand),
count = $tw.utils.getInt(operator.suffix,1); count = $tw.utils.getInt(operator.suffix,1);
return (index === -1) ? return (index === -1) ?
@@ -34,9 +32,9 @@ exports.putbefore = function (source, operator) {
/* /*
Moves a number of items from the tail of the current list after the item named in the operand Moves a number of items from the tail of the current list after the item named in the operand
*/ */
exports.putafter = function (source, operator) { exports.putafter = function(source,operator) {
var results = prepare_results(source), const results = prepare_results(source),
index = results.indexOf(operator.operand), index = results.indexOf(operator.operand),
count = $tw.utils.getInt(operator.suffix,1); count = $tw.utils.getInt(operator.suffix,1);
return (index === -1) ? return (index === -1) ?
@@ -46,9 +44,9 @@ exports.putafter = function (source, operator) {
/* /*
Replaces the item named in the operand with a number of items from the tail of the current list Replaces the item named in the operand with a number of items from the tail of the current list
*/ */
exports.replace = function (source, operator) { exports.replace = function(source,operator) {
var results = prepare_results(source), const results = prepare_results(source),
index = results.indexOf(operator.operand), index = results.indexOf(operator.operand),
count = $tw.utils.getInt(operator.suffix,1); count = $tw.utils.getInt(operator.suffix,1);
return (index === -1) ? return (index === -1) ?
@@ -58,39 +56,39 @@ exports.replace = function (source, operator) {
/* /*
Moves a number of items from the tail of the current list to the head of the list Moves a number of items from the tail of the current list to the head of the list
*/ */
exports.putfirst = function (source, operator) { exports.putfirst = function(source,operator) {
var results = prepare_results(source), const results = prepare_results(source),
count = $tw.utils.getInt(operator.suffix,1); count = $tw.utils.getInt(operator.suffix,1);
return results.slice(-count).concat(results.slice(0, -count)); return [...results.slice(-count), ...results.slice(0, -count)];
}; };
/* /*
Moves a number of items from the head of the current list to the tail of the list Moves a number of items from the head of the current list to the tail of the list
*/ */
exports.putlast = function (source, operator) { exports.putlast = function(source,operator) {
var results = prepare_results(source), const results = prepare_results(source),
count = $tw.utils.getInt(operator.suffix,1); count = $tw.utils.getInt(operator.suffix,1);
return results.slice(count).concat(results.slice(0, count)); return [...results.slice(count), ...results.slice(0, count)];
}; };
/* /*
Moves the item named in the operand a number of places forward or backward in the list Moves the item named in the operand a number of places forward or backward in the list
*/ */
exports.move = function (source, operator) { exports.move = function(source,operator) {
var results = prepare_results(source), const results = prepare_results(source),
index = results.indexOf(operator.operand), index = results.indexOf(operator.operand),
count = $tw.utils.getInt(operator.suffix,1), count = $tw.utils.getInt(operator.suffix,1),
marker = results.splice(index, 1), marker = results.splice(index, 1),
offset = (index + count) > 0 ? index + count : 0; offset = (index + count) > 0 ? index + count : 0;
return results.slice(0, offset).concat(marker).concat(results.slice(offset)); return results.slice(0, offset).concat(marker).concat(results.slice(offset));
}; };
/* /*
Returns the items from the current list that are after the item named in the operand Returns the items from the current list that are after the item named in the operand
*/ */
exports.allafter = function (source, operator) { exports.allafter = function(source,operator) {
var results = prepare_results(source), const results = prepare_results(source),
index = results.indexOf(operator.operand); index = results.indexOf(operator.operand);
return (index === -1) ? [] : return (index === -1) ? [] :
(operator.suffix) ? results.slice(index) : (operator.suffix) ? results.slice(index) :
@@ -99,9 +97,9 @@ exports.allafter = function (source, operator) {
/* /*
Returns the items from the current list that are before the item named in the operand Returns the items from the current list that are before the item named in the operand
*/ */
exports.allbefore = function (source, operator) { exports.allbefore = function(source,operator) {
var results = prepare_results(source), const results = prepare_results(source),
index = results.indexOf(operator.operand); index = results.indexOf(operator.operand);
return (index === -1) ? [] : return (index === -1) ? [] :
(operator.suffix) ? results.slice(0, index + 1) : (operator.suffix) ? results.slice(0, index + 1) :
@@ -110,9 +108,9 @@ exports.allbefore = function (source, operator) {
/* /*
Appends the items listed in the operand array to the tail of the current list Appends the items listed in the operand array to the tail of the current list
*/ */
exports.append = function (source, operator) { exports.append = function(source,operator) {
var append = $tw.utils.parseStringArray(operator.operand, "true"), const append = $tw.utils.parseStringArray(operator.operand,"true"),
results = prepare_results(source), results = prepare_results(source),
count = parseInt(operator.suffix) || append.length; count = parseInt(operator.suffix) || append.length;
return (append.length === 0) ? results : return (append.length === 0) ? results :
@@ -122,9 +120,9 @@ exports.append = function (source, operator) {
/* /*
Prepends the items listed in the operand array to the head of the current list Prepends the items listed in the operand array to the head of the current list
*/ */
exports.prepend = function (source, operator) { exports.prepend = function(source,operator) {
var prepend = $tw.utils.parseStringArray(operator.operand, "true"), const prepend = $tw.utils.parseStringArray(operator.operand,"true"),
results = prepare_results(source), results = prepare_results(source),
count = $tw.utils.getInt(operator.suffix,prepend.length); count = $tw.utils.getInt(operator.suffix,prepend.length);
return (prepend.length === 0) ? results : return (prepend.length === 0) ? results :
@@ -134,21 +132,19 @@ exports.prepend = function (source, operator) {
/* /*
Returns all items from the current list except the items listed in the operand array Returns all items from the current list except the items listed in the operand array
*/ */
exports.remove = function (source, operator) { exports.remove = function(source, operator) {
var array = $tw.utils.parseStringArray(operator.operand, "true"), const array = $tw.utils.parseStringArray(operator.operand, "true"),
results = prepare_results(source), results = prepare_results(source);
count = parseInt(operator.suffix) || array.length,
p, if(array.length === 0) {
len, return results;
index; }
len = array.length - 1; const count = parseInt(operator.suffix, 10) || array.length,
for(p = 0; p < count; ++p) { targetItems = operator.prefix ? array.slice(-count).reverse() : array.slice(0, count);
if(operator.prefix) {
index = results.indexOf(array[len - p]); for(const item of targetItems) {
} else { const index = results.indexOf(item);
index = results.indexOf(array[p]);
}
if(index !== -1) { if(index !== -1) {
results.splice(index, 1); results.splice(index, 1);
} }
@@ -158,40 +154,38 @@ exports.remove = function (source, operator) {
/* /*
Returns all items from the current list sorted in the order of the items in the operand array Returns all items from the current list sorted in the order of the items in the operand array
*/ */
exports.sortby = function (source, operator) { exports.sortby = function(source,operator) {
var results = prepare_results(source); const results = prepare_results(source);
if(!results || results.length < 2) { if(!results || results.length < 2) {
return results; return results;
} }
var lookup = $tw.utils.parseStringArray(operator.operand, "true"); const lookup = $tw.utils.parseStringArray(operator.operand,"true"),
results.sort(function (a, b) { // The "end" suffix places unlisted titles last, the default is first
return lookup.indexOf(a) - lookup.indexOf(b); unlisted = operator.suffix === "end" ? lookup.length : -1,
}); position = (title) => {
return results; const index = lookup.indexOf(title);
return index === -1 ? unlisted : index;
};
return results.sort((a,b) => position(a) - position(b));
}; };
/* /*
Removes all duplicate items from the current list Removes all duplicate items from the current list
*/ */
exports.unique = function (source, operator) { exports.unique = function(source, operator) {
var results = prepare_results(source); return Array.from(new Set(prepare_results(source)));
var set = results.reduce(function (a, b) {
if(a.indexOf(b) < 0) {
a.push(b);
}
return a;
}, []);
return set;
}; };
var cycleValueInArray = function(results,operands,stepSize) { const cycleValueInArray = function(results,operands,stepSize) {
var resultsIndex, let resultsIndex,
step = stepSize || 1, step = stepSize || 1,
i = 0, i = 0,
opLength = operands.length,
nextOperandIndex; nextOperandIndex;
for(i; i < opLength; i++) { const opLength = operands.length;
for(; i < opLength; i++) {
resultsIndex = results.indexOf(operands[i]); resultsIndex = results.indexOf(operands[i]);
if(resultsIndex !== -1) { if(resultsIndex !== -1) {
break; break;
@@ -213,18 +207,19 @@ var cycleValueInArray = function(results,operands,stepSize) {
/* /*
Toggles an item in the current list. Toggles an item in the current list.
*/ */
exports.toggle = function(source,operator) { exports.toggle = function(source,operator) {
return cycleValueInArray(prepare_results(source),operator.operands); return cycleValueInArray(prepare_results(source),operator.operands);
}; };
exports.cycle = function(source,operator) { exports.cycle = function(source,operator) {
var results = prepare_results(source), const results = prepare_results(source),
operands = (operator.operand.length ? $tw.utils.parseStringArray(operator.operand, "true") : [""]), operands = operator.operand.length ? $tw.utils.parseStringArray(operator.operand,"true") : [""];
step = $tw.utils.getInt(operator.operands[1]||"",1); let step = $tw.utils.getInt(operator.operands[1] || "",1);
if(step < 0) { if(step < 0) {
operands.reverse(); operands.reverse();
step = Math.abs(step); step = Math.abs(step);
} }
return cycleValueInArray(results,operands,step); return cycleValueInArray(results,operands,step);
}; };
+3 -2
View File
@@ -88,10 +88,11 @@ BackSubIndexer.prototype.update = function(updateDescriptor) {
var newTargets = [], var newTargets = [],
oldTargets = [], oldTargets = [],
self = this; self = this;
if(updateDescriptor.old.exists) { // System tiddlers are never indexed as sources, matching the _init() scan
if(updateDescriptor.old.exists && !this.wiki.isSystemTiddler(updateDescriptor.old.tiddler.fields.title)) {
oldTargets = this._getTarget(updateDescriptor.old.tiddler); oldTargets = this._getTarget(updateDescriptor.old.tiddler);
} }
if(updateDescriptor.new.exists) { if(updateDescriptor.new.exists && !this.wiki.isSystemTiddler(updateDescriptor.new.tiddler.fields.title)) {
newTargets = this._getTarget(updateDescriptor.new.tiddler); newTargets = this._getTarget(updateDescriptor.new.tiddler);
} }
+12 -11
View File
@@ -26,25 +26,26 @@ exports.params = [
Run the macro Run the macro
*/ */
exports.run = function(target,fallbackTarget,colourA,colourB) { 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) { if(!rgbTarget) {
return colourA; return colourA;
} }
var rgbColourA = $tw.utils.parseCSSColor(colourA), var rgbColourA = $tw.utils.parseCSSColorObject(colourA),
rgbColourB = $tw.utils.parseCSSColor(colourB); rgbColourB = $tw.utils.parseCSSColorObject(colourB);
if(rgbColourA && !rgbColourB) { if(rgbColourA && !rgbColourB) {
return rgbColourA; return colourA;
} }
if(rgbColourB && !rgbColourA) { if(rgbColourB && !rgbColourA) {
return rgbColourB; return colourB;
} }
if(!rgbColourA && !rgbColourB) { if(!rgbColourA && !rgbColourB) {
// If neither colour is readable, return a crude inverse of the target // 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 aContrast = rgbColourA.contrast(rgbTarget,"DeltaPhi"),
var brightnessTarget = rgbTarget[0] * 0.299 + rgbTarget[1] * 0.587 + rgbTarget[2] * 0.114, bContrast = rgbColourB.contrast(rgbTarget,"DeltaPhi");
brightnessA = rgbColourA[0] * 0.299 + rgbColourA[1] * 0.587 + rgbColourA[2] * 0.114, return aContrast > bContrast ? colourA : colourB;
brightnessB = rgbColourB[0] * 0.299 + rgbColourB[1] * 0.587 + rgbColourB[2] * 0.114;
return Math.abs(brightnessTarget - brightnessA) > Math.abs(brightnessTarget - brightnessB) ? colourA : colourB;
}; };
+68 -56
View File
@@ -221,7 +221,7 @@ exports.parseMacroInvocationAsTransclusion = function(source,pos) {
orderedAttributes: [] orderedAttributes: []
}; };
// Define our regexps // Define our regexps
var reVarName = /([^\s>"'=:]+)/y; var reVarName = /([^\s>"'=]+)/y;
// Skip whitespace // Skip whitespace
pos = $tw.utils.skipWhiteSpace(source,pos); pos = $tw.utils.skipWhiteSpace(source,pos);
// Look for a double opening angle bracket // Look for a double opening angle bracket
@@ -269,7 +269,7 @@ exports.parseMVVReferenceAsTransclusion = function(source,pos) {
orderedAttributes: [] orderedAttributes: []
}; };
// Define our regexps // Define our regexps
var reVarName = /([^\s>"'=:)]+)/y; var reVarName = /([^\s>"'=)]+)/y;
// Skip whitespace // Skip whitespace
pos = $tw.utils.skipWhiteSpace(source,pos); pos = $tw.utils.skipWhiteSpace(source,pos);
// Look for a double opening parenthesis // Look for a double opening parenthesis
@@ -528,67 +528,79 @@ exports.parseAttribute = function(source,pos) {
pos = token.end; pos = token.end;
// Skip whitespace // Skip whitespace
pos = $tw.utils.skipWhiteSpace(source,pos); pos = $tw.utils.skipWhiteSpace(source,pos);
// Look for a string literal do {
var stringLiteral = $tw.utils.parseStringLiteral(source,pos); // Look for a string literal
if(stringLiteral) { var stringLiteral = $tw.utils.parseStringLiteral(source,pos);
pos = stringLiteral.end; if(stringLiteral) {
node.type = "string"; pos = stringLiteral.end;
node.value = stringLiteral.value; node.type = "string";
} else { node.value = stringLiteral.value;
break;
}
// Look for a filtered value // Look for a filtered value
var filteredValue = $tw.utils.parseTokenRegExp(source,pos,reFilteredValue); var filteredValue = $tw.utils.parseTokenRegExp(source,pos,reFilteredValue);
if(filteredValue) { if(filteredValue) {
pos = filteredValue.end; pos = filteredValue.end;
node.type = "filtered"; node.type = "filtered";
node.filter = filteredValue.match[1]; node.filter = filteredValue.match[1];
} else { break;
// Look for an indirect value
var indirectValue = $tw.utils.parseTokenRegExp(source,pos,reIndirectValue);
if(indirectValue) {
pos = indirectValue.end;
node.type = "indirect";
node.textReference = indirectValue.match[1];
} else {
// Look for a macro invocation value
var macroInvocation = $tw.utils.parseMacroInvocationAsTransclusion(source,pos);
if(macroInvocation) {
pos = macroInvocation.end;
node.type = "macro";
node.value = macroInvocation;
} else {
// Look for an MVV reference value
var mvvReference = $tw.utils.parseMVVReferenceAsTransclusion(source,pos);
if(mvvReference) {
pos = mvvReference.end;
node.type = "macro";
node.value = mvvReference;
node.isMVV = true;
} else {
var substitutedValue = $tw.utils.parseTokenRegExp(source,pos,reSubstitutedValue);
if(substitutedValue) {
pos = substitutedValue.end;
node.type = "substituted";
node.rawValue = substitutedValue.match[1] || substitutedValue.match[2];
} else {
// Look for a unquoted value
var unquotedValue = $tw.utils.parseTokenRegExp(source,pos,reUnquotedAttribute);
if(unquotedValue) {
pos = unquotedValue.end;
node.type = "string";
node.value = unquotedValue.match[1];
} else if(source.charAt(pos) === "<" && source.charAt(pos + 1) === "<" && source.indexOf(">>",pos) !== -1) {
// Value looks like a macro invocation (starts with << with a closing >> ahead) but does not parse as one. Return null so the enclosing tag fails to parse rather than silently binding the attribute to "true" and treating the remainder as further attributes (restores v5.3.8 behaviour)
return null;
} else {
node.type = "string";
node.value = "true";
}
}
}
}
}
} }
}
// Look for an indirect value
var indirectValue = $tw.utils.parseTokenRegExp(source,pos,reIndirectValue);
if(indirectValue) {
pos = indirectValue.end;
node.type = "indirect";
node.textReference = indirectValue.match[1];
break;
}
// Look for a macro invocation value
var macroInvocation = $tw.utils.parseMacroInvocationAsTransclusion(source,pos);
if(macroInvocation) {
pos = macroInvocation.end;
node.type = "macro";
node.value = macroInvocation;
break;
}
// Look for an MVV reference value
var mvvReference = $tw.utils.parseMVVReferenceAsTransclusion(source,pos);
if(mvvReference) {
pos = mvvReference.end;
node.type = "macro";
node.value = mvvReference;
node.isMVV = true;
break;
}
// Look for a substituted value
var substitutedValue = $tw.utils.parseTokenRegExp(source,pos,reSubstitutedValue);
if(substitutedValue) {
pos = substitutedValue.end;
node.type = "substituted";
node.rawValue = substitutedValue.match[1] || substitutedValue.match[2];
break;
}
// Look for a unquoted value
var unquotedValue = $tw.utils.parseTokenRegExp(source,pos,reUnquotedAttribute);
if(unquotedValue) {
pos = unquotedValue.end;
node.type = "string";
node.value = unquotedValue.match[1];
break;
}
if(source.charAt(pos) === "<" && source.charAt(pos + 1) === "<" && source.indexOf(">>",pos) !== -1) {
// Value looks like a macro invocation (starts with << with a closing >> ahead) but does not parse as one. Return null so the enclosing tag fails to parse rather than silently binding the attribute to "true" and treating the remainder as further attributes (restores v5.3.8 behaviour)
return null;
}
node.type = "string";
node.value = "true";
} while(false);
} else { } else {
// If there is no equals sign or colon, then this is an attribute with no value, defaulting to "true" // If there is no equals sign or colon, then this is an attribute with no value, defaulting to "true"
node.type = "string"; node.type = "string";
@@ -25,7 +25,7 @@ exports.init = function(parser) {
}; };
exports.parse = function() { exports.parse = function() {
var reEnd = /(\r?\n```$)/mg; var reEnd = /(^|\r?\n)```$/mg;
var languageStart = this.parser.pos + 3, var languageStart = this.parser.pos + 3,
languageEnd = languageStart + this.match[1].length; languageEnd = languageStart + this.match[1].length;
// Move past the match // Move past the match
@@ -31,13 +31,16 @@ exports.parse = function() {
reEnd.lastIndex = this.parser.pos; reEnd.lastIndex = this.parser.pos;
var match = reEnd.exec(this.parser.source), var match = reEnd.exec(this.parser.source),
text, text,
start = this.parser.pos; start = this.parser.pos,
textEnd;
// Process the text // Process the text
if(match) { if(match) {
text = this.parser.source.substring(this.parser.pos,match.index); text = this.parser.source.substring(this.parser.pos,match.index);
textEnd = match.index;
this.parser.pos = match.index + match[0].length; this.parser.pos = match.index + match[0].length;
} else { } else {
text = this.parser.source.substr(this.parser.pos); text = this.parser.source.substr(this.parser.pos);
textEnd = this.parser.sourceLength;
this.parser.pos = this.parser.sourceLength; this.parser.pos = this.parser.sourceLength;
} }
return [{ return [{
@@ -47,7 +50,7 @@ exports.parse = function() {
type: "text", type: "text",
text: text, text: text,
start: start, start: start,
end: this.parser.pos end: textEnd
}] }]
}]; }];
}; };
@@ -32,7 +32,8 @@ exports.parse = function() {
this.parser.pos = this.matchRegExp.lastIndex; this.parser.pos = this.matchRegExp.lastIndex;
// Create the link unless it is suppressed // Create the link unless it is suppressed
if(this.match[0].substr(0,1) === "~") { if(this.match[0].substr(0,1) === "~") {
return [{type: "text", text: this.match[0].substr(1), start: start, end: this.parser.pos}]; // Start after the suppressing "~" so the span matches the plain text
return [{type: "text", text: this.match[0].substr(1), start: start + 1, end: this.parser.pos}];
} else { } else {
return [{ return [{
type: "element", type: "element",
@@ -34,7 +34,7 @@ exports.parse = function() {
// Parse the filter terminated by a line break // Parse the filter terminated by a line break
var reMatch = /(.*)(?:$|\r?\n)/mg; var reMatch = /(.*)(?:$|\r?\n)/mg;
reMatch.lastIndex = this.parser.pos; reMatch.lastIndex = this.parser.pos;
var filterStart = this.parser.source; var filterStart = this.parser.pos;
var match = reMatch.exec(this.parser.source); var match = reMatch.exec(this.parser.source);
this.parser.pos = reMatch.lastIndex; this.parser.pos = reMatch.lastIndex;
// Parse tree nodes to return // Parse tree nodes to return
@@ -27,9 +27,11 @@ Parse the most recent match
*/ */
exports.parse = function() { exports.parse = function() {
// Get the details of the match // Get the details of the match
var linkText = this.match[0]; var linkText = this.match[0],
// Start after the suppressing "~" so the span matches the plain text
start = this.parser.pos + 1;
// Move past the wikilink // Move past the wikilink
this.parser.pos = this.matchRegExp.lastIndex; this.parser.pos = this.matchRegExp.lastIndex;
// Return the link without unwikilink character as plain text // Return the link without unwikilink character as plain text
return [{type: "text", text: linkText.substr(1)}]; return [{type: "text", text: linkText.substr(1), start: start, end: this.parser.pos}];
}; };
+29 -9
View File
@@ -4,7 +4,6 @@ type: application/javascript
module-type: startup module-type: startup
Browser message handling Browser message handling
\*/ \*/
"use strict"; "use strict";
@@ -18,6 +17,20 @@ exports.synchronous = true;
/* /*
Load a specified url as an iframe and call the callback when it is loaded. If the url is already loaded then the existing iframe instance is used Load a specified url as an iframe and call the callback when it is loaded. If the url is already loaded then the existing iframe instance is used
*/ */
// Ensure the callback fires once, whether by PLUGIN-LIBRARY-READY or onload (legacy)
function flushCallbacks(iframeInfo, err) {
if(iframeInfo.status !== "loaded") {
iframeInfo.status = err ? "error" : "loaded";
saveIFrameInfoTiddler(iframeInfo);
var cb;
while((cb = iframeInfo.callbacks.shift())) {
cb(err, iframeInfo);
}
}
}
function loadIFrame(url,callback) { function loadIFrame(url,callback) {
// Check if iframe already exists // Check if iframe already exists
var iframeInfo = $tw.browserMessaging.iframeInfoMap[url]; var iframeInfo = $tw.browserMessaging.iframeInfoMap[url];
@@ -30,7 +43,8 @@ function loadIFrame(url,callback) {
iframeInfo = { iframeInfo = {
url: url, url: url,
status: "loading", status: "loading",
domNode: iframe domNode: iframe,
callbacks: [callback]
}; };
$tw.browserMessaging.iframeInfoMap[url] = iframeInfo; $tw.browserMessaging.iframeInfoMap[url] = iframeInfo;
saveIFrameInfoTiddler(iframeInfo); saveIFrameInfoTiddler(iframeInfo);
@@ -38,19 +52,18 @@ function loadIFrame(url,callback) {
iframe.style.display = "none"; iframe.style.display = "none";
iframe.setAttribute("library","true"); iframe.setAttribute("library","true");
document.body.appendChild(iframe); document.body.appendChild(iframe);
// Set up onload
// Set up onload. Legacy fallback: if PLUGIN-LIBRARY-READY never arrives, onload triggers the flush later
iframe.onload = function() { iframe.onload = function() {
iframeInfo.status = "loaded"; flushCallbacks(iframeInfo);
saveIFrameInfoTiddler(iframeInfo);
callback(null,iframeInfo);
}; };
iframe.onerror = function() { iframe.onerror = function() {
callback("Cannot load iframe"); flushCallbacks(iframeInfo, "Cannot load iframe");
}; };
try { try {
iframe.src = url; iframe.src = url;
} catch(ex) { } catch(ex) {
callback(ex); flushCallbacks(iframeInfo, ex);
} }
} }
} }
@@ -60,7 +73,7 @@ Unload library iframe for given url
*/ */
function unloadIFrame(url){ function unloadIFrame(url){
var iframes = document.getElementsByTagName("iframe"); var iframes = document.getElementsByTagName("iframe");
for(var t=iframes.length-1; t--; t>=0) { for(var t = iframes.length - 1; t >= 0; t--) {
var iframe = iframes[t]; var iframe = iframes[t];
if(iframe.getAttribute("library") === "true" && if(iframe.getAttribute("library") === "true" &&
iframe.getAttribute("src") === url) { iframe.getAttribute("src") === url) {
@@ -147,6 +160,13 @@ exports.startup = function() {
// console.log("browser-messaging: Received message from",event.origin); // console.log("browser-messaging: Received message from",event.origin);
// console.log("browser-messaging: Message content",event.data); // console.log("browser-messaging: Message content",event.data);
switch(event.data.verb) { switch(event.data.verb) {
case "PLUGIN-LIBRARY-READY":
$tw.utils.each($tw.browserMessaging.iframeInfoMap, function(info) {
if(info && info.domNode && info.domNode.contentWindow === event.source) {
flushCallbacks(info);
}
});
break;
case "GET-RESPONSE": case "GET-RESPONSE":
if(event.data.status.charAt(0) === "2") { if(event.data.status.charAt(0) === "2") {
if(event.data.cookies) { if(event.data.cookies) {
+2 -1
View File
@@ -15,7 +15,8 @@ var getCellInfo = function(text, start, length, SEPARATOR) {
var isCellQuoted = text.charAt(start) === QUOTE; var isCellQuoted = text.charAt(start) === QUOTE;
var cellStart = isCellQuoted ? start + 1 : start; var cellStart = isCellQuoted ? start + 1 : start;
if(text.charAt(i) === SEPARATOR) { // A quote licenses a separator inside the cell, so only an unquoted cell reads an immediate separator as empty
if(!isCellQuoted && text.charAt(cellStart) === SEPARATOR) {
return [cellStart, cellStart, false]; return [cellStart, cellStart, false];
} }
+62
View File
@@ -0,0 +1,62 @@
/*\
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 components in the 0-255 range, matching the legacy csscolorparser.
// Clamp because Color.js can emit out-of-gamut sRGB components (e.g. from P3 inputs).
return [
Math.round(Math.max(0,Math.min(255,rgb[0] * 255))),
Math.round(Math.max(0,Math.min(255,rgb[1] * 255))),
Math.round(Math.max(0,Math.min(255,rgb[2] * 255))),
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) {
// The <input type="color"> element only accepts six digit hex values, so we
// discard any alpha channel and fall back to black for unparseable input
var rgb = exports.parseCSSColor(colourString);
if(rgb) {
return "#" + rgb.slice(0,3).map(function(component) {
return ("0" + component.toString(16)).slice(-2);
}).join("");
} else {
return "#000000";
}
};
+21
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
@@ -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"
}
}
]
}
-200
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) { }
@@ -1,3 +0,0 @@
title: $:/core/modules/utils/dom/csscolorparser.js
type: application/javascript
module-type: utils
+6 -5
View File
@@ -164,15 +164,16 @@ exports.forceLayout = function(element) {
Pulse an element for debugging purposes Pulse an element for debugging purposes
*/ */
exports.pulseElement = function(element) { exports.pulseElement = function(element) {
var eventName = $tw.utils.convertEventName("animationEnd");
// Event handler to remove the class at the end // Event handler to remove the class at the end
element.addEventListener($tw.browser.animationEnd,function handler(event) { element.addEventListener(eventName,function handler(event) {
element.removeEventListener($tw.browser.animationEnd,handler,false); element.removeEventListener(eventName,handler,false);
$tw.utils.removeClass(element,"pulse"); $tw.utils.removeClass(element,"tc-pulse");
},false); },false);
// Apply the pulse class // Apply the pulse class
$tw.utils.removeClass(element,"pulse"); $tw.utils.removeClass(element,"tc-pulse");
$tw.utils.forceLayout(element); $tw.utils.forceLayout(element);
$tw.utils.addClass(element,"pulse"); $tw.utils.addClass(element,"tc-pulse");
}; };
/* /*
+13 -2
View File
@@ -115,7 +115,9 @@ var TW_Element = function(tag, namespace) {
this.children = []; this.children = [];
this._style = {}; // Internal style object this._style = {}; // Internal style object
this.style = new TW_Style(this); // Proxy for style management this.style = new TW_Style(this); // Proxy for style management
this.namespaceURI = namespace || "http://www.w3.org/1999/xhtml"; // createElementNS with empty-string or null normalises to null (no namespace) per spec.
// https://dom.spec.whatwg.org/#dom-document-createelementns
this.namespaceURI = namespace !== undefined ? (namespace || null) : "http://www.w3.org/1999/xhtml";
}; };
@@ -206,7 +208,16 @@ TW_Element.prototype.addEventListener = function(type,listener,useCapture) {
Object.defineProperty(TW_Element.prototype, "tagName", { Object.defineProperty(TW_Element.prototype, "tagName", {
get: function() { get: function() {
return this.tag || ""; if(!this.tag) {
return "";
}
// HTML elements report uppercase tagName per DOM spec. Other namespaces
// preserve case. Fakedom only models HTML documents.
// https://dom.spec.whatwg.org/#dom-element-tagname
if(this.namespaceURI === "http://www.w3.org/1999/xhtml") {
return this.tag.toUpperCase();
}
return this.tag;
} }
}); });
+27 -7
View File
@@ -243,8 +243,28 @@ exports.slowInSlowOut = function(t) {
}; };
exports.copyObjectPropertiesSafe = function(object) { exports.copyObjectPropertiesSafe = function(object) {
const seen = new Set(), const seen = new Set();
isDOMElement = (value) => value instanceof Node || value instanceof Window;
function isDOMElement(value) {
if(!value || typeof value !== "object") {
return false;
}
// Cross-realm DOM nodes
if(typeof value.nodeType === "number" &&
typeof value.nodeName === "string") {
return true;
}
// Cross-realm Window objects
if(value.window === value &&
value.document &&
value.location) {
return true;
}
return false;
}
function safeCopy(obj) { function safeCopy(obj) {
// skip circular references // skip circular references
@@ -255,10 +275,6 @@ exports.copyObjectPropertiesSafe = function(object) {
if(typeof obj !== "object" || obj === null) { if(typeof obj !== "object" || obj === null) {
return obj; return obj;
} }
// skip DOM elements
if(isDOMElement(obj)) {
return undefined;
}
// copy arrays, preserving positions // copy arrays, preserving positions
if(Array.isArray(obj)) { if(Array.isArray(obj)) {
return obj.map((item) => { return obj.map((item) => {
@@ -266,7 +282,11 @@ exports.copyObjectPropertiesSafe = function(object) {
return value === undefined ? null : value; return value === undefined ? null : value;
}); });
} }
// skip DOM elements
if(isDOMElement(obj)) {
return undefined;
}
seen.add(obj); seen.add(obj);
const copy = {}; const copy = {};
let key, let key,
+2 -2
View File
@@ -62,8 +62,8 @@ SendMessageWidget.prototype.invokeAction = function(triggeringWidget,event) {
var paramObject = Object.create(null); var paramObject = Object.create(null);
// Add names/values pairs if present // Add names/values pairs if present
if(this.actionNames && this.actionValues) { if(this.actionNames && this.actionValues) {
var names = this.wiki.filterTiddlers(this.actionNames,this), var names = this.wiki.filterTiddlers(this.actionNames,this,null,{defaultFilterRunPrefix: "all"}),
values = this.wiki.filterTiddlers(this.actionValues,this); values = this.wiki.filterTiddlers(this.actionValues,this,null,{defaultFilterRunPrefix: "all"});
$tw.utils.each(names,function(name,index) { $tw.utils.each(names,function(name,index) {
paramObject[name] = values[index] || ""; paramObject[name] = values[index] || "";
}); });
@@ -56,10 +56,10 @@ Invoke the action associated with this widget
*/ */
SetMultipleFieldsWidget.prototype.invokeAction = function(triggeringWidget,event) { SetMultipleFieldsWidget.prototype.invokeAction = function(triggeringWidget,event) {
var tiddler = this.wiki.getTiddler(this.actionTiddler), var tiddler = this.wiki.getTiddler(this.actionTiddler),
names, values = this.wiki.filterTiddlers(this.actionValues,this); names, values = this.wiki.filterTiddlers(this.actionValues,this,null,{defaultFilterRunPrefix: "all"});
if(this.actionFields) { if(this.actionFields) {
var additions = {}; var additions = {};
names = this.wiki.filterTiddlers(this.actionFields,this); names = this.wiki.filterTiddlers(this.actionFields,this,null,{defaultFilterRunPrefix: "all"});
$tw.utils.each(names,function(fieldname,index) { $tw.utils.each(names,function(fieldname,index) {
additions[fieldname] = values[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)); this.wiki.addTiddler(new $tw.Tiddler(creationFields,tiddler,{title: this.actionTiddler},modificationFields,additions));
} else if(this.actionIndexes) { } else if(this.actionIndexes) {
var data = this.wiki.getTiddlerData(this.actionTiddler,Object.create(null)); var data = this.wiki.getTiddlerData(this.actionTiddler,Object.create(null));
names = this.wiki.filterTiddlers(this.actionIndexes,this); names = this.wiki.filterTiddlers(this.actionIndexes,this,null,{defaultFilterRunPrefix: "all"});
$tw.utils.each(names,function(name,index) { $tw.utils.each(names,function(name,index) {
data[name] = values[index] || ""; data[name] = values[index] || "";
}); });
+19 -5
View File
@@ -151,10 +151,24 @@ ButtonWidget.prototype.getBoundingClientRect = function() {
}; };
ButtonWidget.prototype.isSelected = function() { ButtonWidget.prototype.isSelected = function() {
return this.setTitle ? (this.setField ? this.wiki.getTiddler(this.setTitle).getFieldString(this.setField) === this.setTo : var currentValue;
(this.setIndex ? this.wiki.extractTiddlerDataItem(this.setTitle,this.setIndex) === this.setTo : if(this.setTitle) {
this.wiki.getTiddlerText(this.setTitle))) || this.defaultSetValue || this.getVariable("currentTiddler") : if(this.setField) {
this.wiki.getTextReference(this.set,this.defaultSetValue,this.getVariable("currentTiddler")) === this.setTo; // The state tiddler usually does not exist until the button is first clicked
var tiddler = this.wiki.getTiddler(this.setTitle);
currentValue = tiddler ? tiddler.getFieldString(this.setField) : undefined;
} else if(this.setIndex) {
currentValue = this.wiki.extractTiddlerDataItem(this.setTitle,this.setIndex);
} else {
currentValue = this.wiki.getTiddlerText(this.setTitle);
}
if(!currentValue) {
currentValue = this.defaultSetValue || this.getVariable("currentTiddler");
}
} else {
currentValue = this.wiki.getTextReference(this.set,this.defaultSetValue,this.getVariable("currentTiddler"));
}
return currentValue === this.setTo;
}; };
ButtonWidget.prototype.isPoppedUp = function() { ButtonWidget.prototype.isPoppedUp = function() {
@@ -267,7 +281,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
*/ */
ButtonWidget.prototype.refresh = function(changedTiddlers) { ButtonWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes(); var changedAttributes = this.computeAttributes();
if(changedAttributes.tooltip || changedAttributes.actions || changedAttributes.to || changedAttributes.message || changedAttributes.param || changedAttributes.set || changedAttributes.setTo || changedAttributes.popup || changedAttributes.hover || changedAttributes.selectedClass || changedAttributes.style || changedAttributes.dragFilter || changedAttributes.dragTiddler || (this.set && changedTiddlers[this.set]) || (this.popup && changedTiddlers[this.popup]) || (this.popupTitle && changedTiddlers[this.popupTitle]) || changedAttributes.popupAbsCoords || changedAttributes.setTitle || changedAttributes.setField || changedAttributes.setIndex || changedAttributes.popupTitle || changedAttributes.disabled || changedAttributes["default"]) { if(changedAttributes.tooltip || changedAttributes.actions || changedAttributes.to || changedAttributes.message || changedAttributes.param || changedAttributes.set || changedAttributes.setTo || changedAttributes.popup || changedAttributes.hover || changedAttributes.selectedClass || changedAttributes.style || changedAttributes.dragFilter || changedAttributes.dragTiddler || (this.set && changedTiddlers[this.set]) || (this.setTitle && changedTiddlers[this.setTitle]) || (this.popup && changedTiddlers[this.popup]) || (this.popupTitle && changedTiddlers[this.popupTitle]) || changedAttributes.popupAbsCoords || changedAttributes.setTitle || changedAttributes.setField || changedAttributes.setIndex || changedAttributes.popupTitle || changedAttributes.disabled || changedAttributes["default"]) {
this.refreshSelf(); this.refreshSelf();
return true; return true;
} else { } else {
+1 -1
View File
@@ -22,7 +22,7 @@ Inherit from the base widget class
DiffTextWidget.prototype = new Widget(); DiffTextWidget.prototype = new Widget();
DiffTextWidget.prototype.invisibleCharacters = { DiffTextWidget.prototype.invisibleCharacters = {
"\n": "↩︎\n", "\n": "\n",
"\r": "⇠", "\r": "⇠",
"\t": "⇥\t" "\t": "⇥\t"
}; };
+4 -4
View File
@@ -72,8 +72,8 @@ GenesisWidget.prototype.execute = function() {
this.attributeNames = []; this.attributeNames = [];
this.attributeValues = []; this.attributeValues = [];
if(this.genesisNames && this.genesisValues) { if(this.genesisNames && this.genesisValues) {
this.attributeNames = this.wiki.filterTiddlers(self.genesisNames,this); this.attributeNames = this.wiki.filterTiddlers(self.genesisNames,this,null,{defaultFilterRunPrefix: "all"});
this.attributeValues = this.wiki.filterTiddlers(self.genesisValues,this); this.attributeValues = this.wiki.filterTiddlers(self.genesisValues,this,null,{defaultFilterRunPrefix: "all"});
$tw.utils.each(this.attributeNames,function(varname,index) { $tw.utils.each(this.attributeNames,function(varname,index) {
$tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],varname,self.attributeValues[index] || ""); $tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],varname,self.attributeValues[index] || "");
}); });
@@ -103,8 +103,8 @@ GenesisWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes(), var changedAttributes = this.computeAttributes(),
filterNames = this.getAttribute("$names",""), filterNames = this.getAttribute("$names",""),
filterValues = this.getAttribute("$values",""), filterValues = this.getAttribute("$values",""),
attributeNames = this.wiki.filterTiddlers(filterNames,this), attributeNames = this.wiki.filterTiddlers(filterNames,this,null,{defaultFilterRunPrefix: "all"}),
attributeValues = this.wiki.filterTiddlers(filterValues,this); attributeValues = this.wiki.filterTiddlers(filterValues,this,null,{defaultFilterRunPrefix: "all"});
if($tw.utils.count(changedAttributes) > 0 || !$tw.utils.isArrayEqual(this.attributeNames,attributeNames) || !$tw.utils.isArrayEqual(this.attributeValues,attributeValues)) { if($tw.utils.count(changedAttributes) > 0 || !$tw.utils.isArrayEqual(this.attributeNames,attributeNames) || !$tw.utils.isArrayEqual(this.attributeValues,attributeValues)) {
this.refreshSelf(); this.refreshSelf();
return true; return true;
+2 -1
View File
@@ -99,7 +99,8 @@ LinkWidget.prototype.renderLink = function(parent,nextSibling) {
wikiLinkText = this.wiki.filterTiddlers(wikilinkTransformFilter,this,function(iterator) { wikiLinkText = this.wiki.filterTiddlers(wikilinkTransformFilter,this,function(iterator) {
iterator(self.wiki.getTiddler(self.to),self.to); iterator(self.wiki.getTiddler(self.to),self.to);
})[0]; })[0];
} else { }
if(!wikiLinkText) {
// Expand the tv-wikilink-template variable to construct the href // Expand the tv-wikilink-template variable to construct the href
var wikiLinkTemplateMacro = this.getVariable("tv-wikilink-template"), var wikiLinkTemplateMacro = this.getVariable("tv-wikilink-template"),
wikiLinkTemplate = wikiLinkTemplateMacro ? wikiLinkTemplateMacro.trim() : "#$uri_encoded$"; wikiLinkTemplate = wikiLinkTemplateMacro ? wikiLinkTemplateMacro.trim() : "#$uri_encoded$";
+4 -4
View File
@@ -49,8 +49,8 @@ SetMultipleVariablesWidget.prototype.setVariables = function() {
this.variableNames = []; this.variableNames = [];
this.variableValues = []; this.variableValues = [];
if(filterNames && filterValues) { if(filterNames && filterValues) {
this.variableNames = this.wiki.filterTiddlers(filterNames,this); this.variableNames = this.wiki.filterTiddlers(filterNames,this,null,{defaultFilterRunPrefix: "all"});
this.variableValues = this.wiki.filterTiddlers(filterValues,this); this.variableValues = this.wiki.filterTiddlers(filterValues,this,null,{defaultFilterRunPrefix: "all"});
$tw.utils.each(this.variableNames,function(varname,index) { $tw.utils.each(this.variableNames,function(varname,index) {
self.setVariable(varname,self.variableValues[index]); self.setVariable(varname,self.variableValues[index]);
}); });
@@ -63,8 +63,8 @@ Refresh the widget by ensuring our attributes are up to date
SetMultipleVariablesWidget.prototype.refresh = function(changedTiddlers) { SetMultipleVariablesWidget.prototype.refresh = function(changedTiddlers) {
var filterNames = this.getAttribute("$names",""), var filterNames = this.getAttribute("$names",""),
filterValues = this.getAttribute("$values",""), filterValues = this.getAttribute("$values",""),
variableNames = this.wiki.filterTiddlers(filterNames,this), variableNames = this.wiki.filterTiddlers(filterNames,this,null,{defaultFilterRunPrefix: "all"}),
variableValues = this.wiki.filterTiddlers(filterValues,this); variableValues = this.wiki.filterTiddlers(filterValues,this,null,{defaultFilterRunPrefix: "all"});
if(!$tw.utils.isArrayEqual(this.variableNames,variableNames) || !$tw.utils.isArrayEqual(this.variableValues,variableValues)) { if(!$tw.utils.isArrayEqual(this.variableNames,variableNames) || !$tw.utils.isArrayEqual(this.variableValues,variableValues)) {
this.refreshSelf(); this.refreshSelf();
return true; return true;
+10
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]]
customisations: $:/palettes/AutoToggle/Customisations
palette-import@light: $:/palettes/TwentyTwenties
palette-import@dark: $:/palettes/TwentyTwenties/Dark
category: 2026
@@ -0,0 +1,25 @@
title: $:/palettes/AutoToggle/Customisations
\procedure layer-input-actions()
<$action-setfield $tiddler=<<layerTitle>> palette-import={{$:/palette}}/>
\end layer-input-actions
\procedure set-imported-palette(field)
<$select field=<<field>> default={{{ [[$:/temp/palette-consolidated]get<field>] }}} actions=<<layer-input-actions>>>
<$list filter="[all[shadows+tiddlers]tag[$:/tags/Palette]sort[name]] -[{$:/palette}]">
<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.
<$let layerTitle={{{ [function[tf.palette-customisations-layer]] }}}>
<$tiddler tiddler=<<layerTitle>>>
Light palette: <<set-imported-palette field:"palette-import@light">>
Dark palette: <<set-imported-palette field:"palette-import@dark">>
</$tiddler>
</$let>
+226
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
customisations: $:/palettes/TwentyTwenties/Customisations
palette-import: $:/palettes/Vanilla
category: 2026
# Background and foreground colours, which are interpolated as required
base-paper: #FFFCF0
base-background: #f0d5c6
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[]]
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[]]
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
@@ -0,0 +1,25 @@
title: $:/palettes/TwentyTwenties/Customisations
\procedure layer-input-actions()
<$action-setfield $tiddler=<<layerTitle>> palette-import={{$:/palette}}/>
\end layer-input-actions
\procedure entry(name,description)
<$text text=<<description>>/>: <$edit-text tiddler=<<layerTitle>> index=<<name>> type="color" tag="input" default={{{ [function[colour],<name>] }}} inputActions=<<layer-input-actions>>/>
\end entry
<$let layerTitle={{{ [function[tf.palette-customisations-layer]] }}}>
<<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">>
</$let>
+12
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
+12
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
+13
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)
+1
View File
@@ -4,6 +4,7 @@ description: Pale and unobtrusive
tags: $:/tags/Palette tags: $:/tags/Palette
type: application/x-tiddler-dictionary type: application/x-tiddler-dictionary
color-scheme: light color-scheme: light
category: Legacy
alert-background: #ffe476 alert-background: #ffe476
alert-border: #b99e2f alert-border: #b99e2f
+12
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>>
@@ -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]]
+4 -4
View File
@@ -1,11 +1,11 @@
title: $:/core/stylesheets/custom-properties title: $:/core/stylesheets/custom-properties
\rules only transcludeinline macrocallinline html transcludeblock \rules only transcludeinline macrocallinline html transcludeblock filteredtranscludeinline
/* Tiddlywiki's CSS properties */ /* Tiddlywiki's CSS properties */
:root { :root {
<$list filter="[[$:/palettes/Vanilla]indexes[]]"> <$list filter="[[$:/temp/palette-colours]indexes[]!prefix[?]]">
--tpc-<<currentTiddler>>: <$transclude $variable="colour" $mode="inline" name=<<currentTiddler>>/>; --tpc-<<currentTiddler>>: <$transclude $variable="colour" $mode="inline" name=<<currentTiddler>>/>;
</$list> </$list>
@@ -21,10 +21,10 @@ title: $:/core/stylesheets/custom-properties
--tp-story-left: {{$:/themes/tiddlywiki/vanilla/metrics/storyleft}}; --tp-story-left: {{$:/themes/tiddlywiki/vanilla/metrics/storyleft}};
--tp-story-top: {{$:/themes/tiddlywiki/vanilla/metrics/storytop}}; --tp-story-top: {{$:/themes/tiddlywiki/vanilla/metrics/storytop}};
--tp-story-right: {{$:/themes/tiddlywiki/vanilla/metrics/storyright}}; --tp-story-right: {{$:/themes/tiddlywiki/vanilla/metrics/storyright}};
--tp-story-width: {{$:/themes/tiddlywiki/vanilla/metrics/storyrwidth}}; --tp-story-width: {{$:/themes/tiddlywiki/vanilla/metrics/storywidth}};
--tp-tiddler-width: {{$:/themes/tiddlywiki/vanilla/metrics/tiddlerwidth}}; --tp-tiddler-width: {{$:/themes/tiddlywiki/vanilla/metrics/tiddlerwidth}};
--tp-sidebar-breakpoint: {{$:/themes/tiddlywiki/vanilla/metrics/sidebarbreakpoint}}; --tp-sidebar-breakpoint: {{$:/themes/tiddlywiki/vanilla/metrics/sidebarbreakpoint}};
--tp-sidebar-width: {{$:/themes/tiddlywiki/vanilla/metrics/sidebarwidth}}; --tp-sidebar-width: {{$:/themes/tiddlywiki/vanilla/metrics/sidebarwidth}};
--tp-animation-duration: {{{ [{$:/config/AnimationDuration}addsuffix[ms]] }}}; --tp-animation-duration: {{{ [{$:/config/AnimationDuration}addsuffix[ms]] }}};
} }
+3
View File
@@ -0,0 +1,3 @@
title: $:/core/templates/plain-text
<$text text=<<text>>/>
+3
View File
@@ -0,0 +1,3 @@
title: $:/core/templates/rendered-text
<<text>>
+5 -3
View File
@@ -4,13 +4,15 @@ description: create a new journal tiddler
\whitespace trim \whitespace trim
\function get-tags() [<textFieldTags>] [<tagsFieldTags>] +[join[ ]] \function get-tags() [<textFieldTags>] [<tagsFieldTags>] +[join[ ]]
<$let journalTitleTemplate={{$:/config/NewJournal/Title}} textFieldTags={{$:/config/NewJournal/Tags}} tagsFieldTags={{$:/config/NewJournal/Tags!!tags}} journalText={{$:/config/NewJournal/Text}}> <$let journalTitle=<<now format={{$:/config/NewJournal/Title}}>>
<$wikify name="journalTitle" text="<$transclude $variable='now' format=<<journalTitleTemplate>>/>"> textFieldTags={{$:/config/NewJournal/Tags}}
tagsFieldTags={{$:/config/NewJournal/Tags!!tags}}
journalText={{$:/config/NewJournal/Text}}
>
<$reveal type="nomatch" state=<<journalTitle>> text=""> <$reveal type="nomatch" state=<<journalTitle>> text="">
<$action-sendmessage $message="tm-new-tiddler" title=<<journalTitle>> tags=<<get-tags>> text={{{ [<journalTitle>get[]] }}}/> <$action-sendmessage $message="tm-new-tiddler" title=<<journalTitle>> tags=<<get-tags>> text={{{ [<journalTitle>get[]] }}}/>
</$reveal> </$reveal>
<$reveal type="match" state=<<journalTitle>> text=""> <$reveal type="match" state=<<journalTitle>> text="">
<$action-sendmessage $message="tm-new-tiddler" title=<<journalTitle>> tags=<<get-tags>> text=<<journalText>>/> <$action-sendmessage $message="tm-new-tiddler" title=<<journalTitle>> tags=<<get-tags>> text=<<journalText>>/>
</$reveal> </$reveal>
</$wikify>
</$let> </$let>
+1 -1
View File
@@ -6,5 +6,5 @@ caption: {{$:/language/ControlPanel/Appearance/Caption}}
{{$:/language/ControlPanel/Appearance/Hint}} {{$:/language/ControlPanel/Appearance/Hint}}
<div class="tc-control-panel"> <div class="tc-control-panel">
<$macrocall $name="tabs" tabsList="[all[shadows+tiddlers]tag[$:/tags/ControlPanel/Appearance]!has[draft.of]]" default="$:/core/ui/ControlPanel/Theme" explicitState="$:/state/tab--1963855381"/> <$macrocall $name="tabs" tabsList="[all[shadows+tiddlers]tag[$:/tags/ControlPanel/Appearance]!has[draft.of]]" default="$:/core/ui/ControlPanel/Palette" explicitState="$:/state/tab--1963855381"/>
</div> </div>
+1 -17
View File
@@ -2,20 +2,4 @@ title: $:/core/ui/ControlPanel/Palette
tags: $:/tags/ControlPanel/Appearance tags: $:/tags/ControlPanel/Appearance
caption: {{$:/language/ControlPanel/Palette/Caption}} caption: {{$:/language/ControlPanel/Palette/Caption}}
\define lingo-base() $:/language/ControlPanel/Palette/ {{$:/PaletteManager}}
{{$:/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>
+3 -3
View File
@@ -108,10 +108,10 @@ tags: $:/tags/EditTemplate
storeTitle=<<newFieldNameInputTiddler>> storeTitle=<<newFieldNameInputTiddler>>
searchListState=<<newFieldNameSelectionTiddler>> searchListState=<<newFieldNameSelectionTiddler>>
> >
<div class="tc-edit-field-add-name-wrapper"> <div class=`tc-edit-field-add-name-wrapper ${ [<newFieldNameTiddler>get[text]] :intersection[<storyTiddler>fields[]] :then[[tc-edit-field-exists]] }$`>
<$transclude $variable="keyboard-driven-input" <$transclude $variable="keyboard-driven-input"
cancelPopups="yes" cancelPopups="yes"
class=`tc-edit-texteditor tc-popup-handle ${ [<newFieldNameTiddler>get[text]] :intersection[<storyTiddler>fields[]] :then[[tc-edit-field-exists]] }$` class="tc-edit-texteditor tc-popup-handle"
configTiddlerFilter="[[$:/config/EditMode/fieldname-filter]]" configTiddlerFilter="[[$:/config/EditMode/fieldname-filter]]"
default="" default=""
focus={{{ [{!!draft.of}is[tiddler]then{$:/config/AutoFocusEdit}match[fields]then[true]] :else[{$:/config/AutoFocus}match[fields]then[true]] :else[[false]] }}} focus={{{ [{!!draft.of}is[tiddler]then{$:/config/AutoFocusEdit}match[fields]then[true]] :else[{$:/config/AutoFocus}match[fields]then[true]] :else[[false]] }}}
@@ -178,4 +178,4 @@ tags: $:/tags/EditTemplate
</$let> </$let>
</$let> </$let>
</div> </div>
</$let> </$let>
+19 -24
View File
@@ -6,30 +6,25 @@ tags: $:/tags/EditTemplate
\procedure lingo-base() $:/language/EditTemplate/ \procedure lingo-base() $:/language/EditTemplate/
\procedure tag-body-inner(colour,fallbackTarget,colourA,colourB,icon,tagField:"tags") \procedure tag-body-inner(colour,fallbackTarget,colourA,colourB,icon,tagField:"tags")
<$wikify name="foregroundColor" <$let foregroundColor=<<contrastcolour target=<<colour>> fallbackTarget=<<fallbackTarget>> colourA=<<colourA>> colourB=<<colourB>> >>
text="""<$macrocall $name="contrastcolour" backgroundColor=<<colour>>
target=<<colour>> >
fallbackTarget=<<fallbackTarget>> <span class="tc-tag-label tc-tag-list-item tc-small-gap-right"
colourA=<<colourA>> data-tag-title=<<currentTiddler>>
colourB=<<colourB>>/> style=`color:$(foregroundColor)$; fill:$(foregroundColor)$; background-color:$(backgroundColor)$;`
""" >
> <$transclude tiddler=<<icon>>/>
<$let backgroundColor=<<colour>> > <$view field="title" format="text"/>
<span class="tc-tag-label tc-tag-list-item tc-small-gap-right" <$button class="tc-btn-invisible tc-remove-tag-button"
data-tag-title=<<currentTiddler>> style.fill=<<foregroundColor>>
style=`color:$(foregroundColor)$; fill:$(foregroundColor)$; background-color:$(backgroundColor)$;`
> >
<$transclude tiddler=<<icon>>/> <$action-listops $tiddler=<<saveTiddler>> $field=<<tagField>> $subfilter="-[{!!title}]"/>
<$view field="title" format="text"/> <!-- Touch a temp tiddler so that the tag remove animation fires instantly. Otherwise it's delayed by the typing timeout duration -->
<$button class="tc-btn-invisible tc-remove-tag-button" <$action-setfield $tiddler="$:/temp/tags-edit-refresh" text=<<currentTiddler>>/>
style.fill=<<foregroundColor>> {{$:/core/images/close-button}}
> </$button>
<$action-listops $tiddler=<<saveTiddler>> $field=<<tagField>> $subfilter="-[{!!title}]"/> </span>
{{$:/core/images/close-button}} </$let>
</$button>
</span>
</$let>
</$wikify>
\end \end
\procedure tag-body(colour,palette,icon,tagField:"tags") \procedure tag-body(colour,palette,icon,tagField:"tags")
@@ -48,7 +43,7 @@ tags: $:/tags/EditTemplate
<$list filter="[<currentTiddler>get<tagField>enlist-input[]sort[title]]" storyview="pop"> <$list filter="[<currentTiddler>get<tagField>enlist-input[]sort[title]]" storyview="pop">
<$macrocall $name="tag-body" <$macrocall $name="tag-body"
colour={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}} colour={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}}
palette={{$:/palette}} palette="$:/temp/palette-colours"
icon={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerIconFilter]!is[draft]get[text]] }}} icon={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerIconFilter]!is[draft]get[text]] }}}
tagField=<<tagField>> tagField=<<tagField>>
/> />
+105
View File
@@ -0,0 +1,105 @@
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"/>
<%if [<layerTitle>getindex<colourName>] %>
<br/><em><<lingo Customised/Entry>></em>
<%endif%>
</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}}>
<$let layerTitle={{{ [function[tf.palette-customisations-layer]] }}}>
<<lingo Prompt>>&#32;<$link to={{$:/palette}}><$macrocall $name="currentTiddler" $output="text/plain"/></$link>
<$list filter="[function[tf.palette-customisations-layer]is[tiddler]]" variable="listItem">
<<lingo Customised/Prompt>>
&#32;
<$button actions="""<$action-deletetiddler $tiddler=<<layerTitle>>/>""">{{$:/language/ControlPanel/Palette/Customisations/Reset/Caption}}</$button>
</$list>
<$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>>
</$let>
+43 -87
View File
@@ -1,94 +1,50 @@
title: $:/PaletteManager title: $:/PaletteManager
\define lingo-base() $:/language/ControlPanel/Palette/Editor/ \define lingo-base() $:/language/ControlPanel/Palette/
\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) <!-- Used by the language string Customisations/Prompt -->
\import $:/core/macros/utils \procedure palette-link()
\whitespace trim <$tiddler tiddler={{$:/palette}}>
<$wikify name="name" text="""$macrocall$"""> <$link to={{!!title}}>
<<name>> <$view field="name" format="text">
</$wikify> <$view field="title" format="text"/>
\end </$view>
</$link>
</$tiddler>
\end palette-link
<$transclude $tiddler="$:/snippets/paletteswitcher" thumbnails="yes"/>
{{$:/snippets/palettetests}}
<$let
resolvedPalette={{{ [{$:/palette}is[tiddler]] [{$:/palette}is[shadow]] :else[[$:/palettes/Vanilla]] }}}
paletteCustomisations={{{ [<resolvedPalette>get[customisations]] :else[[$:/temp/palette-consolidated]get[customisations]] }}}
layerTitle={{{ [function[tf.palette-customisations-layer]] }}}
>
<%if [<paletteCustomisations>!match[]] %>
<div>
<<lingo Customisations/Prompt>>
<$transclude $tiddler=<<paletteCustomisations>> $mode="block"/>
</div>
<%endif%>
<%if [<layerTitle>is[tiddler]] %>
<div>
<$button actions="""<$action-deletetiddler $tiddler=<<layerTitle>>/>"""><<lingo Customisations/Reset/Caption>></$button>
</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>
<$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>
<$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>>
+19
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>
+55
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
+11
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>
@@ -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>
@@ -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>
+18
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>
@@ -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>
+7
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>
+9
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>
+10
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>
+15
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>
@@ -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>
@@ -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>
@@ -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"/>
+2 -4
View File
@@ -16,15 +16,13 @@ title: $:/core/ui/TagPickerTagTemplate
<$set name="backgroundColor" <$set name="backgroundColor"
value={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}} value={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}}
> >
<$wikify name="foregroundColor" <$let foregroundColor=<<contrastcolour target=<<backgroundColor>> fallbackTarget=<<fallbackTarget>> colourA=<<colourA>> colourB=<<colourB>> >> >
text="""<$macrocall $name="contrastcolour" target=<<backgroundColor>> fallbackTarget=<<fallbackTarget>> colourA=<<colourA>> colourB=<<colourB>>/>"""
>
<span class="tc-tag-label tc-btn-invisible" <span class="tc-tag-label tc-btn-invisible"
style=<<tag-pill-styles>> style=<<tag-pill-styles>>
data-tag-title=<<currentTiddler>> data-tag-title=<<currentTiddler>>
> >
{{||$:/core/ui/TiddlerIcon}}<$view field="title" format="text"/> {{||$:/core/ui/TiddlerIcon}}<$view field="title" format="text"/>
</span> </span>
</$wikify> </$let>
</$set> </$set>
</$button> </$button>
-1
View File
@@ -7,7 +7,6 @@ title: $:/core/ui/TagTemplate
tag=<<currentTiddler>> tag=<<currentTiddler>>
icon={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerIconFilter]!is[draft]get[text]] }}} 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]] }}} colour={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}}
palette={{$:/palette}}
element-tag="$button" element-tag="$button"
element-attributes="""popup=<<qualify "$:/state/popup/tag">> dragFilter="[subfilter{$:/core/config/TagPillDragFilter}]" tag='span'""" element-attributes="""popup=<<qualify "$:/state/popup/tag">> dragFilter="[subfilter{$:/core/config/TagPillDragFilter}]" tag='span'"""
/> />
+4 -3
View File
@@ -6,7 +6,6 @@ description: {{$:/language/Buttons/NewJournalHere/Hint}}
\whitespace trim \whitespace trim
\procedure journalButton() \procedure journalButton()
<$button tooltip={{$:/language/Buttons/NewJournalHere/Hint}} aria-label={{$:/language/Buttons/NewJournalHere/Caption}} class=<<tv-config-toolbar-class>>> <$button tooltip={{$:/language/Buttons/NewJournalHere/Hint}} aria-label={{$:/language/Buttons/NewJournalHere/Caption}} class=<<tv-config-toolbar-class>>>
<$wikify name="journalTitle" text="""<$transclude $variable="now" format=<<journalTitleTemplate>>/>""">
<$action-sendmessage $message="tm-new-tiddler" title=<<journalTitle>> tags=`[[$(currentTiddlerTag)$]] $(journalTags)$`/> <$action-sendmessage $message="tm-new-tiddler" title=<<journalTitle>> tags=`[[$(currentTiddlerTag)$]] $(journalTags)$`/>
<%if [<tv-config-toolbar-icons>match[yes]] %> <%if [<tv-config-toolbar-icons>match[yes]] %>
{{$:/core/images/new-journal-button}} {{$:/core/images/new-journal-button}}
@@ -16,9 +15,11 @@ description: {{$:/language/Buttons/NewJournalHere/Hint}}
<$text text={{$:/language/Buttons/NewJournalHere/Caption}}/> <$text text={{$:/language/Buttons/NewJournalHere/Caption}}/>
</span> </span>
<%endif%> <%endif%>
</$wikify>
</$button> </$button>
\end \end
<$let journalTitleTemplate={{$:/config/NewJournal/Title}} journalTags={{$:/config/NewJournal/Tags}} currentTiddlerTag=<<currentTiddler>>> <$let journalTitle=<<now format={{$:/config/NewJournal/Title}}>>
journalTags={{$:/config/NewJournal/Tags}}
currentTiddlerTag=<<currentTiddler>>
>
<<journalButton>> <<journalButton>>
</$let> </$let>
+5 -1
View File
@@ -17,6 +17,10 @@ This page summarises high level information about the configuration of this ~Tid
Drag this link to copy this tool to another wiki Drag this link to copy this tool to another wiki
\end intrinsic-lingo-Drag/Caption \end intrinsic-lingo-Drag/Caption
\procedure intrinsic-lingo-Generate/Caption()
Click to generate wiki information report
\end intrinsic-lingo-Generate/Caption
\procedure lingo(title,mode:"inline") \procedure lingo(title,mode:"inline")
<%if [<title>addprefix<lingo-base>is[shadow]] %> <%if [<title>addprefix<lingo-base>is[shadow]] %>
<$transclude $tiddler={{{ [<title>addprefix<lingo-base>] }}} $mode=<<mode>>/> <$transclude $tiddler={{{ [<title>addprefix<lingo-base>] }}} $mode=<<mode>>/>
@@ -105,7 +109,7 @@ Drag this link to copy this tool to another wiki
<$button> <$button>
<<display-wiki-info-modal>> <<display-wiki-info-modal>>
Click to generate wiki information report <<lingo title:"Generate/Caption">>
</$button> </$button>
<$link to="$:/core/ui/ControlPanel/WikiInformation"> <$link to="$:/core/ui/ControlPanel/WikiInformation">
@@ -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[]] =[all[shadows+tiddlers]tag[$:/tags/Palette]changecount[]] =[all[shadows+tiddlers]tag[$:/tags/BackgroundPalette]changecount[]] =[all[tiddlers]prefix[$:/config/PaletteCustomisations/]changecount[]]
\import [subfilter{$:/core/config/GlobalImportFilter}]
<<actions-recompile-current-palette>>
+1 -1
View File
@@ -1,6 +1,6 @@
title: $:/config/OfficialPluginLibrary title: $:/config/OfficialPluginLibrary
tags: $:/tags/PluginLibrary tags: $:/tags/PluginLibrary
url: https://tiddlywiki.com/library/v5.4.1/index.html url: https://tiddlywiki.com/library/v5.5.0/index.html
caption: {{$:/language/OfficialPluginLibrary}} caption: {{$:/language/OfficialPluginLibrary}}
{{$:/language/OfficialPluginLibrary/Hint}} {{$:/language/OfficialPluginLibrary/Hint}}
@@ -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]] import: [field:plugin-type[import]then[$:/core/ui/ViewTemplate/body/import]]
plugin: [has[plugin-type]then[$:/core/ui/ViewTemplate/body/plugin]] plugin: [has[plugin-type]then[$:/core/ui/ViewTemplate/body/plugin]]
hide-body: [field:hide-body[yes]then[$:/core/ui/ViewTemplate/body/blank]] 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]] default: [[$:/core/ui/ViewTemplate/body/default]]
+9 -49
View File
@@ -1,52 +1,12 @@
title: $:/snippets/currpalettepreview title: $:/snippets/currpalettepreview
\define resolve-colour(macrocall)
\import $:/core/macros/utils
\whitespace trim \whitespace trim
<$wikify name="name" text="""$macrocall$""">
<<name>> \import [all[shadows+tiddlers]tag[$:/tags/Preview/Helpers]!is[draft]sort[title]]
</$wikify>
\end <!-- currentTiddler is the palette to use -->
\define swatchStyle() <$palette.preview paletteTitle=<<currentTiddler>>>
background-color: $(swatchColour)$; <div class="tc-palette-preview-thumbnail" style.background-color=<<colour page-background>>>
\end <<palette-preview-component-list tag:"$:/tags/Preview/Page">>
\define swatch-inner() </div>
\whitespace trim </$palette.preview>
<$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>
+167 -10
View File
@@ -1,17 +1,174 @@
title: $:/core/macros/CSS title: $:/core/macros/CSS
tags: $:/tags/Macro tags: $:/tags/Macro
<!-- Needs to stay that way for backwards compatibility. See GH issue: #8326 --> \procedure actions-compile-palette-filtered(consolidatedPalette,outputPalette)
\define colour(name) <!-- The $:/config/DefaultColourMappings/ fallback used below is unused by the core and is retained for compatibility with third party plugins -->
\whitespace trim <!-- Note the join, needed to cope with palette entries containing spaces -->
<$transclude tiddler={{$:/palette}} index="$name$"> \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[ ]]
<$transclude tiddler="$:/palettes/Vanilla" index="$name$"> \function colour(name) [tf.colour<name>]
<$transclude tiddler="$:/config/DefaultColourMappings/$name$"/> \function color(name) [tf.colour<name>]
</$transclude> <!-- Make the colour function use the input palette -->
</$transclude> <$list filter="[<consolidatedPalette>indexes[]sort[]]" variable="colour-name">
\end <$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 unsuffixed palette entries -->
<$action-setmultiplefields $tiddler=<<outputPalette>> $indexes="[<inputPalette>indexes[]!regexp[@]sort[]]" $values="[<inputPalette>indexes[]!regexp[@]sort[]] :map[<inputPalette>getindex<currentTiddler>]"/>
<!-- Copy the suffixed palette entries with the suffix stripped, overriding any unsuffixed entries of the same name -->
<%if [<scheme>!is[blank]] %>
<$let
prefixed-scheme={{{ [<scheme>addprefix[@]] }}}
>
<$action-setmultiplefields $tiddler=<<outputPalette>> $indexes="[<inputPalette>indexes[]suffix<prefixed-scheme>sort[]removesuffix<prefixed-scheme>]" $values="[<inputPalette>indexes[]suffix<prefixed-scheme>sort[]] :map[<inputPalette>getindex<currentTiddler>]"/>
</$let>
<%endif%>
<!-- Copy the fields from the palette -->
<$action-setmultiplefields $tiddler=<<outputPalette>> $fields="[<inputPalette>fields[]sort[]] -title -tags -text -type" $values="[<inputPalette>fields[]sort[]] -title -tags -text -type :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[<inputPalette>get[palette-import]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>>/>
<!-- Blank any entries with recursive definitions, reporting them via the readability test mechanism -->
<$list filter="[<outputPalette>indexes[]!prefix[?]] :filter[<outputPalette>getindex<currentTiddler>regexp[Excessive filter recursion]]" variable="entry-name">
<$action-setfield $tiddler=<<outputPalette>> $index=<<entry-name>> $value=""/>
<$action-setfield $tiddler=<<outputPalette>> $index={{{ [<entry-name>addprefix[?recursion-]] }}} $value={{{ [[the definition of ']addsuffix<entry-name>addsuffix[' is recursive]] }}}/>
</$list>
</$let>
</$let>
\end actions-compile-palette
<!-- Customisations of the current palette are stored in a layer tiddler that imports the palette itself -->
\function tf.palette-customisations-layer()
[{$:/palette}is[tiddler]] [{$:/palette}is[shadow]] :else[[$:/palettes/Vanilla]] :map[addprefix[$:/config/PaletteCustomisations/]]
\end tf.palette-customisations-layer
\procedure actions-recompile-current-palette()
\procedure tv-action-refresh-policy() always
<$let
resolvedPalette={{{ [{$:/palette}is[tiddler]] [{$:/palette}is[shadow]] :else[[$:/palettes/Vanilla]] }}}
inputPalette={{{ [function[tf.palette-customisations-layer]has[palette-import]] :else[<resolvedPalette>] }}}
>
<$transclude $variable="actions-compile-palette" inputPalette=<<inputPalette>> outputPalette="$:/temp/palette-colours"/>
</$let>
\end actions-recompile-current-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
<!-- The exclusions parameter guards against palettes that import each other in a loop -->
\function colour-inner-get-imported-palette(name,paletteTitle,exclusions)
[[palette-import@]addsuffix<scheme>] :map[<paletteTitle>get<currentTiddler>has[title]] +[!match[]] :else[<paletteTitle>get[palette-import]has[title]] +[!match[]] :filter[enlist<exclusions>match<currentTiddler>count[]compare:number:eq[0]] :map[function[colour-inner-descend-imported-palette],<name>,<currentTiddler>,<exclusions>,<paletteTitle>] +[!match[]]
\end colour-inner-get-imported-palette
<!-- Helper to extend the exclusions list with the parent palette before descending -->
\function colour-inner-descend-imported-palette(name,paletteTitle,exclusions,parentTitle)
[enlist<exclusions>] [<parentTitle>] +[format:titlelist[]join[ ]] :map[function[colour-inner-get-palette-entry],<name>,<paletteTitle>,<currentTiddler>]
\end colour-inner-descend-imported-palette
\function colour-inner-get-palette-entry(name,paletteTitle,exclusions)
[<name>addsuffix[@]addsuffix<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>,<exclusions>]
\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[<paletteTitle>get[palette-import]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
<!-- Translucent entries are composited over the background entry before their contrast is measured -->
\function tf.check-colour-contrast-subfunction()
[function[colour],[background]] =>backdrop [function[colour],<paletteEntryA>colour-composite<backdrop>] =>colourA [function[colour],<paletteEntryB>colour-composite<backdrop>] =>colourB [<colourA>] [<colourB>] +[colour-contrast[]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) \procedure datauri(title)
<$macrocall $name="makedatauri" type={{{ [<title>get[type]] }}} text={{{ [<title>get[text]] }}} _canonical_uri={{{ [<title>get[_canonical_uri]] }}}/> <$macrocall $name="makedatauri" type={{{ [<title>get[type]] }}} text={{{ [<title>get[text]] }}} _canonical_uri={{{ [<title>get[_canonical_uri]] }}}/>
+1 -1
View File
@@ -6,5 +6,5 @@ $:/language/
\end \end
\define lingo(title) \define lingo(title)
{{$(lingo-base)$$title$}} {{||$(lingo-base)$$title$}}
\end \end
+1 -1
View File
@@ -167,7 +167,7 @@ The second ESC tries to close the "draft tiddler"
<!-- keep those variables because they may "bleed" into macros using old syntax --> <!-- keep those variables because they may "bleed" into macros using old syntax -->
<!-- "nonSystemTagsFilter", "systemTagsFilter" __need to be the same__ as fields: "first-search-filter", "second-search-filter" --> <!-- "nonSystemTagsFilter", "systemTagsFilter" __need to be the same__ as fields: "first-search-filter", "second-search-filter" -->
<$let <$let
palette={{$:/palette}} palette="$:/temp/palette-colours"
colourA={{{ [<palette>getindex[foreground]] }}} colourA={{{ [<palette>getindex[foreground]] }}}
colourB={{{ [<palette>getindex[background]] }}} colourB={{{ [<palette>getindex[background]] }}}
fallbackTarget={{{ [<palette>getindex[tag-background]] }}} fallbackTarget={{{ [<palette>getindex[tag-background]] }}}
+4 -4
View File
@@ -28,15 +28,16 @@ color:$(foregroundColor)$;
</$let> </$let>
\end \end
<!-- Note that the 'palette' parameter is unused and is only retained for backwards compatibility -->
\define tag-pill-body(tag,icon,colour,palette,element-tag,element-attributes,actions) \define tag-pill-body(tag,icon,colour,palette,element-tag,element-attributes,actions)
\whitespace trim \whitespace trim
<$macrocall $name="tag-pill-inner" <$macrocall $name="tag-pill-inner"
tag=<<__tag__>> tag=<<__tag__>>
icon=<<__icon__>> icon=<<__icon__>>
colour=<<__colour__>> colour=<<__colour__>>
fallbackTarget={{$palette$##tag-background}} fallbackTarget=<<colour tag-background>>
colourA={{$palette$##foreground}} colourA=<<colour foreground>>
colourB={{$palette$##background}} colourB=<<colour background>>
element-tag=<<__element-tag__>> element-tag=<<__element-tag__>>
element-attributes=<<__element-attributes__>> element-attributes=<<__element-attributes__>>
actions=<<__actions__>> actions=<<__actions__>>
@@ -51,7 +52,6 @@ color:$(foregroundColor)$;
tag=<<__tag__>> tag=<<__tag__>>
icon={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerIconFilter]!is[draft]get[text]] }}} 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]] }}} colour={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}}
palette={{$:/palette}}
element-tag=<<__element-tag__>> element-tag=<<__element-tag__>>
element-attributes=<<__element-attributes__>> element-attributes=<<__element-attributes__>>
actions=<<__actions__>>/> actions=<<__actions__>>/>
+1 -1
View File
@@ -1,3 +1,3 @@
title: $:/palette title: $:/palette
$:/palettes/Vanilla $:/palettes/AutoToggle
+25 -14
View File
@@ -1,19 +1,30 @@
title: $:/snippets/paletteswitcher title: $:/snippets/paletteswitcher
\parameters (thumbnails)
\whitespace trim \whitespace trim
<$linkcatcher to="$:/palette"> \procedure actions()
<div class="tc-chooser"> <$action-setfield $tiddler="$:/palette" text=<<navigateTo>>/>
<$list filter="[all[shadows+tiddlers]tag[$:/tags/Palette]!is[draft]sort[name]]"> \end actions
<$set name="cls" filter="[all[current]prefix{$:/palette}]" value="tc-chooser-item tc-chosen" emptyValue="tc-chooser-item">
<div class=<<cls>>> <!-- Display palette in currentTiddler with selection logic -->
<$link to={{!!title}}> \procedure palette-entry(thumbnails)
''<$view field="name" format="text"/>'' <div class={{{ [<currentTiddler>match{$:/palette}then[tc-chosen]] tc-chooser-item +[join[ ]] }}}>
&#32;-&#32; <$link to={{!!title}} aria-label=`${[[$:/language/ControlPanel/Palette/Caption]get[text]]}$ - ${[all[current]get[name]]}$. ${[all[current]get[description]]}$`>
<$view field="description" format="text"/> <%if [<thumbnails>match[yes]] %>
{{||$:/snippets/currpalettepreview}} {{||$:/snippets/currpalettepreview}}
</$link> <%endif%>
</div> ''<$view field="name" format="text"/>''
</$set> <br>
</$list> <$view field="description" format="text"/>
</$link>
</div> </div>
\end palette-entry
<$linkcatcher actions=<<actions>>>
<div class={{{ tc-chooser [<thumbnails>match[yes]then[tc-chooser-cards]] +[join[ ]] }}}>
<$list filter="[all[shadows+tiddlers]tag[$:/tags/Palette]!is[draft]sort[name]]">
<$transclude $variable="palette-entry" thumbnails=<<thumbnails>>/>
</$list>
</div>
</$linkcatcher> </$linkcatcher>

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