Is there a way to trigger an action on navigation?

TLDR: Every time I navigate to a tiddler with the tag “book”, I want the title of the tiddler to be automatically saved to the clipboard.

Any way to do this? :slight_smile:


My actual problem, for those interested:

When taking notes from books I pretty much always add a citation like <<source MyBook>>. To make this less tedious I have a keyboard shortcut that adds the citation for me, based on the last tiddler that I navigated to (which is usually the “book” tiddler).

This works for me on desktop, but of course I can’t use keyboard shortcuts on mobile. I’m thinking of using a text expander instead, but this means getting access to the last-navigated-to-tiddler from an app outside of TiddlyWiki. My thinking was to store this in the clipboard with tm-copy-to-clipboard, but to do this I think I would need to run an action widget every time I navigate to a “book” tiddler.

I’m trying to avoid adding extra buttons and stuff.

But what if you just copied something to the clipboard and deleted the source?, navigating to any tiddler will trash the current clipboard entry?

  • Although in windows you can look at all previous copies.

Yes you can, it is somewhat involved, in part because there are multiple places navigation can occur.

At a high level only;

  • Use the message catcher and/or Even catcher widget
  • Make a new layout https://tiddlywiki.com/#Alternative%20page%20layouts
  • In the Page template wrap the whole page in the catcher widget.
    • All navigations will be captured and you can test and respond with other conditions and action widgets
    • In many cases you then need to reissue the navigation action.
2 Likes

Starting with a half-baked idea:

Surround links to books with a LinkCatcherWidget, which you can use to both navigate to the target tiddler and copy the title to the clip board.

I’m sure this would be a pain to do for every single link to a book, but there may be some ways you might not need to. Here’s just one idea of how that might be achievable through light “hacking” of the story river (using the Story Tiddler Template Cascade):

  1. Make a tiddler with LinkCatcherWidget as described above, except one that conditionally copies to clipboard only if the navigate-to tiddler is tagged Book.
  2. Inside the link catcher widget, transclude the View Template (I think you have to do this without setting the current tiddler.) You could perhaps name this one “(yourDesiredPrefix)/ViewTemplateWrapper”
  3. Create a second tiddler, tagged $:/tags/StoryTiddlerTemplateFilter and add list-before: $:/config/StoryTiddlerTemplateFilters/default and in the text field write [[(yourDesiredPrefix)/ViewTemplateWrapper]] (or whatever you named the first tiddler).

This should, in effect, surround all tiddlers in the story river (except those in edit mode) with the LinkCatcherWidget so you only need to write it once, without changing anything about how you write links, nor any of your existing code.

1 Like

@TW_Tones 's suggestion to use the Page Template would be more comprehensive in that it would catch links in places other than the story river. I imagine it would be extremely easy to do just by modifying the Page Template tiddler to surround the contents with the widget; however, it might be very difficult to do it without overriding any shadow tiddlers.

If you want to avoid overriding shadow tiddlers, my suggestion may be easier to do, since you can utilize the existing template by transclusion rather than by editing, and use the cascade mechanism to insert the needed tiddler into the story river cascade, all without touching any shadows.

But if you’re not concerned about modifying shadow tiddlers, modifying the Page Template is probably a better choice.

Thank you both so much for your help!

I only really need to catch links within the story river I think. I’ve got it working by editing $:/core/ui/PageTemplate/story as @TW_Tones suggested.

I would prefer to avoid modifying any shadow tiddlers, but for some reason @bluepenguindeveloper’s suggestion works perfectly except that it breaks scrolling to opened tiddlers. Clicking on links opens the tiddler, but doesn’t scroll to it!

Here is the code I used for the wrapper:

\define actions()
<$action-navigate $to=<<navigateTo>>/>
<$list filter="[<navigateTo>tag[book]]" variable="ignore">
    <$action-sendmessage $message="tm-copy-to-clipboard" $param=<<navigateTo>>/>
</$list>
\end

<$linkcatcher actions=<<actions>> >
    {{||$:/core/ui/ViewTemplate}}
</$linkcatcher>

The cascade tiddler is exactly as you described. I have tried forcing $scoll="yes" but it makes no difference.

Any idea what is causing the problem?

@Sii may be it should be wrapped within the navigator widget. I am not sure though.

If you look closely you will see to copy the mage template and modify it is the way to create a new layout.

  • This allows the regular page template to be left and switch to the new with the layout switch.
  • By changing it in the page template it applies to all links from which you navigate including the side bar and Open tiddlers tab.

I forgot about this and also couldn’t find anything in the docs about a way to do something like that. But sure enough, it’s right there in the Control Panel.

Which means that yes, you can create a copy and switch (which has one of the same downsides as overriding shadow tiddlers, namely that you won’t get the updates if the page template is updated in a new TW version), or you can transclude the default page template rather than copying it.

you can add this ‘hook’

/*\
title: bj/hook/navHasTag.js
type: application/javascript
module-type: startup

\*/

$tw.hooks.addHook("th-navigating",function(event) {

    var tiddler;
    if(!event.navigateTo) return event;
    tiddler= $tw.wiki.getTiddler(event.navigateTo);

	if (!tiddler.hasTag("book"))return event;
	$tw.utils.copyToClipboard(event.navigateTo);
    return event;
	
});

here is the tid:

bj_hook_navHasTag.js.json (560 Bytes)

after adding the tid you need to reload the tiddlywiki

3 Likes

I think you have a better solution now that we know you can switch the page template without editing the existing one (and possibly another solution via the hook, though that is beyond my knowledge), so I’m not concerned about “solving” it, but I am curious if anyone happens to have any ideas as to why this broke scrolling.

@buggyj normally I would discourage moving to a JS solution when non-JavaScript TiddlyWiki script are available. Perhaps for two reasons;

  • The solution is dependant on your Javascript solution that must be packaged along with other tiddlers.
  • It make people think there is not native TiddlyWiki Script solution.

However

The idea of action on navigate is quite common and the traditional approach is somewhat global in nature.

Suggestion

Lets take your example and make a more sharable and reusable solution, which we may even get into the core one day.

Requirements

If a tiddler contains the (non-empty) field “on-navigation” or “on-navigation-action”, transclude it as an action on navigate as part of your hook.

  • This will remove the specific condition test from your code and replace it with if the current tiddler has the on-navigation field.
  • But transuding it inside the actions we can include macros, conditions, transclusions or action widgets in the on-navigation.

This way uses need only modify the field on a given tiddler to enable this feature.

1 Like

This is not dissimilar to my approach here How to safely customize the fields section of the edit template - #3 by TW_Tones

Thank you @buggyj , this is great! It works for me, except that it causes an error when navigating to missing tiddlers:

Uncaught TypeError: Cannot read properties of undefined (reading ‘hasTag’)

I’m assuming we need to tell the script not to check for tags if the tiddler navigated to doesn’t actually exist?

yep! heres the corrected version

/*\
title: bj/hook/navHasTag.js
type: application/javascript
module-type: startup

\*/

$tw.hooks.addHook("th-navigating",function(event) {

    var tiddler;
    if(!event.navigateTo) return event;
    tiddler= $tw.wiki.getTiddler(event.navigateTo);

	if (!tiddler || !tiddler.hasTag("book"))return event;
	$tw.utils.copyToClipboard(event.navigateTo);
    return event;
	
});

bj_hook_navHasTag.js(1).json (572 Bytes)

2 Likes

The hook is handled by the root widget and so I don’t think it would be possible to send messages from it and that would mean calling action widgets would be a problem.

I would be nice to be able to ‘monkey patch’ core templates, ie transclude the shadow tiddler from a overridden tiddler in an easy way. Also the $messagecatcher widget could do with a ‘resend’ option to resend the message it caught.

The th-navigating hook is called by the navigator-widget right before it actually takes any action.

So the th-navigating hook is intended to allow modification to the event eg: .navigateTo or any other variable, that will be used afterwards by the code.

Dev-docs: https://tiddlywiki.com/dev/#Hook%3A%20th-navigating

The code in the navigator can be seen at: TiddlyWiki5/core/modules/widgets/navigator.js at 9820dc0c6c8b06eadafdba1661fd18c0897eac0e · pmario/TiddlyWiki5 · GitHub

hope that helps
-mario

I agree on this “big time”, it is a gap in my view.

1 Like

Yes, that is correct. Thanks.