#10011·TiddlyWiki5

[IDEA] oncompletion/onerror support for tm-save-wiki and tm-download-file

Author: DesignThinkererCreated Sep 4, 2026Updated Sep 4, 2026
Labelsdiscussion

tm-save-wiki and tm-download-file currently offer no way for a widget/message-level consumer to know whether a save actually succeeded or failed, or when it completed. I'd like to propose adding oncompletion/onerror action-string support to both messages, mirroring the pattern already established by tm-http-request and tm-save-dom-to-image.

Current behavior

SaverHandler.prototype.saveWiki (in core/modules/saver-handler.js) already distinguishes success from failure internally:

javascript
callback = function(err) {
    if(err) {
        alert($tw.language.getString("Error/WhileSaving") + ":\n\n" + err);
    } else {
        ...
        $tw.notifier.display(self.titleSavedNotification);
        if(options.callback) {
            options.callback();
        }
    }
};

But options.callback is only reachable by calling saveWiki() directly from JS. The tm-save-wiki/tm-download-file event handlers registered in the constructor only forward filename/type from paramObject, there's no path from the WikiText/widget layer (<$action-sendmessage $message="tm-save-wiki">) to that callback at all.

As a result, the only externally observable signals of save outcome are side effects: a $tw.notifier.display($:/language/Notifications/Save/Done) call on success, or a plain alert() on failure. Reacting to these from a plugin requires monkey-patching window.alert/$tw.notifier.display, which is fragile: it's coupled to internal wording/behavior and risks false positives from unrelated alert()/notifier.display() calls happening elsewhere during the same window.

Proposed change

Add oncompletion and onerror action-string parameters to tm-save-wiki and tm-download-file, consistent with the existing convention used by tm-http-request:

javascript
$tw.rootWidget.addEventListener("tm-http-request",function(event) {
    var params = event.paramObject || {};
    $tw.httpClient.initiateHttpRequest({
        ...
        oncompletion: params.oncompletion,
        onprogress: params.onprogress,
        ...
    });
});

and by tm-save-dom-to-image:

javascript
self.wiki.invokeActionString(oncompletion,self,variables,{parentWidget: $tw.rootWidget});

Concretely, this means:

  1. Threading oncompletion/onerror (and var-* passthrough variables, matching the existing convention) from event.paramObject through to saveWiki()'s options.
  2. Inside saveWiki()'s callback, invoking options.oncompletion on success and options.onerror on failure via wiki.invokeActionString(...), passing along a status/error variable similar to what tm-http-request provides.
  3. This is purely additive, the existing internal options.callback mechanism (used by direct JS callers) is untouched, and any wiki not using the new parameters sees no behavior change.

Why this is needed

I'm building a plugin (in-place upgrade) that rewrites the wiki's HTML shell (e.g. to bump the TiddlyWiki core version) and delivers the result through the user's existing saver via tm-save-wiki. This is an inherently risky, semi-destructive operation, so it matters a lot whether the save genuinely completed before telling the user it's safe to reload:

  • Without a completion signal, a user could reload before an async save actually finishes, risking data loss.
  • Without a failure signal, a failed save could be silently reported as successful.

But this isn't just about my plugin: any plugin doing autosave-driven workflows, "save then navigate/close" sequences, or "save then chain another action" patterns hits the same wall today, and has to either not bother (accepting the risk) or resort to monkey-patching core internals. Given tm-http-request and tm-save-dom-to-image both already establish this exact oncompletion/onerror/var-* convention, extending it to tm-save-wiki/tm-download-file would close a real, precedent-inconsistent gap in the messaging API, with no breaking changes and a fairly small, contained diff in saver-handler.js.

I'm happy to submit a PR implementing this if the maintainers are open to the approach. I wanted to propose it as a feature request first to confirm there isn't a design objection or existing plan that conflicts with it.