1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2026-02-19 00:19:50 +00:00

Compare commits

..

12 Commits

Author SHA1 Message Date
Jeremy Ruston
f879313d9b Merge branch 'master' into media-query-tracker 2025-01-23 18:11:33 +00:00
Jeremy Ruston
d97a163e30 Merge branch 'master' into media-query-tracker 2025-01-23 14:37:32 +00:00
Jeremy Ruston
365b734a22 Add another background action demo 2025-01-21 16:34:38 +00:00
Jeremy Ruston
818a9977a3 Introduce background actions
Background actions are invoked when the results of a filter change.

The sample background action is just for demo purposes to show how an automatic dark/light mode feature would be powered
2025-01-07 15:09:13 +00:00
Jeremy Ruston
508c74f8e4 Refactor filter tracker
1. Pass params as an options object
2. Add fnProcess which is called during processing after enter, leave and change have been called
2025-01-07 14:53:38 +00:00
Jeremy Ruston
806960afc7 Remove duplicates from filter tracker results 2025-01-06 16:43:02 +00:00
Jeremy Ruston
ed2c7c90de Be more defensive about missing parameters 2025-01-06 11:38:12 +00:00
Jeremy Ruston
964ced35ff Add untrack method to filter tracker 2025-01-06 11:37:58 +00:00
Jeremy Ruston
e78d3ae04e Introduce new generic filter tracker
The new filter tracker is a generic JS mechanism for tracking changes to the results of filters and changes to the tiddlers identified by those results.

The rationale for the new mechanism is that it is a generalisation of code that is already present in the keyboard manager. The intention is to refactor the keyboard manager to use the filter tracker in due course
2025-01-05 17:32:15 +00:00
Jeremy Ruston
ea2426eea1 Merge branch 'master' into media-query-tracker 2025-01-04 14:26:14 +00:00
Jeremy Ruston
7f9b8bf0c1 Fixes suggested by @pmario 2024-08-27 17:40:07 +01:00
Jeremy Ruston
ba17e342b2 Introduce new MediaQueryTrackerMechanism, and use it for dark mode 2024-08-27 16:34:58 +01:00
32 changed files with 419 additions and 118 deletions

View File

@@ -2463,15 +2463,13 @@ $tw.boot.initStartup = function(options) {
$tw.utils.registerFileType("image/webp","base64",".webp",{flags:["image"]});
$tw.utils.registerFileType("image/heic","base64",".heic",{flags:["image"]});
$tw.utils.registerFileType("image/heif","base64",".heif",{flags:["image"]});
$tw.utils.registerFileType("image/avif","base64",".avif",{flags:["image"]});
$tw.utils.registerFileType("image/svg+xml","utf8",".svg",{flags:["image"]});
$tw.utils.registerFileType("image/vnd.microsoft.icon","base64",".ico",{flags:["image"]});
$tw.utils.registerFileType("image/x-icon","base64",".ico",{flags:["image"]});
$tw.utils.registerFileType("application/wasm","base64",".wasm");
$tw.utils.registerFileType("font/woff","base64",".woff");
$tw.utils.registerFileType("font/woff2","base64",".woff2");
$tw.utils.registerFileType("font/ttf","base64",".ttf");
$tw.utils.registerFileType("font/otf","base64",".otf");
$tw.utils.registerFileType("application/font-woff","base64",".woff");
$tw.utils.registerFileType("application/x-font-ttf","base64",".woff");
$tw.utils.registerFileType("application/font-woff2","base64",".woff2");
$tw.utils.registerFileType("audio/ogg","base64",".ogg");
$tw.utils.registerFileType("audio/mp4","base64",[".mp4",".m4a"]);
$tw.utils.registerFileType("video/ogg","base64",[".ogm",".ogv",".ogg"]);

View File

@@ -0,0 +1,117 @@
/*\
title: $:/core/modules/background-actions.js
type: application/javascript
module-type: global
Class to dispatch actions when filters change
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
function BackgroundActionDispatcher(filterTracker,wiki) {
var self = this;
this.filterTracker = filterTracker;
this.wiki = wiki;
this.nextTrackedFilterId = 1;
this.trackedFilters = Object.create(null); // Hashmap by id
// Track the filter for the background actions
this.filterTracker.track({
filterString: "[all[tiddlers+shadows]tag[$:/tags/BackgroundAction]!is[draft]]",
fnEnter: function fnEnter(title) {
return self.trackFilter(title);
},
fnLeave: function fnLeave(title,enterValue) {
self.untrackFilter(enterValue);
},
fnChange: function fnChange(title,enterValue) {
self.untrackFilter(enterValue);
return self.trackFilter(title);
},
fnProcess: function fnProcess(changes) {
self.process(changes);
}
});
}
BackgroundActionDispatcher.prototype.trackFilter = function(title) {
var tiddler = this.wiki.getTiddler(title),
id = this.nextTrackedFilterId++,
tracker = new BackgroundActionTracker({
wiki: this.wiki,
title: title,
trackFilter: tiddler.fields["track-filter"],
actions: tiddler.fields.text
});
this.trackedFilters[id] = tracker;
return id;
};
BackgroundActionDispatcher.prototype.untrackFilter = function(enterValue) {
var tracker = this.trackedFilters[enterValue];
if(tracker) {
tracker.destroy();
}
delete this.trackedFilters[enterValue];
};
BackgroundActionDispatcher.prototype.process = function(changes) {
for(var id in this.trackedFilters) {
this.trackedFilters[id].process(changes);
}
};
/*
Represents an individual tracked filter. Options include:
wiki: wiki to use
title: title of the tiddler being tracked
trackFilter: filter string to track changes
actions: actions to be executed when the filter changes
*/
function BackgroundActionTracker(options) {
var self = this;
this.wiki = options.wiki;
this.title = options.title;
this.trackFilter = options.trackFilter;
this.actions = options.actions
this.filterTracker = new $tw.FilterTracker(this.wiki);
this.hasChanged = false;
this.trackerID = this.filterTracker.track({
filterString: this.trackFilter,
fnEnter: function(title) {
self.hasChanged = true;
},
fnLeave: function(title,enterValue) {
self.hasChanged = true;
},
fnProcess: function(changes) {
if(self.hasChanged) {
self.hasChanged = false;
self.wiki.invokeActionString(
self.actions,
null,
{
title: self.title
},{
parentWidget: $tw.rootWidget
}
);
}
}
});
}
BackgroundActionTracker.prototype.process = function(changes) {
this.filterTracker.handleChangeEvent(changes);
};
BackgroundActionTracker.prototype.destroy = function() {
this.filterTracker.untrack(this.trackerID);
};
exports.BackgroundActionDispatcher = BackgroundActionDispatcher;
})();

View File

@@ -0,0 +1,108 @@
/*\
title: $:/core/modules/filter-tracker.js
type: application/javascript
module-type: global
Class to track the results of a filter string
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
function FilterTracker(wiki) {
this.wiki = wiki;
this.trackers = [];
this.nextTrackerId = 1;
}
FilterTracker.prototype.handleChangeEvent = function(changes) {
this.processTrackers();
this.processChanges(changes);
};
/*
Add a tracker to the filter tracker. Returns null if any of the parameters are invalid, or a tracker id if the tracker was added successfully. Options include:
filterString: the filter string to track
fnEnter: function to call when a title enters the filter results. Called even if the tiddler does not actually exist. Called as (title), and should return a truthy value that is stored in the tracker as the "enterValue"
fnLeave: function to call when a title leaves the filter results. Called as (title,enterValue)
fnChange: function to call when a tiddler changes in the filter results. Only called for filter results that identify a tiddler or shadow tiddler. Called as (title,enterValue), and may optionally return a replacement enterValue
fnProcess: function to call each time the tracker is processed, after any enter, leave or change functions are called. Called as (changes)
*/
FilterTracker.prototype.track = function(options) {
// Add the tracker details
var tracker = {
id: this.nextTrackerId++,
filterString: options.filterString,
fnEnter: options.fnEnter,
fnLeave: options.fnLeave,
fnChange: options.fnChange,
fnProcess: options.fnProcess,
previousResults: [], // Results from the previous time the tracker was processed
resultValues: {} // Map by title to the value returned by fnEnter
};
this.trackers.push(tracker);
// Process the tracker
this.processTracker(this.trackers.length - 1);
return tracker.id;
};
FilterTracker.prototype.untrack = function(id) {
for(var t=0; t<this.trackers.length; t++) {
if(this.trackers[t].id === id) {
this.trackers.splice(t,1);
break;
}
}
};
FilterTracker.prototype.processTrackers = function() {
for(var t=0; t<this.trackers.length; t++) {
this.processTracker(t);
}
};
FilterTracker.prototype.processTracker = function(index) {
var tracker = this.trackers[index];
var results = [];
// Evaluate the filter and remove duplicate results
$tw.utils.each(this.wiki.filterTiddlers(tracker.filterString),function(title) {
$tw.utils.pushTop(results,title);
});
// Process the newly entered results
$tw.utils.each(results,function(title) {
if(tracker.previousResults.indexOf(title) === -1 && !tracker.resultValues[title] && tracker.fnEnter) {
tracker.resultValues[title] = tracker.fnEnter(title) || true;
}
});
// Process the results that have just left
$tw.utils.each(tracker.previousResults,function(title) {
if(results.indexOf(title) === -1 && tracker.resultValues[title] && tracker.fnLeave) {
tracker.fnLeave(title,tracker.resultValues[title]);
delete tracker.resultValues[title];
}
});
// Update the previous results
tracker.previousResults = results;
};
FilterTracker.prototype.processChanges = function(changes) {
for(var t=0; t<this.trackers.length; t++) {
var tracker = this.trackers[t];
$tw.utils.each(changes,function(change,title) {
if(title && tracker.previousResults.indexOf(title) !== -1 && tracker.fnChange) {
// Call the change function and if it doesn't return a value then keep the old value
tracker.resultValues[title] = tracker.fnChange(title,tracker.resultValues[title]) || tracker.resultValues[title];
}
});
if(tracker.fnProcess) {
tracker.fnProcess(changes);
}
}
};
exports.FilterTracker = FilterTracker;
})();

View File

@@ -17,24 +17,19 @@ Export our filter function
*/
exports.function = function(source,operator,options) {
var functionName = operator.operands[0],
params = [],
results;
params = [];
$tw.utils.each(operator.operands.slice(1),function(param) {
params.push({value: param});
});
// console.log(`Calling ${functionName} with params ${JSON.stringify(params)}`);
var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(functionName,{params: params, source: source});
if(variableInfo && variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) {
results = variableInfo.resultList ? variableInfo.resultList : [variableInfo.text];
return variableInfo.resultList ? variableInfo.resultList : [variableInfo.text];
}
// Return the input list if the function wasn't found
if(!results) {
results = [];
source(function(tiddler,title) {
results.push(title);
});
}
// console.log(`function ${functionName} with params ${JSON.stringify(params)} results: ${JSON.stringify(results)}`);
var results = [];
source(function(tiddler,title) {
results.push(title);
});
return results;
};

View File

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

View File

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

View File

@@ -40,7 +40,6 @@ exports["image/gif"] = ImageParser;
exports["image/webp"] = ImageParser;
exports["image/heic"] = ImageParser;
exports["image/heif"] = ImageParser;
exports["image/avif"] = ImageParser;
exports["image/x-icon"] = ImageParser;
exports["image/vnd.microsoft.icon"] = ImageParser;

View File

@@ -46,10 +46,8 @@ function SaverHandler(options) {
// Filter the changes so that we only count changes to tiddlers that we care about
var filteredChanges = self.filterFn.call(self.wiki,function(iterator) {
$tw.utils.each(changes,function(change,title) {
if(change.normal) {
var tiddler = self.wiki.getTiddler(title);
iterator(tiddler,title);
}
var tiddler = self.wiki.getTiddler(title);
iterator(tiddler,title);
});
});
// Adjust the number of changes

View File

@@ -16,6 +16,9 @@ Load core modules
exports.name = "load-modules";
exports.synchronous = true;
// Set to `true` to enable performance instrumentation
var PERFORMANCE_INSTRUMENTATION_CONFIG_TITLE = "$:/config/Performance/Instrumentation";
exports.startup = function() {
// Load modules
$tw.modules.applyMethods("utils",$tw.utils);
@@ -35,6 +38,19 @@ exports.startup = function() {
$tw.macros = $tw.modules.getModulesByTypeAsHashmap("macro");
$tw.wiki.initParsers();
$tw.Commander.initCommands();
// --------------------------
// The rest of the startup process here is not strictly to do with loading modules, but are needed before other startup
// modules are executed. It is easier to put them here than to introduce a new startup module
// --------------------------
// Set up the performance framework
$tw.perf = new $tw.Performance($tw.wiki.getTiddlerText(PERFORMANCE_INSTRUMENTATION_CONFIG_TITLE,"no") === "yes");
// Kick off the filter tracker
$tw.filterTracker = new $tw.FilterTracker($tw.wiki);
$tw.wiki.addEventListener("change",function(changes) {
$tw.filterTracker.handleChangeEvent(changes);
});
// Kick off the background action dispatcher
$tw.backgroundActionDispatcher = new $tw.BackgroundActionDispatcher($tw.filterTracker,$tw.wiki);
};
})();

View File

@@ -75,7 +75,7 @@ exports.startup = function() {
$tw.wiki.unpackPluginTiddlers();
// Queue change events for the changed shadow tiddlers
$tw.utils.each(Object.keys(changedShadowTiddlers),function(title) {
$tw.wiki.enqueueTiddlerEvent(title,changedShadowTiddlers[title], true);
$tw.wiki.enqueueTiddlerEvent(title,changedShadowTiddlers[title]);
});
}
}

View File

@@ -77,9 +77,8 @@ exports.startup = function() {
$tw.rootWidget.addEventListener("tm-copy-to-clipboard",function(event) {
$tw.utils.copyToClipboard(event.param,{
successNotification: event.paramObject && event.paramObject.successNotification,
failureNotification: event.paramObject && event.paramObject.failureNotification,
plainText: event.paramObject && event.paramObject.plainText
},event.paramObject && event.paramObject.type);
failureNotification: event.paramObject && event.paramObject.failureNotification
});
});
// Install the tm-focus-selector message
$tw.rootWidget.addEventListener("tm-focus-selector",function(event) {

View File

@@ -17,9 +17,6 @@ exports.name = "startup";
exports.after = ["load-modules"];
exports.synchronous = true;
// Set to `true` to enable performance instrumentation
var PERFORMANCE_INSTRUMENTATION_CONFIG_TITLE = "$:/config/Performance/Instrumentation";
var widget = require("$:/core/modules/widgets/widget.js");
exports.startup = function() {
@@ -57,8 +54,6 @@ exports.startup = function() {
}
// Initialise version
$tw.version = $tw.utils.extractVersionInfo();
// Set up the performance framework
$tw.perf = new $tw.Performance($tw.wiki.getTiddlerText(PERFORMANCE_INSTRUMENTATION_CONFIG_TITLE,"no") === "yes");
// Create a root widget for attaching event handlers. By using it as the parentWidget for another widget tree, one can reuse the event handlers
$tw.rootWidget = new widget.widget({
type: "widget",
@@ -67,6 +62,14 @@ exports.startup = function() {
wiki: $tw.wiki,
document: $tw.browser ? document : $tw.fakeDocument
});
// Execute any startup actions
$tw.rootWidget.invokeActionsByTag("$:/tags/StartupAction");
if($tw.browser) {
$tw.rootWidget.invokeActionsByTag("$:/tags/StartupAction/Browser");
}
if($tw.node) {
$tw.rootWidget.invokeActionsByTag("$:/tags/StartupAction/Node");
}
// Kick off the language manager and switcher
$tw.language = new $tw.Language();
$tw.languageSwitcher = new $tw.PluginSwitcher({
@@ -109,14 +112,6 @@ exports.startup = function() {
handlerMethod: "handleKeydownEvent"
}]);
}
// Execute any startup actions
$tw.rootWidget.invokeActionsByTag("$:/tags/StartupAction");
if($tw.browser) {
$tw.rootWidget.invokeActionsByTag("$:/tags/StartupAction/Browser");
}
if($tw.node) {
$tw.rootWidget.invokeActionsByTag("$:/tags/StartupAction/Node");
}
// Clear outstanding tiddler store change events to avoid an unnecessary refresh cycle at startup
$tw.wiki.clearTiddlerEventQueue();
// Find a working syncadaptor

View File

@@ -268,10 +268,9 @@ exports.copyStyles = function(srcDomNode,dstDomNode) {
/*
Copy plain text to the clipboard on browsers that support it
*/
exports.copyToClipboard = function(text,options,type) {
var text = text || "";
var options = options || {};
var type = type || "text/plain";
exports.copyToClipboard = function(text,options) {
options = options || {};
text = text || "";
var textArea = document.createElement("textarea");
textArea.style.position = "fixed";
textArea.style.top = 0;
@@ -284,16 +283,10 @@ exports.copyToClipboard = function(text,options,type) {
textArea.style.outline = "none";
textArea.style.boxShadow = "none";
textArea.style.background = "transparent";
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
textArea.setSelectionRange(0,text.length);
textArea.addEventListener("copy",function(event) {
event.preventDefault();
if (options.plainText) {
event.clipboardData.setData("text/plain",options.plainText);
}
event.clipboardData.setData(type,text);
});
var succeeded = false;
try {
succeeded = document.execCommand("copy");

View File

@@ -216,11 +216,11 @@ HttpClientRequest.prototype.send = function(callback) {
if(lengthComputable) {
setBinding(self.bindProgress,"" + Math.floor((loaded/total) * 100))
}
self.wiki.invokeActionString(self.progressActions,undefined,$tw.utils.extend({},self.variables,{
self.wiki.invokeActionString(self.progressActions,undefined,{
lengthComputable: lengthComputable ? "yes" : "no",
loaded: loaded,
total: total
}),{parentWidget: $tw.rootWidget});
},{parentWidget: $tw.rootWidget});
}
});
}

View File

@@ -43,6 +43,15 @@ EditWidget.prototype.execute = function() {
// Get our parameters
this.editTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler"));
this.editField = this.getAttribute("field","text");
this.editIndex = this.getAttribute("index");
this.editClass = this.getAttribute("class");
this.editPlaceholder = this.getAttribute("placeholder");
this.editTabIndex = this.getAttribute("tabindex");
this.editFocus = this.getAttribute("focus","");
this.editCancelPopups = this.getAttribute("cancelPopups","");
this.editInputActions = this.getAttribute("inputActions");
this.editRefreshTitle = this.getAttribute("refreshTitle");
this.editAutoComplete = this.getAttribute("autocomplete");
// Choose the appropriate edit widget
this.editorType = this.getEditorType();
// Make the child widgets
@@ -80,8 +89,8 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
*/
EditWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
// Refresh if the editor type has changed
if(changedAttributes.tiddler || changedAttributes.field || (this.getEditorType() !== this.editorType)) {
// Refresh if an attribute has changed, or the type associated with the target tiddler has changed
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes.tabindex || changedAttributes.cancelPopups || changedAttributes.inputActions || changedAttributes.refreshTitle || changedAttributes.autocomplete || (this.getEditorType() !== this.editorType)) {
this.refreshSelf();
return true;
} else {

View File

@@ -23,21 +23,15 @@ Inherit from the base widget class
*/
GenesisWidget.prototype = new Widget();
GenesisWidget.prototype.computeAttributes = function(options) {
options = options || Object.create(null);
options.filterFn = function(name) {
// Only compute our own attributes which start with a single dollar
return name.charAt(0) === "$" && name.charAt(1) !== "$";
}
return Widget.prototype.computeAttributes.call(this,options);
};
/*
Render this widget into the DOM
*/
GenesisWidget.prototype.render = function(parent,nextSibling) {
this.parentDomNode = parent;
this.computeAttributes();
this.computeAttributes({filterFn: function(name) {
// Only compute our own attributes which start with a single dollar
return name.charAt(0) === "$" && name.charAt(1) !== "$";
}});
this.execute();
this.renderChildren(parent,nextSibling);
};

View File

@@ -141,15 +141,12 @@ This method should be called after the changes it describes have been made to th
title: Title of tiddler
isDeleted: defaults to false (meaning the tiddler has been created or modified),
true if the tiddler has been deleted
isShadow: defaults to false (meaning the change applies to the normal tiddler),
true if the tiddler being changed is a shadow tiddler
*/
exports.enqueueTiddlerEvent = function(title,isDeleted,isShadow) {
exports.enqueueTiddlerEvent = function(title,isDeleted) {
// Record the touch in the list of changed tiddlers
this.changedTiddlers = this.changedTiddlers || Object.create(null);
this.changedTiddlers[title] = this.changedTiddlers[title] || Object.create(null);
this.changedTiddlers[title][isDeleted ? "deleted" : "modified"] = true;
this.changedTiddlers[title][isShadow ? "shadow" : "normal"] = true;
// Increment the change count
this.changeCount = this.changeCount || Object.create(null);
if($tw.utils.hop(this.changeCount,title)) {

View File

@@ -78,13 +78,13 @@ code-background: <<colour background>>
code-border: <<colour flexoki-tx>>
code-foreground: <<colour flexoki-tx>>
diff-delete-background: <<colour flexoki-re>>
diff-delete-foreground: <<colour flexoki-bg-2>>
diff-delete-foreground: <<colour flexoki-paper>>
diff-equal-background:
diff-equal-foreground: inherit
diff-insert-background: <<colour flexoki-gr>>
diff-insert-foreground: <<colour flexoki-bg-2>>
diff-insert-foreground: <<colour flexoki-paper>>
diff-invisible-background: <<colour flexoki-ye>>
diff-invisible-foreground: <<colour flexoki-bg-2>>
diff-invisible-foreground: <<colour flexoki-paper>>
dirty-indicator: <<colour flexoki-re>>
download-background: <<colour flexoki-cy-2>>
download-foreground: <<colour background>>
@@ -103,8 +103,8 @@ external-link-foreground-visited: <<colour flexoki-bl>>
external-link-foreground: <<colour flexoki-bl>>
footnote-target-background: <<colour flexoki-bg-2>>
foreground: #CECDC3
highlight-background: <<colour flexoki-yellow-900>>
highlight-foreground: inherit
highlight-background: <<colour flexoki-cyan-950>>
highlight-foreground: <<colour flexoki-cyan-400>>
menubar-background: <<colour primary>>
menubar-foreground: <<colour flexoki-paper>>
message-background: <<colour background>>

View File

@@ -105,7 +105,7 @@ external-link-foreground-visited: <<colour flexoki-bl>>
external-link-foreground: <<colour flexoki-bl>>
footnote-target-background: <<colour flexoki-bg-2>>
foreground: #100F0F
highlight-background: <<colour flexoki-yellow-100>>
highlight-background: <<colour flexoki-cyan-050>>
highlight-foreground: inherit
menubar-background: <<colour primary>>
menubar-foreground: <<colour flexoki-paper>>

View File

@@ -0,0 +1,9 @@
title: SampleBackgroundAction: Dark Mode
tags: $:/tags/BackgroundAction
track-filter: [{$:/info/browser/darkmode}]
<%if [{$:/info/browser/darkmode}match[no]] %>
<$action-setfield $tiddler="$:/palette" text="$:/palettes/Vanilla"/>
<%else%>
<$action-setfield $tiddler="$:/palette" text="$:/palettes/SolarizedDark"/>
<%endif%>

View File

@@ -0,0 +1,15 @@
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>

View File

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

View File

@@ -3,11 +3,9 @@ tags: $:/tags/Macro
\whitespace trim
\procedure copy-to-clipboard(src,class:"tc-btn-invisible",style,type:"text/plain",plain)
\procedure copy-to-clipboard-actions()
<$action-sendmessage $message="tm-copy-to-clipboard" $param=<<src>> type=<<type>> plainText=<<plain>>/>
\end copy-to-clipboard-actions
<$button actions=<<copy-to-clipboard-actions>>
\procedure copy-to-clipboard(src,class:"tc-btn-invisible",style)
<$button message="tm-copy-to-clipboard"
param=<<src>>
class=<<class>>
style=<<style>>
tooltip={{$:/language/Buttons/CopyToClipboard/Hint}}
@@ -17,12 +15,12 @@ tags: $:/tags/Macro
<$text text={{$:/language/Buttons/CopyToClipboard/Caption}}/>
</span>
</$button>
\end copy-to-clipboard
\end
\procedure copy-to-clipboard-above-right(src,class:"tc-btn-invisible",style,type:"text/plain")
<div style.position="relative">
<div style.position="absolute" style.bottom="0" style.right="0">
<$transclude $variable="copy-to-clipboard" src=<<src>> class=<<class>> style=<<style>> type=<<type>> plain=<<plain>>/>
\procedure copy-to-clipboard-above-right(src,class:"tc-btn-invisible",style)
<div style="position: relative;">
<div style="position: absolute; bottom: 0; right: 0;">
<$macrocall $name="copy-to-clipboard" src=<<src>> class=<<class>> style=<<style>>/>
</div>
</div>
\end

View File

@@ -1,6 +1,6 @@
caption: copy-to-clipboard
created: 20171216104754967
modified: 20250127133558352
modified: 20171216104941967
tags: Macros [[Core Macros]]
title: copy-to-clipboard Macro
type: text/vnd.tiddlywiki
@@ -15,9 +15,5 @@ The <<.def copy-to-clipboard>> [[macro|Macros]] displays a button that copies sp
: Optional CSS classes to be assigned to the button (defaults to `tc-btn-invisible`)
;style
: Optional CSS styles to be assigned to the button
;type
: <<.from-version "5.3.7">> MIME type of the text to be copied, defaults to `text/plain`
;plain
: <<.from-version "5.3.7">> Additional plain text to be copied when `type` attribute isn't `text/plain`
<<.macro-examples "copy-to-clipboard">>

View File

@@ -1,14 +1,8 @@
created: 20171216104946277
modified: 20250127134344834
modified: 20171216105109641
tags: [[copy-to-clipboard Macro]] [[Macro Examples]]
title: copy-to-clipboard Macro (Examples)
type: text/vnd.tiddlywiki
<$macrocall $name=".example" n="1" eg="""<<copy-to-clipboard "Mary had a little lamb">>"""/>
<$macrocall $name=".example" n="2" eg="""<$transclude $variable="copy-to-clipboard" src={{$:/SiteTitle}}/>"""/>
In the following examples, press <kbd>ctrl-V</kbd> / <kbd>cmd-V</kbd> in tiddlywiki after copying to see its effects.
<$macrocall $name=".example" n="3" eg="""<<copy-to-clipboard src:"<em>Test</em>" type:"text/html" plain:"Test">> """/>
<$macrocall $name=".example" n="4" eg="""<<copy-to-clipboard src:"The ''quick'' //brown// __fox__ jumps over a `lazy` @@dog@@." type:"text/vnd.tiddlywiki" plain:"The quick brown box jumps over a lazy dog.">> """/>
<$macrocall $name=".example" n="5" eg="""<$transclude $variable="copy-to-clipboard" src=<<jsontiddlers filter:"[tag[Concepts]]">> type="text/vnd.tiddler"/>"""/>
<$macrocall $name=".example" n="2" eg="""<$macrocall $name="copy-to-clipboard" src={{$:/SiteTitle}}/>"""/>

View File

@@ -4,8 +4,8 @@ tags: Mechanisms
title: InfoMechanism
type: text/vnd.tiddlywiki
\define example(name)
<$transclude tiddler="""$:/info/url/$name$""" mode="inline"/>
\procedure example(name)
<$text text={{{ [[$:/info/url/]addsuffix<name>get[text]] }}} />
\end
System tiddlers in the namespace `$:/info/` are used to expose information about the system (including the current browser) so that WikiText applications can adapt themselves to available features.
@@ -19,6 +19,8 @@ System tiddlers in the namespace `$:/info/` are used to expose information about
|[[$:/info/browser/language]] |<<.from-version "5.1.20">> Language as reported by browser (note that some browsers report two character codes such as `en` while others report full codes such as `en-GB`) |
|[[$:/info/browser/screen/width]] |Screen width in pixels |
|[[$:/info/browser/screen/height]] |Screen height in pixels |
|[[$:/info/browser/darkmode]] |<<.from-version "5.3.7">> Is dark mode preferred? ("yes" or "no") |
|[[$:/info/darkmode]] |<<.deprecated-since "5.3.7">> Alias for $:/info/browser/darkmode |
|[[$:/info/node]] |Running under [[Node.js]]? ("yes" or "no") |
|[[$:/info/url/full]] |<<.from-version "5.1.14">> Full URL of wiki (eg, ''<<example full>>'') |
|[[$:/info/url/host]] |<<.from-version "5.1.14">> Host portion of URL of wiki (eg, ''<<example host>>'') |
@@ -28,4 +30,3 @@ System tiddlers in the namespace `$:/info/` are used to expose information about
|[[$:/info/url/port]] |<<.from-version "5.1.14">> Port portion of URL of wiki (eg, ''<<example port>>'') |
|[[$:/info/url/protocol]] |<<.from-version "5.1.14">> Protocol portion of URL of wiki (eg, ''<<example protocol>>'') |
|[[$:/info/url/search]] |<<.from-version "5.1.14">> Search portion of URL of wiki (eg, ''<<example search>>'') |
|[[$:/info/darkmode]] |<<.from-version "5.1.23">> Is dark mode enabled? ("yes" or "no") |

View File

@@ -0,0 +1,8 @@
title: MediaQueryTrackerMechanism
tags: Mechanisms
<<.from-version "5.3.7">> The media query tracker mechanism allows you to define [[custom CSS media queries|https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries]] to be bound to a specified [[info|InfoMechanism]] tiddler. The info tiddler will be dynamically update to reflect the current state of the media query.
Adding or modifying a tiddler tagged $:/tags/MediaQueryTracker takes effect immediately.
The media queries are always applied against the main window. This is relevant for viewport related media queries such as `min-width` which will always respect the main window and ignore the sizes of any external windows.

View File

@@ -1,6 +1,6 @@
caption: tm-copy-to-clipboard
created: 20171215150056004
modified: 20250127134445040
modified: 20240523174013095
tags: Messages
title: WidgetMessage: tm-copy-to-clipboard
type: text/vnd.tiddlywiki
@@ -13,8 +13,6 @@ It requires the following properties on the `event` object:
|param |Text to be copied to the clipboard |
|successNotification |<<.from-version "5.3.4">> Optional title of tiddler containing notification to be used if the operation succeeds |
|failureNotification |<<.from-version "5.3.4">> Optional title of tiddler containing notification to be used if the operation fails |
|type |<<.from-version "5.3.7">> MIME type of the text to be copied, defaults to `text/plain` |
|plainText |<<.from-version "5.3.7">> Additional plain text to be copied when `type` attribute isn't `text/plain` |
This message is usually generated with the ButtonWidget. It is handled by the TiddlyWiki core.

View File

@@ -605,5 +605,3 @@ Thomas E Tuoti, @well-noted, 2024/12/16
@opn, 2025/01/04
J. Ryan Stinnett, @jryans, 2025/01/04
Galen Huntington, @galenhuntington, 2025/01/19

View File

@@ -3,8 +3,6 @@ tags: $:/tags/PageTemplate
\whitespace trim
<%if [<tv-config-static>!match[yes]] %>
<$reveal state="$:/state/consent-banner/accepted" type="match" text="" tag="div">
<div class="tc-consent-backdrop">
@@ -28,5 +26,3 @@ tags: $:/tags/PageTemplate
</div>
</$reveal>
<%endif%>

View File

@@ -6,7 +6,7 @@
"isTiddlerFile": false,
"fields": {
"title": {"source": "filename", "prefix": "$:/plugins/tiddlywiki/katex/fonts/"},
"type": "font/woff"
"type": "application/font-woff"
}
}
],
@@ -37,4 +37,4 @@
"suffix": "})(require);\n"
}
]
}
}

View File

@@ -1,2 +1,2 @@
title: $:/themes/tiddlywiki/starlight/arvo.woff
type: font/woff
type: application/font-woff