Create a journal tiddler on startup

Hello,
Having the same need as raised in this thread as a user coming from Logseq/Obsidian, I would like to have a journal tiddler created as soon as I open Tiddlywiki (or opened if it already exists, which is easy to do with the StartupTiddlers configuration), so that I can type any notes in children stream tiddlers.
I have therefore tried to copy $:/core/ui/Actions/new-journal , make my own version of “Auto journal on startup”:

<$action-createtiddler $basetitle=<<now "YYYY-0MM-0DD, ddd">> tags="Journal" />

or even hard-coded the tiddler’s title

<$action-createtiddler $basetitle="2025-02-12, Wed" />

and tagged them with $:/tags/StartupAction, but when I reload the wiki all of these options give me a red screen of death showing Uncaught TypeError: Cannot read properties of undefined (reading 'getString'), regardless whether or not I include \import [subfilter{$:/core/config/GlobalImportFilter}] before the createtiddler widget.
Same if I use $action-sendmessage instead of createtiddler:

<$action-sendmessage $message="tm-new-tiddler"  title="2025-02-12, Wed" />

Is there some kind of conflict between the creation of a journal tiddler specifically and TW’S startup sequence? Or am I making an obvious mistake somewhere?

1 Like

I created a github issue for this bug here: [Bug] RSOE When Using a StartupAction with the now macro · Issue #8946 · TiddlyWiki/TiddlyWiki5 · GitHub

I think I managed to narrow the source of the error to this snippet of wikitext:

<$transclude $variable='now'  />

It looks like the now macro is failing because $tw.utils.formatDateString is trying to access something that is undefined.

I’ve found a workaround , create a tiddler with the tag $:/tags/StartupAction/PostRender (using $:/tags/StartupAction will produce a RSOE) and this content:

{{$:/core/ui/KeyboardShortcuts/new-journal}}

demo

I don’t know why this work, but it does … my guess is that somehow, enough delay is added to allow $tw.utils.formatDateString to work properly.

Great, thanks for your answer @telumire!

I didn’t want to reuse the new-journal action so I simplified it a bit to this:

<$let journalTitleTemplate={{$:/config/NewJournal/Title}} >
<$wikify name="journalTitle" text="<$transclude $variable='now' format=<<journalTitleTemplate>>/>">

<$list filter="[<journalTitle>!is[tiddler]]" >
<$action-createtiddler $basetitle=<<journalTitle>> $template="$:/zig/JournalTemplate" tags="Journal" /> 
</$list>
</$wikify>

and that works wonderfully to have a daily journal tiddler as soon as I start up TW.
However, it seems to be created after processing the $:/DefaultTiddlers, so that I had to replace my initial [<now "YYYY-0MM-0DD, ddd">is[tiddler]else[Good day!]] with [<now "YYYY-0MM-0DD, ddd">]: Good day! is a welcome tiddler with a button to create a new journal if it does not exist yet, but is being put in the story just before the daily journal tiddler is automatically created.

Now, with that solved, I was trying to do something similar to create a tiddler in edit mode on startup (mostly to parse the URL when sharing content with TW as a PWA app on Android), but I can’t figure it out:

  • <$action-sendmessage $message="tm-new-tiddler" ...> does not seem to do anything when triggered by StartupAction/PostRender
  • <$action-createtiddler> successfully creates a tiddler, but does not put any focus on it
  • a subsequent <$action-sendmessage $message="tm-edit-tiddler" ...> also does not put the new tiddler in edit mode.
  • <$action-navigate $to=<<createTiddler-title>>/> does put the tiddler in the story line, but also not in edit mode.
  • all these options work properly when triggered by a button!

So it seems that $action-sendmessage widgets don’t work (yet?) on StartupAction - am I missing something, or is there any other way to put a tiddler in edit mode on startup?
(I also couldn’t figure out how to use the deprecated $savedrafttitle attribute for action-createtiddler)

Try wrapping your actions in a navigator widget:

<$navigator story="$:/StoryList" history="$:/HistoryList">
actions here
</$navigator>

Certain messages need to be handled by a navigator widget and none is available in startup actions unless you provide one.

Oh wow, that did it!
For future reference, in the end my tiddler looks like this:

\define content(title,text,url)
<<< 
$text$
<<<
from $title$, $url$
[ext[$title$|$url$]] // this does not work, WIP
\end

<$list filter="[[$:/info/url/search]get[text]split[&]first[]regexp[source_title=.*]]">
  <$let source_title={{{ [[$:/info/url/search]get[text]split[&]regexp[source_title=]split[=]last[]] }}}
            source_text={{{ [[$:/info/url/search]get[text]split[&]regexp[source_text=]split[=]last[]] }}}
            source_url={{{ [[$:/info/url/search]get[text]split[&]regexp[source_url=]split[=]last[]] }}} 
            tiddlerTitleTemplate={{$:/language/DefaultNewTiddlerTitle}} >
    <$wikify name="tidTitle" text="<$transclude $variable='now' format=<<tiddlerTitleTemplate>>/>">
      <$wikify name="tidcontent" text="<$transclude $variable='content' title=<<source_title>> text=<<source_text>> url=<<source_url>> />" >
        <$navigator story="$:/StoryList" history="$:/HistoryList">
          <$action-sendmessage $message="tm-new-tiddler" title=<<tidTitle>> text=<<tidcontent>> tags="new_on_start" />
        </$navigator>
      </$wikify>
    </$wikify>
  </$let>
</$list>

Certain messages need to be handled by a navigator widget and none is available in startup actions unless you provide one.

Do you know if there is any documentation about this?

1 Like

I believe there is about how the messages are handled but not about navigator not being available in startup actions.

https://tiddlywiki.com/#WidgetMessage%3A%20tm-new-tiddler

Thanks! What does “handled” actually mean?

and is handled by the NavigatorWidget.

@zigmhount could you wrote another post on how to set this up? I’m trying to follow along what you put where but I’m not getting it. I want this in my own personal edition.

@technome gladly! Not sure what part exactly you’re interested in, so I give you everything :slight_smile:

  1. my default tiddlers (set in the control panel) includes today’s journal [<now "YYYY-0MM-0DD, ddd">].
  2. $:/zig/auto_journal_on_startup is tagged $:/tags/StartupAction/PostRender and contains this:
<$let journalTitleTemplate={{$:/config/NewJournal/Title}} >

<!-- <$wikify name="journalTitle" text="<$transclude $variable='now' format=<journalTitleTemplate>/>"> -->
<!-- No idea why transclude or the macro itself do not interpret the format correctly whereas it works in a filter, but it works better. -->
<!-- ended up hardcoding the date format instead of using journalTitleTemplate -->
<$wikify name="journalTitle" text="{{{[<now 'YYYY-0MM-0DD, ddd'>]}}}">

<$list filter="[<journalTitle>!is[tiddler]]" >
<$action-createtiddler $basetitle=<<journalTitle>> $template="$:/zig/templates/JournalBody" tags="Journal" /> 
</$list>
</$wikify>
  1. $:/zig/templates/JournalBody contains only this
{{||$:/zig/templates/JournalTemplate}}
  1. $:/zig/templates/JournalTemplate contains what I want to see on every journal, currently the list of tiddlers created and modified in that day, and buttons to the previous/next days:
<$list filter="[<currentTiddler>tag[Journal]]"

<$let journaldate={{{ [{!!journal-date}split[]first[8]join[]] }}}
        titledate={{{ [{!!title}parsedate[YYYY-0MM-0DD, ddd]]        }}}
      createddate={{{ [{!!created}format:date[YYYY0MM0DD]]    }}}>
<$set name="thisdate"  value=<<journaldate>> emptyValue=<<titledate>>>
<$set name="thisdate" filter="[<thisdate>search:title[NaN]]" value=<<createddate>> emptyValue=<<thisdate>>>
<$set name="thisdate" filter="[<thisdate>match[]]"           value=<<createddate>> emptyValue=<<thisdate>>>
<$set name="created"  filter="[!is[system]!has[draft.of]] [all[tags]regexp[zig]tagging[]is[system]]                :filter[<currentTiddler>get[created]format:date[YYYY0MM0DD]match<thisdate>]">
<$set name="modified" filter="[!is[system]!has[draft.of]!enlist<created>] [all[tags]regexp[zig]tagging[]is[system]] :filter[<currentTiddler>get[modified]format:date[YYYY0MM0DD]match<thisdate>]">
<$list filter="[<created>!match[]]" >
   Created:
   <ul style="margin-top:0;margin-bottom:0;">
      <$list filter="[enlist<created>sort[]]">
         <li><$link/></li>
      </$list>
   </ul>
</$list>

<$list filter="[<modified>!match[]]">
   Modified:
   <ul style="margin-top:0;margin-bottom:0;">
      <$list filter="[enlist<modified>!sort[modified]]">
         <li><$link/></li>
      </$list>
   </ul>
</$list>

<$let nextday={{{ [<thisdate>format:date[YYYY0MM0DD]add[1]] }}} prevday={{{ [<thisdate>format:date[YYYY0MM0DD]subtract[1]] }}} >
<$button><$link to={{{ [<prevday>format:date[YYYY-0MM-0DD, ddd]] }}}>< Previous day</$link></$button><$button><$link to={{{ [<nextday>format:date[YYYY-0MM-0DD, ddd]] }}}>Next day ></$link></$button>
  1. $:/zig/receive_share_GET_pwa is tagged $:/tags/StartupAction/PostRender with content to parse the URL http://<my localhost tiddlywiki url>?source_title=mytitle&source_text=mytext&source_url=myurl and subsequently remove the query strings from the URL to not reprocess them when I reload the page:
\whitespace trim
\define content(title,text,url)
<%if [[$text$]minlength[1]] %>
<<< 
$text$
<<< 
  <%if [[$url$]minlength[1]] %>
from 
  <%endif%>
<%endif%>
<%if [[$url$]minlength[1]] %>
  <%if [[$title$]minlength[1]] %>
`[ext[$title$|$url$]]`
  <%else%>
$url$
  <%endif%>
<%endif%>
\end
\define link_node(nodetolink) `[[$nodetolink$]]`
\function stream_node_title(parenttt,thistt) [<parenttt>] [[/]] [<thistt>] +[join[]]


<$list filter="[[$:/info/url/search]get[text]split[&]first[]regexp[source_title=.*]] [[$:/info/url/search]get[text]split[&]first[]regexp[source_text=.*]] [[$:/info/url/search]get[text]split[&]first[]regexp[source_url=.*]]">
  <$let source_title={{{ [[$:/info/url/search]get[text]split[&]regexp[source_title=]split[=]last[]] +[decodeuricomponent[]] }}}
            source_text={{{ [[$:/info/url/search]get[text]split[&]regexp[source_text=]split[=]last[]] +[decodeuricomponent[]] }}}
            source_url={{{ [[$:/info/url/search]get[text]split[&]regexp[source_url=]split[=]last[]] +[decodeuricomponent[]] }}} 
            tiddlerTitleTemplate={{$:/language/DefaultNewTiddlerTitle}} >
    <$wikify name="tidTitle" text="<$transclude $variable='now' format=<<tiddlerTitleTemplate>>/>">
      <$wikify name="tidcontent" text="<$transclude $variable='content' title=<<source_title>> text=<<source_text>> url=<<source_url>> />" >
        <$navigator story="$:/StoryList" history="$:/HistoryList">
          <$action-sendmessage $message="tm-new-tiddler" title=<<tidTitle>> text=<<tidcontent>> />
        </$navigator>
      </$wikify>
    </$wikify>
  </$let>
<$action-removeQsFromUrl param="source_text"/>
<$action-removeQsFromUrl param="source_title"/>
<$action-removeQsFromUrl param="source_url"/>

</$list>

... Another similar section to use the URL ...?NewStreamToday= to create a new stream tiddler under today's journal tiddler ...

  1. The custom widget action-removeQsFromUrl.js to remove the query string parameters, type=application/Javascript , field module-type=widget :
/*\

Widget to remove the querystring from the URL

\*/
(function(){

/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";

var Widget = require("$:/core/modules/widgets/widget.js").widget;

// console.log("Called action-removeQsFromUrl custom widget!");

// Copied from http://scott.sauyet.com/Tiddlywiki/WIP/AndoverCharterChanges/v3/?#%24%3A%2F_%2Fatc%2Fcore%2Fmodules%2Fwidgets%2Faction-removequeryparam.js

var RemoveQueryParamWidget = function(parseTreeNode,options) {
		this.initialise(parseTreeNode,options);
	};
	/*
	Inherit from the base widget class
	*/
	RemoveQueryParamWidget.prototype = new Widget();
	
	/*
	Render this widget into the DOM
	*/
	RemoveQueryParamWidget.prototype.render = function(parent,nextSibling) {
		this.computeAttributes();
		this.execute();
//        console.log("Executed render.");
	};
	
	/*
	Compute the internal state of the widget
	*/
	RemoveQueryParamWidget.prototype.execute = function() {
		this.name = this.getAttribute("param","default");
	};
	
	/*
	Refresh the widget by ensuring our attributes are up to date
	*/
	RemoveQueryParamWidget.prototype.refresh = function(changedTiddlers) {
		// Nothing to refresh
		return this.refreshChildren(changedTiddlers);

};

//	console.log("Invoking the action...");
	
	/*
	Invoke the action associated with this widget
	*/
	RemoveQueryParamWidget.prototype.invokeAction = function(triggeringWidget,event) {
		var queryParams = new URLSearchParams(window.location.search);
//     console.log("Found queryParams " + queryParams.toString());
		queryParams.delete(this.name);

		history.replaceState(null, null, "?" + queryParams.toString() + document.location.hash);

        // Remove them all
//        history.replaceState(null, null, document.location.hash);
//		history.replaceState(null, null, (queryParams.size > 0 ? ("?" + queryParams.toString()) : '') + document.location.hash);
		return true; // Action was invoked
	};
//	console.log("Invoked the action");

exports["action-removeQsFromUrl"] = RemoveQueryParamWidget;

})();

That’s quite exhaustive, so I hope I’m not drowning you in irrelevant details @technome :sweat_smile: but I have more PWA related stuff if you need, let me know!

1 Like