"Stamp" self-rewriting pseudo-macro concept

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:

  1. the text replacement happens immediately when you finish typing the macro anywhere instead of only on tiddler save; which is kinda unintuitive.

  2. 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);
	};
};

})();

@Xyvir I value you looking at opportunities to innovate on top of TiddlyWiki and I urge you to continue, here are some of the ways you can do this without these “meta macros”;

  • I have a set of editor toolbar buttons that insert a time stamp “value” into the text, you could give them a shortcut key as well.
  • Some of the autocomplete solutions allow one to insert content and could have timestamp insertion included (if not already)
    • However perhaps the auto-complete solution could be extended to allow an inserted macro be “rendered” and only the result inserted.
  • You could look at defining additional TW-Wiki text symbols to be updated with content through the existing parser, but to rewrite you need a trigger, be this inserting time stamps or replacing with the output.
  • You could put an action on the done button to check for patterns in the body of a tiddler, compute values and replace the pattern with the desired result.
  • We could have buttons in the editor toolbar or in a side bar tab that generates the output from a macro or string such as a time stamp and copies it into the clipboard, you can then ctrl-V paste it into your text one or more times.
  • Another method is set a field value to the time stamp, then insert a reference to that in the text. This allows the field to be searched for and sorted on and retains the reference to the field in the text for searching eg {{!!time-stamp}} similarly we can use {{!!created}} or other versions as {{!!created}} {{!!modified}} is already date format transformed (try it).