Hello All,
I know what I am trying to accomplish below is likely antithetical to TiddlyWiki design principals but I am humbly asking you would humor me.
I’m imagining a sort of macro, or function, or something, that will self-rewrite itself in it’s tiddler body location with the output another invoked macro.
For instance:
<<stamp '<<now>>' >>
Would dynamically re-write itself on tiddler save with plaintext as:
08:07, 11th February 2026
similarly
<<stamp '<<now "DD MMM YYYY">>' >>
would rewrite itself as:
11 February 2026
(I also thought it would be neat to be able to manipulate fields in this fashion, so you can provide an optional second parameter being a field key that will write the macros output to that field key’s value and remove itself from the body; this workflow seems silly in TW but it’s inspired from logseq who actually uses key:value pairs defined/embedded in the block bodies themselves)
After vibing with Gemini a bit I got a ‘working’ implementation, but it’s quite hacky and could use a lot of improvement:
-
the text replacement happens immediately when you finish typing the macro anywhere instead of only on tiddler save; which is kinda unintuitive.
-
It’s not very TW-native, and not a true macro; it just uses a bunch of javascript and regex to do the replacing.
Anyways I’m not trying to re-invent the wheel, and I know one possible workflow is just to copy paste a macro from view mode back into edit mode of a tiddler, plus this goes against the dynamism and bottom-up design of TiddlyWiki; but I think it could have some uses to people who come from the more casual “PKMS” sphere and are used to having similar functionality.
So if this or similair has been done before, or perhaps can already be done more elegantly and natively in TiddlyWiki, I’d love to hear your thoughts.
-Xyvir
(In the demo site below I use snowgoons comptext to mimic discord-like ‘slashcommands’ and /now and /timestamp implement the <<stamp >> pseudo-macro as described above)
stamp demo site
Here is the current hacky implementation:
/*\
title: $:/lithic/modules/startup/stamp-macro.js
type: application/javascript
module-type: startup
Stamp Macro Hook (v11 - Shielded Monkeypatch)
- Overrides addTiddler with aggressive null-checks.
- Prevents "undefined" crashes on state tiddlers.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.name = "stamp-macro-monkeypatch";
exports.platforms = ["browser"];
exports.after = ["startup"];
exports.synchronous = true;
exports.startup = function() {
var originalAddTiddler = $tw.wiki.addTiddler;
function processStampLogic(tiddler) {
// --- SAFETY SHIELD ---
// If it's not a real object, or doesn't have fields, or text isn't a string: skip.
if(!tiddler || !tiddler.fields || typeof tiddler.fields.text !== "string") {
return tiddler;
}
var text = tiddler.fields.text;
// Optimization: If the word "stamp" isn't even in the text, skip regex entirely.
if(text.indexOf("stamp") === -1) {
return tiddler;
}
var fieldUpdates = {};
var hasFieldUpdates = false;
// Dormancy Check (---)
var fenceRegex = /(?:^|\n)\s*---+\s*(?:\n|$)/;
var fenceMatch = fenceRegex.exec(text);
var fenceIndex = fenceMatch ? fenceMatch.index : Infinity;
// Stamp Regex (Flexible spaces)
var stampRegex = /<<stamp\s*(?:"""([\s\S]*?)"""|"([^"]*)"|'([^']*)')(?:(?:\s+)(?:"""([\s\S]*?)"""|"([^"]*)"|'([^']*)'))?\s*>>/g;
if(!stampRegex.test(text)) {
return tiddler;
}
stampRegex.lastIndex = 0;
var newText = text.replace(stampRegex, function(match, c3, c2, c1, f3, f2, f1, offset) {
if (offset > fenceIndex) return match;
var contentToStamp = c3 || c2 || c1;
var targetField = f3 || f2 || f1;
if (!contentToStamp) return match;
var renderedResult = "";
try {
renderedResult = $tw.wiki.renderText(
"text/plain",
"text/vnd.tiddlywiki",
contentToStamp,
{ variables: { currentTiddler: tiddler.fields.title } }
);
} catch (e) {
return match;
}
if (targetField) {
if (tiddler.fields[targetField] === renderedResult) {
fieldUpdates[targetField] = undefined;
} else {
fieldUpdates[targetField] = renderedResult;
}
hasFieldUpdates = true;
return "";
}
return renderedResult;
});
if(newText !== text || hasFieldUpdates) {
return new $tw.Tiddler(tiddler, fieldUpdates, {text: newText});
}
return tiddler;
}
// Override core function
$tw.wiki.addTiddler = function(tiddler) {
// Ensure we are working with the tiddler object
var processedTiddler = processStampLogic(tiddler);
return originalAddTiddler.call($tw.wiki, processedTiddler);
};
};
})();