twMat
August 3, 2025, 9:16am
1
[These thoughts were spurred from @saqimtiaz autosave experiments ]
For the sake of this thread, I’m here defining three terms but kindly enlighten me if there is established, or simply better, terminology:
enable autosave ~ to click the checkbox inControlpanel > Saving > General > "save changes automatically"
trigger autosave ~ to “start preparing” for autosaving
execute autosave ~ autosaving actually starts to save the wiki
Default behaviour in TW is that triggering and executing autosave happens without a delay inbetween.
Request :
Autosave would be much more useful if:
it triggers from making any change in the wiki
…and if there then was a delay between triggering and executing the saving, i.e some timer countdown to avoid non-stop saving.
Regarding (1.) - currently Autosave is only triggered from clicking the tiddler editview Done button. But the system does know when “any change” is made as evidenced from the save-wiki button turning red.
The necessity to click Done means that very basic uses cases such as clicking a button to set some value, or editing transcluded content do not trigger autosave.
Could the behaviour of Autosave be enhanced to feature this?
…
(Note: @saqimtiaz 's experimental implementation does not address this aspect but accepts the requirement to click Done and allows you to only then control the duration before the save is executed.)
twMat
August 3, 2025, 9:36am
3
Thanks but how would one send that message when “any change” is made?
One option would be background actions, planned for v5.4.0 and a part of this PR:
master ← colour-improvements
opened 09:02AM - 25 Oct 24 UTC
**UPDATE 6th FEBRUARY 2026**
After some discussion, this PR is now deferred t… o v5.5.0 to give more time to iron out some details. In the meantime, background actions, filter tracking and media query tracking have been cherrypicked into #9641 so that they can be included in v5.4.0.
# Introduction
This PR brings several new features for end users:
* Automatically switching between a dark and light palette as the operating system setting changes (and to do so without making the wiki dirty)
* Customisation options for palettes. For example, users might choose a base hue, with the colours of the palette automatically adapting to it
* A generalisation of the dark vs. light mechanism to allow an arbitrary number of distinct schemes that are dynamically selected. For example, a palette that has a different scheme for night, morning, day and evening that automatically change with the time of day
There are also new capabilities for palette authors:
* Inheritance for palettes, making it easy to create chains of variants of a base palette
* Self contained palettes that can contain both dark and light variants (or variants for any other custom scheme)
To make all of these new features possible, this PR also includes some useful new general purpose mechanisms and features:
* Background actions that are triggered whenever there is a change to the results of a specified filter
* Several new filter operators for manipulating colour values. The underlying functionality comes from the [color.js](https://colorjs.io/) library
* New media query tracking mechanism that can track the results of any CSS media query (not just dark mode), storing the results in a shadow `$:/info/...` tiddler
* New `changecount` filter operator
* New `:apply` filter run prefix
# Try the Demo
A preview build is available at https://deploy-preview-8702--tiddlywiki-previews.netlify.app/
<img width="1191" alt="image" src="https://github.com/user-attachments/assets/48f7d9df-9cff-4cb5-b723-720e63c73260" />
# Changes to Palettes
Palette entries are now defined as filters that must be evaluated, rather than wikitext that must be wikified.
This makes it possible to create palettes that reference and modify other colours. For example:
```
tiddler-controls-foreground-selected: [tf.interpolate-colours[background],[foreground],[0.9]]
```
Retrospectively re-interpreting palettes entries as filters would normally mean that `<<colour background>>` would no longer work. Instead these entries are automatically converted to the updated form `[function[colour],[background]]`.
# Palette Compilation
The key idea underpinning these changes is a fundamental change to the way that TiddlyWiki handles palettes. At the moment, palette entries are named items that can contain either a CSS colour string, a CSS colour token like "inherit", or can use the `<<colour>>` macro to reference another colour palette entry. Thus, palette entries have to be wikified before they can be used. This has turned out to be very limiting and doesn't provide a viable path to the complex colour manipulations shown above. Switching to filters might make things worse, by encouraging authors to use complex expressions within palettes.
The fix is compiled palettes: at the point of switching to a new palette, the colours within it are "compiled" to raw CSS colour values (typically but not necessarily in `#rrggbbaa` format). This allows palette entries to be used directly, without the requirement to wikify them.
The static palette is created in a new system tiddler `$:/temp/palette-colours` by an action procedure that is invoked at startup and when switching to a new palette.
There should not be any backwards compatibility issues because the use of background actions means that any code that changes `$:/palette` will automatically trigger the recompilation of the palette.
This change also allows us to change the `<<colour>>` procedure to be a function, which allows it to be used as the value for a style attribute:
```
<div style.background=<<colour tiddler-background>>>
```
# Automatic Palette Readability Tests
Palettes can opt to include readability tests as special palette entries. The results of these tests are shown at the bottom of the palette chooser. For example:
```
?base-background-ink: [tf.check-colour-contrast[base-background],[base-ink],[45]]
```
The test framework looks for palette entries starting with a question mark and runs the associated filters. The filter should return nothing if there are no errors, or a textual error message if the conditions are violated. Sample output:
```
alert-contrast: 42.357: alert-background/foreground contrast is too low
background-foreground-contrast: 41.915: background/foreground contrast is too low
base-ink-secondary: 42.357: base-ink/base-secondary contrast is too low
base-paper-ink: 41.915: base-paper/base-ink contrast is too low
base-paper-primary: 32.682: base-paper/base-primary contrast is too low
base-paper-tertiary: 30.502: base-paper/base-tertiary contrast is too low
code-contrast: 28.565: code-background/code-foreground contrast is too low
```
# Colour Manipulation
We need a colour manipulation library that can calculate variants of colours. Only [color.js](https://colorjs.io/) met the requirements of being able to work with P3 colours and the OKLCH colour space. It also includes a CSS colour string parser which can replace the simple one that TiddlyWiki has always incorporated.
# Media Query Tracker
The CSS media query tracker allows a media query to be bound to an info tiddler so that the current state of the query is reflected in the value of the tiddler. The value is updated dynamically.
The use of the info mechanism for the CSS media query tracker means that these tiddlers are dynamically created as shadow tiddlers within the `$:/temp/info` plugin, and so do not appear in tiddler lists.
The mechanism is used to implement the existing dark mode tiddler with the following configuration:
```
title: $:/core/wiki/config/MediaQueryTrackers/DarkLightSwitch
tags: $:/tags/MediaQueryTracker
media-query: (prefers-color-scheme: dark)
info-tiddler: $:/info/browser/darkmode
info-tiddler-alt: $:/info/darkmode
```
Note the use of `info-tiddler-alt` to specify a redundant secondary info tiddler. This is used by the dark mode tracker to maintain compatibility while changing the info tiddler title for consistency.
# Background Actions
The new background actions mechanism allows action strings to be invoked automatically in the background whenever the results of a filter change.
The preview includes a demonstration background action that displays an alert with a list of tiddlers in the story river whenever the story river changes:
```
title: SampleBackgroundAction: Story Change
tags: $:/tags/BackgroundAction
track-filter: [list[$:/StoryList]]
<$action-sendmessage $message="tm-notify" $param="SampleBackgroundAction: Story Change" list={{$:/StoryList!!list}}/>
Story List:
<ol>
<$list filter="[enlist<list>]">
<li>
<$text text=<<currentTiddler>>/>
</li>
</$list>
</ol>
```
# `apply` Filter Run Prefix
An experimental new filter run prefix that makes it possible to use computed values as variables within a filter run. For example:
```
\function tf.interpolate-colours(paletteEntryA,paletteEntryB,weight)
[function[colour],<paletteEntryA>] [function[colour],<paletteEntryB>] :apply[<weight>colour-interpolate:oklch<$1>,<$2>]
\end tf.interpolate-colours
```
# Backwards Compatibility
* The current content of `$:/PaletteManager` is moved into `$:/PaletteEditor`, and `$:/PaletteManager` repurposed as the control panel palette switcher
* `$:/config/DefaultColourMappings/` now only supports CSS colours, and not indirections via `<<colour X>>` or `[function[colour],[X]]`
# References
* The media query tracker and background actions mechanisms were originally developed in a [separate pull request](#8555)
* #5236 discusses the general idea of dynamically computed colours, and includes a [very neat demo](https://github.com/TiddlyWiki/TiddlyWiki5/issues/5236#issuecomment-890428532) from @cdruan
* A thread discussing using the colour macro as an attribute value - https://talk.tiddlywiki.org/t/how-to-use-the-colour-macro-as-an-attribute-value/10051
* A thread discussing improvements to colour handling, including a similar idea for palette previews - https://talk.tiddlywiki.org/t/idea-approach-to-palettes-and-themes/9431
# Progress
* Documentation
* [ ] Palettes and colour schemes
* [ ] Background palettes (which should probably be renamed!)
* [ ] Media Query Tracker
* [ ] Background actions
* [ ] `changecount` operator
* [ ] `colour-interpolate` operator
* [ ] `colour-lighten` operator
* [ ] `colour-darken` operator
* [ ] `colour-get-oklch` operator
* [ ] `colour-set-oklch` operator
* [ ] `colour-contrast` operator
* [ ] `colour-best-contrast` operator
* [ ] Adopt palettes from https://yatagarasu.tiddlyhost.com/
* Background Actions
* [ ] Explore supporting long running asynchronous background tasks (v5.5.0)
Background actions that are triggered whenever there is a change to the results of a specified filter
pmario
August 3, 2025, 4:03pm
5
I think the main problem that Saq wants to tackle in the other thread is, that triggering autosave rapidly, can cause problems if you save to a server.
Eg: Triggering autosave for a big wiki 3 times in 1 second, will cause problems, since the first save is not finished, when the second save is already triggered. Most likely you will get a server error, with an unknown save state.
simon
August 3, 2025, 4:52pm
6
I think [IDEA] Autosave frequency throttle · Issue #6979 · TiddlyWiki/TiddlyWiki5 · GitHub is similar to this FYI.
Edit: I posted this before I had read through the other thread . Now I see that there are already references that link back to this issue, so my comment isn’t super useful. Apologies!
That is how nodejs wiki works, don’t you use nodejs tiddlywiki?
After you use it, you only need to worry about “auto backup”. When to backup whole wiki will still needs consideration.
twMat
August 4, 2025, 2:12pm
8
No I use single files and the majority are on Tiddlyhost where the OP would be very useful.
Yaisog
August 4, 2025, 3:04pm
9
Back when I had single-file wikis I had modified the saver-handler.js so that every change to the tiddler store would trigger a configurable timer that would initiate a save and delete any timer that might have already been running.
The wiki would therefore only save when I had not been working on it for some fixed time, but never as long as I was actively working on it. Depending on your workflow and the length of your “sessions” you might also want to trigger a save after some maximum interval, but I found the wait time during save to be always very distracting (around 10 seconds in my case IIRC).
The code was mostly this:
if(self.numChanges > 0) {
if (self.saveTimer) clearTimeout(self.saveTimer);
self.saveTimer = setTimeout( function() {
self.saveWiki({
method: "save",
downloadType: "text/plain"
});
}, SaveDelay);
}