This page is part of a static HTML representation of the TiddlyWiki at https://tiddlywiki.com/dev/

Adding Babel Polyfill to TiddlyWiki

12th January 2016 at 5:50pm

Not all browsers support the latest features of ES2015. The Babel project offers a polyfill that can be included into your TiddlyWiki so those features can be available to your plugins. To do this you will need a copy of the polyfill source.

You can obtain the source either through npm or downloaded/saved. See the Babel Polyfill documentation for specific information on installing it.

In your TiddlyWiki editions folder make sure you have a plugins/babel-polyfill folder. Then create the plugins/babel-polyfill/plugin.info file with the following in it:

{
  "title": "$:/plugins/babel/babel-polyfill",
  "description": "Babel Polyfills for ES2015 support",
  "author": "Your Name Here",
  "core-version": ">=5.0.0"
}

Create the folder plugins/babel-polyfill/files folder. Then create the plugins/babel-polyfill/files/tiddlywiki.files file with the following in it:

{
  "tiddlers": [
    {
      "file": "polyfill.min.js",
      "fields": {
        "title": "$:/plugins/babel/babel-polyfill/polyfill.min.js",
        "type": "application/javascript",
        "module-type": "library",
        "global-module": "true"
      }
    }
  ]
}

Now copy the polyfill.min.js you downloaded/saved.

If you downloaded this via npm then it would be available in ./node_modules/babel-polyfill/dist/polyfill.min.js.

Lastly you need a initializer so create the plugins/babel-polyfill/plugin.js file with the following in it:

/*\
title: $:/plugins/babel/babel-polyfill/plugin.js
type: application/javascript
module-type: startup

Load the babel-polyfill library on startup

\*/

exports.startup = function() {
  $tw.modules.execute('$:/plugins/babel/babel-polyfill/polyfill.min.js');
}

Because the polyfill is meant to be used in the browser we need to conditionally load the library which ES2016 doesn't allow. This is why it is written using TiddlyWiki's dependency resolver instead of using ES2015 import statements.

Now all the runtime ES2015 features are available like using Promise in your plugin code.

See Using ES2016 for Writing Plugins on how to use the ES2015 syntax for your plugin code.

apptree.svg

8th July 2014 at 3:54pm

arch.svg

17th July 2014 at 7:41pm

BootMechanism

2nd May 2014 at 2:29pm

Introduction

At its heart, TiddlyWiki5 is a relatively small boot kernel that runs either under Node.js or in the browser with all other functionality added via dynamically loaded modules.

The kernel boots just enough of the TiddlyWiki environment to allow it to load and execute module tiddlers. The module system is compatible with CommonJS and Node.js.

There are many different types of module: parsers, deserializers, widgets etc. It goes much further than you might expect. For example, individual tiddler fields are modules, too: there's a module that knows how to handle the tags field, and another that knows how to handle the special behaviour of the modified and created fields. Some plugin modules have further sub-plugins: the wikitext parser, for instance, accepts parsing rules as individual plugin modules.

Plugins

In TiddlyWiki5, Plugins are bundles of tiddlers that are distributed and managed as one; Modules are JavaScript tiddlers with a module type identifying when and how they should be executed.

The tiddler $:/boot/boot.js is a barebones TiddlyWiki kernel that is just sufficient to load the core plugin modules and trigger a startup module to load up the rest of the application.

The boot kernel includes:

  • Several short shared utility functions
  • A handful of methods implementing the module mechanism
  • The $tw.Tiddler class (and field definition plugins)
  • The $tw.Wiki class (and tiddler deserialisation methods)
  • Code for the browser to load tiddlers from the HTML DOM
  • Code for the server to load tiddlers from the file system

Each module is an ordinary CommonJS module, using the require() function to access other modules and the exports global to return JavaScript values. The boot kernel smooths over the differences between Node.js and the browser, allowing the same plugin modules to execute in both environments.

In the browser, core/boot.js is packed into a template HTML file that contains the following elements in order:

  • Ordinary and system tiddlers, packed as HTML <DIV> elements
  • core/bootprefix.js, containing a few lines to set up the plugin environment
  • Optional JavaScript modules, packed as HTML <SCRIPT> blocks
  • core/boot.js, containing the boot kernel

On the server, core/boot.js is executed directly. It uses the Node.js local file API to load plugins directly from the file system in the core/modules directory. The code loading is performed synchronously for brevity (and because the system is in any case inherently blocked until plugins are loaded).

The boot process sets up the $tw global variable that is used to store all the state data of the system.

At the end of the boot process the StartupMechanism schedules the execution of startup modules to bring up the rest of TiddlyWiki.

Caching

17th July 2014 at 6:09pm

The core plug-in adds two caches to the wiki store. A global cache and a cache for each tiddler. While the global cache is cleared whenever any tiddler changes, the tiddler cache is only cleared when the associated tiddler changes. The idea of the tiddler cache is, that sometimes we want to calculate information from a tiddler but don't want to recalculate every time we need this information. Imagine a tiddler which contains links to other tiddlers and at one point we want to have a list of the linked tiddlers (LinksFilter). With the tiddler cache we can parse the WikiText, extract the linked tiddlers and put this list in the cache. Until the tiddler is changed, the cache can be used to access this information in the next requests.

The same idea holds for the global cache. For example when a filter string is parsed and a list of matching tiddlers is constructed. This list can be put in the global cache for later reuse until something changes in the store.

Like the Event Mechanism, the cache needs hook functions in the microkernel. The microkernel calls clearGlobalCache() and clearCache(tiddlertitle) when a tiddler changes. The core's cache mechanism overwrites this functions. The functions providing the cache system are added via the wikimethod module type.

Child widgets tutorial

22nd February 2019 at 11:11pm

Introduction

Until now the examples have covered only simple, leaf widgets, but widgets can be arranged into a hierarchy. Compared to a leaf-only widget, widget classes which support having children must contain code to instantiate the children.

Not all widgets need to support having children. Widgets whose only purpose is to integrate third party javascript libraries, for example may not need it.

wiki text ⇨ parse tree ⇨ widget tree ⇨ DOM tree

  1. wiki text - Users write content in tiddlers using wiki text.
  2. parse tree - A parse tree is a JSON data structure describing the wiki text. Wiki text can be converted into a parse tree using this.wiki.parseText.
  3. widget tree - Most items in the parse tree correspond to one or more items in the widget tree. The this.makeChildWidgets method is used to convert the parse tree into a widget tree.
  4. DOM tree - A DOM tree is a standard javascript datastructure used by browsers to display a web page. The this.renderChildren method is used to instantiate the widget class and then render each widget in the widget tree. If a given widget will be visible in the browser, then one or more DOM nodes will be created.

Examples

Simple trees without nesting

Widgets in wiki text have a syntax similar to html (i.e. <$list/>). But all wiki markup is seen by the tiddlywiki code as widgets.

Even a simple string with no special syntax will be treated as a widget. In this case a widget of type text:

wiki texthtmlrenders as
hello hello hello

parse treewidget tree


[
    {
        "type": "text",
        "text": "hello",
        "start": 0,
        "end": 5
    }
]



{
    "type": "widget",
    "children": [
        {
            "type": "text",
            "text": "hello"
        }
    ]
}

The above bare string of text is mostly just a synonym for this widget syntax:

wiki texthtmlrenders as
<$text text=hello/> hello hello

parse treewidget tree


[
    {
        "type": "text",
        "start": 0,
        "attributes": {
            "text": {
                "start": 6,
                "name": "text",
                "type": "string",
                "value": "hello",
                "end": 17
            }
        },
        "orderedAttributes": [
            {
                "start": 6,
                "name": "text",
                "type": "string",
                "value": "hello",
                "end": 17
            }
        ],
        "tag": "$text",
        "isSelfClosing": true,
        "end": 19,
        "isBlock": false,
        "rule": "html"
    }
]



{
    "type": "widget",
    "children": [
        {
            "type": "text",
            "attributes": {
                "text": "hello"
            }
        }
    ]
}

Some of the details of the parseTree and widgetTree are different in the two examples, but the general structure is the same and the rendered output is the same.

In these two simple examples, there is no nesting and so no child widgets are created.

Trees with one level of nesting

This next example shows the structure for bold wiki syntax. It is still simple, but does introduce one level of nesting: elementtext

The wiki syntax for bold converts to the standard html element strong. Any standard html element is represented in tiddlywiki as a widget of type element:

wiki texthtmlrenders as
''bold'' <strong>bold</strong> bold

parse treewidget tree


[
    {
        "type": "element",
        "tag": "strong",
        "children": [
            {
                "type": "text",
                "text": "bold",
                "start": 2,
                "end": 6
            }
        ],
        "start": 0,
        "end": 8,
        "rule": "bold"
    }
]



{
    "type": "widget",
    "children": [
        {
            "type": "element",
            "tag": "strong",
            "children": [
                {
                    "type": "text",
                    "text": "bold"
                }
            ]
        }
    ]
}

Another example showing one level of nesting (linktext):

wiki texthtmlrenders as
<$link to=atiddler>link</$link> <a class="tc-tiddlylink tc-tiddlylink-missing" href="#atiddler">link</a> link

parse treewidget tree


[
    {
        "type": "link",
        "start": 0,
        "attributes": {
            "to": {
                "start": 6,
                "name": "to",
                "type": "string",
                "value": "atiddler",
                "end": 18
            }
        },
        "orderedAttributes": [
            {
                "start": 6,
                "name": "to",
                "type": "string",
                "value": "atiddler",
                "end": 18
            }
        ],
        "tag": "$link",
        "end": 31,
        "openTagStart": 0,
        "openTagEnd": 19,
        "isBlock": false,
        "children": [
            {
                "type": "text",
                "text": "link",
                "start": 19,
                "end": 23
            }
        ],
        "closeTagEnd": 31,
        "closeTagStart": 23,
        "rule": "html"
    }
]



{
    "type": "widget",
    "children": [
        {
            "type": "link",
            "attributes": {
                "to": "atiddler"
            },
            "children": [
                {
                    "type": "text",
                    "text": "link"
                }
            ]
        }
    ]
}

Widgets with no DOM contribution

Not all widgets contribute items to the DOM. The purpose of some widgets is to affect the display of descendant widgets by changing some state. The $set widget for example sets a variable which will be accessible to the descendant widgets:

wiki texthtmlrenders as
<$set name=myvar value=hi/>

parse treewidget tree


[
    {
        "type": "set",
        "start": 0,
        "attributes": {
            "name": {
                "start": 5,
                "name": "name",
                "type": "string",
                "value": "myvar",
                "end": 16
            },
            "value": {
                "start": 16,
                "name": "value",
                "type": "string",
                "value": "hi",
                "end": 25
            }
        },
        "orderedAttributes": [
            {
                "start": 5,
                "name": "name",
                "type": "string",
                "value": "myvar",
                "end": 16
            },
            {
                "start": 16,
                "name": "value",
                "type": "string",
                "value": "hi",
                "end": 25
            }
        ],
        "tag": "$set",
        "isSelfClosing": true,
        "end": 27,
        "isBlock": false,
        "rule": "html"
    }
]



{
    "type": "widget",
    "children": [
        {
            "type": "set",
            "attributes": {
                "name": "myvar",
                "value": "hi"
            }
        }
    ]
}

Nothing is rendered and there is no html, but the parse tree and widget tree contain information about the variable.

Dynamic widget children using this.wiki.parseText

In all the examples so far, there has been a close mapping between the nesting structure of the parse tree and the widget tree. If the item is in the parse tree, then it is in the widget tree. If the parse tree item has children, then the widget tree has the same number of children.

However, there are several examples of widgets in which more levels of nesting are created dynamically during the widget rendering process

The $macrocall widget is one example:

wiki texthtmlrenders as
<$macrocall $name=now/> 11:01, 18th November 2024 11:01, 18th November 2024

parse treewidget tree


[
    {
        "type": "macrocall",
        "start": 0,
        "attributes": {
            "$name": {
                "start": 11,
                "name": "$name",
                "type": "string",
                "value": "now",
                "end": 21
            }
        },
        "orderedAttributes": [
            {
                "start": 11,
                "name": "$name",
                "type": "string",
                "value": "now",
                "end": 21
            }
        ],
        "tag": "$macrocall",
        "isSelfClosing": true,
        "end": 23,
        "isBlock": false,
        "rule": "html"
    }
]



{
    "type": "widget",
    "children": [
        {
            "type": "macrocall",
            "attributes": {
                "$name": "now"
            },
            "children": [
                {
                    "type": "transclude",
                    "attributes": {
                        "$variable": "now",
                        "$type": "text/vnd.tiddlywiki",
                        "$output": "text/html"
                    },
                    "children": [
                        {
                            "type": "text",
                            "text": "11:01, 18th November 2024"
                        }
                    ]
                }
            ]
        }
    ]
}

The parse tree has just a single type=$macrocall element with no children. The widget tree has that same type=$macrocall element, but it also has a child widget which wasn't in the parse tree.

In all cases, the $macrocall widget calls the given macro and then calls this.wiki.parseText on the output. This results in a new parse tree which is used to make the child widgets.

In this particular case, the now macro is called. It returns a simple string of text, so when it is parsed the result causes the creation of a single child text widget containing the current date and time.

Widgets which do not support nesting

Just as some widgets can cause widget trees to have more nesting than the parse tree, some widgets can cause widget trees to have less nesting.

This happens when there is no use for the widget to have any children. The $text widget, for example, has no use for displaying any descendants.

This behavior is accomplished by not calling the makeChildWidgets method. Without that method call, the child widgets are not created from the child parse tree items. For example:

wiki texthtmlrenders as
<$text text="hi">ignored child text</$text> hi hi

parse treewidget tree


[
    {
        "type": "text",
        "start": 0,
        "attributes": {
            "text": {
                "start": 6,
                "name": "text",
                "type": "string",
                "value": "hi",
                "end": 16
            }
        },
        "orderedAttributes": [
            {
                "start": 6,
                "name": "text",
                "type": "string",
                "value": "hi",
                "end": 16
            }
        ],
        "tag": "$text",
        "end": 43,
        "openTagStart": 0,
        "openTagEnd": 17,
        "isBlock": false,
        "children": [
            {
                "type": "text",
                "text": "ignored child text",
                "start": 17,
                "end": 35
            }
        ],
        "closeTagEnd": 43,
        "closeTagStart": 35,
        "rule": "html"
    }
]



{
    "type": "widget",
    "children": [
        {
            "type": "text",
            "attributes": {
                "text": "hi"
            }
        }
    ]
}

Since the $text widget does not have a call to makeChildWidgets, 'ignored child text' above is present in the parse tree, but not in the widget tree.

Reference

methodwhen to callwhat it does
makeChildWidgetsdirectly or indirectly from render methodconverts child parse tree items into widget tree items
renderChildrendirectly or indirectly from render method, after the makeChildWidgets callcalls the render method of the child widget
refreshChildrendirectly or indirectly from refresh method, only needed if refreshSelf is not calledcalls the refresh method of of the child widgets so they can check if refresh is needed
this.wiki.parseTextpass the output parse tree to makeChildWidgetsconverts the given text to a parse tree...only needed for dynamically constructed parsetree
example callpurposewidgets using this approach
this.makeChildWidgets()construct child widgets from the static parse tree$link, $button, etc.
this.makeChildWidgets(parseTreeNodes)construct child widgets from a dynamic parse tree$list, $transclude, $macrocall, etc
this.renderChildren(parent, nextSibling)when the widget adds nothing to the dom, just pass the render arguments through to the children$set, $importvariables, $tiddler, $wikify, etc.
this.renderChildren(domNode, null)passes the dom node generated by this widget into the children$link, $button, etc

Conclusion

17th July 2014 at 9:18pm

TiddlyWiki consists in its heart only of a basic microkernel providing bare tiddlerstore and module system. It is written in JavaScript and suited to run in a browser or as node.js application. The core plug-in extends the microkernel with powerful functions from a central event system to a sophisticated widget system transforming WikiText to dynamic HTML. Because of it's microkernel architecture the application is highly customizable. The plug-in system not only allows to add new modules but also to override existing modules. Most of the components don't refer directly to modules but load them by type, allowing developers to inject additional modules including new saver implementations, widgets or even rules for the WikiText parser. The user interface of TiddlyWiki is written in WikiText and can be customized with the same language, a user normally uses when just writing wiki entries.

A drawback of the core plug-in is it's high complexity. While the microkernel provides just the bare some bare functions and structures, the core plug-in adds a whole bunch of components at once. It can be challenging to decompose the core architecture and understand the connections between the components. This documentation could only cover the most important parts. This gives a developer the choice of building a whole new application on the microkernel or building the application on the core plug-in, including all modules and UI tiddlers.

In conclusion, TiddlyWiki is a interesting piece of software. The focus on tiddlers, the functionality provided in the core and the fact that the core comes with a full blown wiki application puts TiddlyWiki into a personal information management domain, especially when using TiddlyWiki as a single file application storing code and data in a single HTML file. But the highly customizable nature makes TiddlyWiki perfect for this exact domain. A casual user can organize information with tags and metadata and customize the UI to a grade, that he is able to implement and test his own workflows just by using WikiText. JavaScript developers can add whole new features and create completely new single page applications by building on the microkernel or customizing the core plug-in.

Continuous Deployment

28th August 2020 at 9:45am

TiddlyWiki 5 uses GitHub Actions for continuous deployment. It is driven by the workflow file .github/workflows/ci.yml in the repo, along with the scripts in the bin folder that it invokes.

The build history can be seen at https://github.com/TiddlyWiki/TiddlyWiki5/actions

Contributing to the TiddlyWiki Core

7th November 2014 at 9:32pm

The TiddlyWiki core is the container for all the generic features of TiddlyWiki that are of universal utility. Concretely, it comprises the tiddlers in the $:/core plugin.

Core Contribution Requirements

There are requirements that must be met for any contribution that is to be accepted into the core:

  • If appropriate, the new functionality must support both standalone and Node.js configurations. For example, any new widgets must be capable of being rendered on the server
  • The contribution must not compromise the backwards compatibility of the existing code
  • Code contributions must comply with the TiddlyWiki Coding Style Guidelines
  • Generic components are preferred over point solutions for specific problems (which belong in plugins)

The Core and Innovation

If you've created something new and innovative, don't try to rush to get it included into the core. Once new stuff is in the core it is subject to the core policies of strict backwards compatibility, making it frozen as far as radical innovation is concerned. It's usually better to release the new thing as a plugin so that it can be shared with the rest of the community for feedback.

The expected model of innovation is that the core development will move relatively slowly as more and more of the initial planned functionality is implemented. Innovation can take place in the much more unconstrained environment of plugins. Over time, as these third party plugins gain popularity and become more polished, some or all of their functionality will be migrated into the core.

Improving Hackability of the Core

Don't be afraid to submit issues or pull requests that add hooks or other points of extensibility to help your plugin integrate with the core. An important goal for TiddlyWiki is for the core to be infinitely adaptable through plugins. It is expected that many more points of extension will need to be added to support a healthy ecosystem of plugins.

Contributing to the TiddlyWiki Plugin Library

16th October 2014 at 8:33am

The TiddlyWiki Plugin library is the set of plugins, themes and languages that are distributed via https://tiddlywiki.com.

The plugin library is intended to help end users of TiddlyWiki in the following ways:

  • By being a reliable, official source of community assets
  • Permitting automatic upgrading of plugins during the upgrade process
  • Providing a guarantee of backwards compatibility

Plugins in the library need a maintainer who is prepared to make a commitment to keep them tested and fully operational with successive releases of the core code. Many plugins are maintained by the core team.

Data Management during Runtime

8th July 2014 at 8:56am

Data Persistence

17th July 2014 at 7:57pm

The next important part of the application are deserializers. Deserializers are responsible to load tiddlers from various sources. One of the deserializers provided by the microkernel for example can load new tiddlers from the current DOM tree. The counterpart of deserializers are saver. A saver is used to save the complete store or the complete TiddlyWiki for that matter at once. After a user made changes to some tiddlers, this method can be used to download the current state of the store as a completely new TiddlyWiki application.

Another way of persisting data are syncadaptors. Like deserializer and saver, a syncadaptor can load tiddlers into the store and can persist changed tiddlers. But in contrast to deserializer and saver it can load and persist single tiddlers. A syncadaptor can provide a list of loadable tiddlers registers at the store for changes. Now when a tiddler is changed and these changes are written to the store, the syncadaptor is triggered and persists the new changes, for each tiddler individually.

Tiddlers can be persisted from/to harddisk or synced with a remote server.

Data Storage

25th May 2021 at 4:55pm

Data Storage in Single File TiddlyWiki

14th June 2021 at 1:47pm

The single file version of TiddlyWiki saves the tiddlers within the HTML file.

Version 5.2.0 of TiddlyWiki introduced a new format for how tiddlers are saved within the HTML file.


Up to and including TiddlyWiki v5.1.23

Tiddlers are saved within the HTML file in one of two <div> tags depending on whether the TiddlyWiki is configured to encrypt its content or not.

Without encryption

If the TiddlyWiki is not encrypted the data is stored in a <div> tag with an id attribute of "storeArea".

<div id="storeArea" style="display:none;">

Within the store area <div>, each tiddler is stored in its own div.

Tiddler DIV

Each tiddler <div> has an attribute corresponding to each of the tiddler's fields, except the text field which is saved within a pre tag within the <div>. Note that all attributes and therefore field names must be lowercase letters, digits or the characters - (dash), _ (underscore) and . (period). The only attribute that is required is title, all other attributes are optional. All attribute values and the text field within the pre tag are HTML encoded.

Example:

<div id="storeArea" style="display:none;">
<div created="20140611153703343" modified="20140611153734589" tags="testTag" testfield="testvalue" title="TestTiddler" type="text/plain">
<pre>testText</pre>
</div>
</div>

With encryption

If the TiddlyWiki is configured to encrypt its content, the tiddlers are stored as as an encrypted JSON string in a <pre> tag with the id attribute "encryptedStoreArea".

<pre id="encryptedStoreArea" type="text/plain" style="display:none;">

TiddlyWiki uses the Stanford JavaScript Crypto Library.


From TiddlyWiki v5.2.0

From v5.2.0 onwards, TiddlyWiki introduces a new JSON based format for the tiddler store area for unencrypted content, while retaining support for the older store area formats described above for backwards compatibility. The store area format remains unchanged for encrypted content.

Without encryption

By default, all tiddlers are now stored as an array of JSON objects inside a <script> tag with the class attribute "tiddlywiki-tiddler-store" and the type "application/json". For better readability, every tiddler object begins on a new line. There are no longer any restrictions on characters that are allowed in tiddler field names. However, <script> tag store areas must encode the < character to \u003c.

<script class="tiddlywiki-tiddler-store" type="application/json">[
{"title":"XLSX Utilities Edition","created":"20161023202301847","modified":"20161023202301847","tags":"Editions","type":"text/vnd.tiddlywiki","text":"The ''XLSX Utilities'' edition of TiddlyWiki contains tools to work with `.XLSX` spreadsheets generated by applications like Microsoft Excel and Google Sheets. It can be used in the browser or under Node.js.\n\nhttps://tiddlywiki.com/editions/xlsx-utils/\r\n"},
{"text":"In accordance with the [[Philosophy of Tiddlers]], documentation tiddlers are typically short and interlinked.\n\nWhen a tiddler seems as if it needs to contain subheadings, this is often a sign that it should in fact be split into several tiddlers. But it is reasonable for a [[reference tiddler|Reference Tiddlers]] to consist of an untitled introductory section followed by a titled section of details.\n\nConsistency of terminology is essential if the reader is not to become confused. Consistent typography and punctuation lend a professional quality to the documentation. Macros can improve the consistency and maintainability of the text.\n\nUse numbered lists for step-by-step instructions, and bullet points for lists whose order is arbitrary. Use a definition list in preference to a bulleted list if each bulleted item would begin with a term and a colon. If at all possible, avoid burdening the reader with a nested list.\n\nUse a table when information naturally falls into three or more columns, and also for lists of parameters, attributes, etc in [[reference tiddlers|Reference Tiddlers]].\n\nAvoid periods at the end of list items, headings and table cell text.\n\nThe documentation describes the current reality of ~TiddlyWiki. Avoid discussing future aspirations.\n","title":"Tiddler Structure","tags":"[[Improving TiddlyWiki Documentation]]","modified":"20210207124737959","created":"20150110183300000"}
]</script>

To retain compatibility with external tools that might insert tiddlers by directly manipulating the TiddlyWiki HTML file, the older format <div> store area is still present in the HTML file immediately after the new <script> tag store area:

<script class="tiddlywiki-tiddler-store" type="application/json">[
{"title":"XLSX Utilities Edition","created":"20161023202301847","modified":"20161023202301847","tags":"Editions","type":"text/vnd.tiddlywiki","text":"The ''XLSX Utilities'' edition of TiddlyWiki contains tools to work with `.XLSX` spreadsheets generated by applications like Microsoft Excel and Google Sheets. It can be used in the browser or under Node.js.\n\nhttps://tiddlywiki.com/editions/xlsx-utils/\r\n"},
{"text":"In accordance with the [[Philosophy of Tiddlers]], documentation tiddlers are typically short and interlinked.\n\nWhen a tiddler seems as if it needs to contain subheadings, this is often a sign that it should in fact be split into several tiddlers. But it is reasonable for a [[reference tiddler|Reference Tiddlers]] to consist of an untitled introductory section followed by a titled section of details.\n\nConsistency of terminology is essential if the reader is not to become confused. Consistent typography and punctuation lend a professional quality to the documentation. Macros can improve the consistency and maintainability of the text.\n\nUse numbered lists for step-by-step instructions, and bullet points for lists whose order is arbitrary. Use a definition list in preference to a bulleted list if each bulleted item would begin with a term and a colon. If at all possible, avoid burdening the reader with a nested list.\n\nUse a table when information naturally falls into three or more columns, and also for lists of parameters, attributes, etc in [[reference tiddlers|Reference Tiddlers]].\n\nAvoid periods at the end of list items, headings and table cell text.\n\nThe documentation describes the current reality of ~TiddlyWiki. Avoid discussing future aspirations.\n","title":"Tiddler Structure","tags":"[[Improving TiddlyWiki Documentation]]","modified":"20210207124737959","created":"20150110183300000"}
]</script><div id="storeArea" style="display:none;"></div>

Any tiddlers in the older format <div> store area are also loaded before tiddlers from the new <script> store area.

Multiple store areas and precedence

Tiddlers from the new <script> tag store areas are loaded in the order of their store areas in the HTML file. Therefore if the same tiddler exists in two different <script> tag store areas, the tiddler from the later store area takes precedence. Note however that tiddlers from <script> tag store areas always take precedence over tiddlers from the older format <div> store area. Therefore a tiddler from the older <div> store area can never overwrite a tiddler from a <script> tag store area.

Note that all <script> tags with the class attribute "tiddlywiki-tiddler-store" that precede the $:/boot.js module in the HTML file will have their content parsed as JSON and loaded as tiddlers.

This allows external tools to easily insert tiddlers into an HTML file by prepending an additional <script> tag(s) at the very start of the HTML file:

<script class="tiddlywiki-tiddler-store" type="application/json">[
{"created":"20210525212411223","text":"This is some test text","tags":"[[test tag]] [[another tag]]","title":"My new tiddler to insert","modified":"20210525212430577"}
]</script>
<!doctype html>
...

Although invalid HTML, all known browsers will silently move the script tag to a valid position within the

Additional topics:

Data-Storage

10th July 2014 at 8:10am

Datamodel

15th July 2014 at 8:38am

The micro-kernel creates a TiddlyWiki specific data-structure called tiddler. Here you have to separate the different definition of tiddlers. In the architectural view a tiddler is a JavaScript object holding some data. In the overall concept a tiddler is similar to a wiki page in a normal wiki application like wikipedia. In this section we describe the architectural view of a tiddler. The listing below shows the JSON representation of a tiddler object. During the runtime of the TiddlyWiki everything is saved an object like this. Without any plug-in the architecture is not able to persist any kind of data. All the data is stored in a store. This store is a JavaScript object. This store is constructed like a Map with a bunch of key value pairs. Every tiddler has its name as the key in the map and the JavaScript-Object as the value. The tiddler concept is the main data-model within TiddlyWiki, everything from data up to plug-ins is stored as a tiddler.

{"fields":{
   "text":"example Text",
   "title":"Infrastruktur",
   "tags":["vs"],
   "modified":"2014-07-01T16:25:01.230Z",
   "myField":"myFieldValue",
   "created":"2014-07-01T16:22:10.673Z"
   }
}

Deserializer

15th July 2014 at 6:48pm

Modules with module-type: tiddlerdeserializer can provide functions to create tiddlers out of any textual representation. Each function must be associated with a type like application/json or text/html. They get the textual representation of the tiddlers, some default field values and the type of the text block as arguments. They return an array of JavaScript objects representing the resulting tiddlers.

Deserializers are not managed by the syncer module. Instead the concept of deserializers is in fact part of the microkernel. This is necessary because the microkernel needs a way of loading tiddlers before it can load the core plug-in and execute it's startup modules. (Due to the fact that the core plug-in and it's modules are represented as tiddlers.)

The load-modules startup module loads additional deserializers and pushes them into the store. The core plug-in for example contains a few deserializers which can read a whole TiddlyWiki 5 or classic HTML file and load the individual tiddlers into the store.

Developing plugins using Node.js and GitHub

22nd October 2014 at 2:52pm

The most practical way to develop plugins is to use Node.js with the tiddlywiki5 repository to build your plugins, and to use GitHub to manage you files.

Step by step

1. Installation

First read https://tiddlywiki.com/static/PluginMechanism.html.

Install Git from http://git-scm.com/downloads

Install Node.js from http://nodejs.org/

2. Create a new blank repository on GitHub

Hint: GitHub repositories cannot be grouped together into directories, so it is only possible to group by using a naming scheme, e.g. use 'TW5-' as a name prefix with tiddlywiki5 projects to group them together.

Go to https://github.com/ and create new a repository 'pluginname' - choose to add a readme file.

3. Setup a working environment

Choose a location in your file system (eg TW5) for your plugin project; issue commands to:

1. Create the directory

mkdir TW5

2. Make a local read-only copy of the TiddlyWiki5 repository

git clone https://github.com/TiddlyWiki/TiddlyWiki5.git TW5

3. Make a directory for your plugin

cd TW5
cd plugins
mkdir yourname
cd yourname
mkdir pluginname

4. Make a local copy of your plugin repository

git clone https://github.com/yourgithub/pluginname.git pluginname

5. Go to your files

cd pluginname

Create the file plugin.info with content:

{
	"title": "$:/plugins/yourgithub/pluginname",
	"description": "summary of the plugin's purpose",
	"author": "yourname",
	"version": "0.0.1",
	"core-version": ">=5.0.8",
	"source": "https://github.com/yourgithub/pluginname",
	"plugin-type": "plugin"
}

4. Create the files for your plugin

For example files see the plugins in the TiddlyWiki5 repository i.e. those located at plugins/tiddlywiki/. See TiddlerFiles for details of the supported tiddler file formats.

5. Build your files into a TiddlyWiki

Modify editions/tw5.com/tiddlywiki.info to include a reference to your plugin directory, i.e. find "plugins": [ and add "yourname/pluginname".

From the TW5 directory issue the command

node ./tiddlywiki.js editions/tw5.com --build index

The resultant file (index.html) will be placed in the editions/tw5.com/output directory of the TW5 repo.

6. Save your work on GitHub

From plugins/yourname/pluginname/ issue commands to:

1. Add all files

git add -A

2. Commit to your local repository

git commit -am "something meaningful about this check in"

3. Copy local changes to github

git push

Do nothing widget demo

1st February 2019 at 10:26pm

[ { "title": "$:/DefaultTiddlers", "text": "[[do nothing widget]]" } ] [ { "title": "donothing.js", "text": "/*\\\n\nDo nothing widget\n\n\\*/\n(function(){\n\n/*jslint node: true, browser: true */\n/*global $tw: false */\n\"use strict\";\n\nvar Widget = require(\"$:/core/modules/widgets/widget.js\").widget;\n\nexports.donothing = Widget;\n\n})();\n", "created": "20190201115945945", "modified": "20190201222441271", "module-type": "widget", "tags": "", "type": "application/javascript" } ] [ { "title": "do nothing widget", "text": "\n```\n<$donothing/>\n```\n\nRenders as:\n\n<$donothing/>\n" } ]

Do nothing widget tutorial

2nd February 2019 at 2:55pm

In order to define a widget in a tiddler, the tiddler must satisfy these requirements:

  • type field is application/javascript
  • module-type field is widget
  • the text field contains the javascript code

The donothing.js tiddler fulfills the requirements and its code looks like this:

/*\

Do nothing widget

\*/
(function(){

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

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

exports.donothing = Widget;

})();
That code does 2 key things:

Here's what it looks like:

[ { "title": "$:/DefaultTiddlers", "text": "[[do nothing widget]]" } ] [ { "title": "donothing.js", "text": "/*\\\n\nDo nothing widget\n\n\\*/\n(function(){\n\n/*jslint node: true, browser: true */\n/*global $tw: false */\n\"use strict\";\n\nvar Widget = require(\"$:/core/modules/widgets/widget.js\").widget;\n\nexports.donothing = Widget;\n\n})();\n", "created": "20190201115945945", "modified": "20190201222441271", "module-type": "widget", "tags": "", "type": "application/javascript" } ] [ { "title": "do nothing widget", "text": "\n```\n<$donothing/>\n```\n\nRenders as:\n\n<$donothing/>\n" } ]
And it worked. No error message this time.

Exercise: Modify donothing.js and Do nothing widget demo so the widget is named noop instead of donothing

domwidget.js

1st February 2019 at 3:07am
/*\

Library function for creating widget using a dom creating function

\*/
(function() {

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

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

function createDomWidget(domCreatorFunction) {

	var MyWidget = function(parseTreeNode, options) {
		this.initialise(parseTreeNode, options);
	};

	/*
	Inherit from the base widget class
	*/
	MyWidget.prototype = new Widget();

	/*
	Render this widget into the DOM
	*/
	MyWidget.prototype.render = function(parent, nextSibling) {
		this.parentDomNode = parent;
		var domNode = domCreatorFunction(this.document);
		parent.insertBefore(domNode, nextSibling);
		this.domNodes.push(domNode);
	};

	/*
	A widget with optimized performance will selectively refresh, but here we refresh always
	*/
	MyWidget.prototype.refresh = function(changedTiddlers) {
		// Regenerate and rerender the widget and replace the existing DOM node
		this.refreshSelf();
		return true;
	};

	return MyWidget;
}
module.exports = createDomWidget;
})();

donothing.js

1st February 2019 at 10:24pm
/*\

Do nothing widget

\*/
(function(){

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

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

exports.donothing = Widget;

})();

DragAndDropInterop

EncryptionMechanism

29th November 2013 at 4:36pm

TiddlyWiki5 allows the entire content of a TiddlyWiki HTML file to be encrypted with the Stanford JavaScript Crypto Library. Opening an encrypted TiddlyWiki in the browser prompts for a password before decrypting and displaying the content.

For instructions on how to use TiddlyWiki5's encryption features, see Encryption.

The EncryptionMechanism is implemented with the following elements:

  • A PasswordVault within the BootMechanism that holds the current encryption password
  • The ability of the BootMechanism to read a block of encrypted tiddlers from the TiddlyWiki file, to prompt the user for a password, and to decrypt the tiddlers
  • Handlers for the messages WidgetMessage: tm-set-password and WidgetMessage: tm-clear-password that handle the user interface for password changes
  • The EncryptWidget within the main file template that encrypts a filtered list of tiddlers with the currently held password
  • The $:/isEncrypted tiddler that contains "yes" or "no" according to whether there is a password in the password vault
    • The availability of this tiddler allows the RevealWidget to be used to selectively display user interface elements according to whether encryption is in force

Event Mechanism

17th July 2014 at 8:14pm

Extended Persistence

17th July 2014 at 6:11pm

The microkernel only contains a bare store and some deserializers to load tiddlers from JSON files or from the DOM of the current HTML file. The core plug-in adds some more deserializers and a new mechanism for persisting and synchronising tiddlers.

This mechanism is provided as a global module in $:/core/modules/syncer.js. The saver module has three responsibilities:

  1. Save the whole wiki.
  2. Provide the ability to download single tiddlers as files.
  3. Synchronise the local wiki store with a remote wiki store, i.e. running in Node.js

The syncer module is connected mainly to two other modules. For one it registers to changes at the wiki store (Event Mechanism) and if any changes occur they are synced to the remote store. Then it provides a function saveWiki(options). This function can be used by other modules. For example the RootWidget uses this function to save the whole wiki or start downloading single tiddlers.

The syncer itself does not provide a concrete implementation of saving, downloading or syncing the tiddlers. Instead it loads modules of type saver and syncadaptor and manages the saving/syncing process.

Deserializer

Modules with module-type: tiddlerdeserializer can provide functions to create tiddlers out of any textual representation. Each function must be associated with a type like application/json or text/html. They get the textual representation of the tiddlers, some default field values and the type of the text block as arguments. They return an array of JavaScript objects representing the resulting tiddlers.

Deserializers are not managed by the syncer module. Instead the concept of deserializers is in fact part of the microkernel. This is necessary because the microkernel needs a way of loading tiddlers before it can load the core plug-in and execute it's startup modules. (Due to the fact that the core plug-in and it's modules are represented as tiddlers.)

The load-modules startup module loads additional deserializers and pushes them into the store. The core plug-in for example contains a few deserializers which can read a whole TiddlyWiki 5 or classic HTML file and load the individual tiddlers into the store.

Saver

Modules with module-type: saver provide functionality to save the whole wiki. There are three methods a saver can support:

save
This method is used, when the user requests a save, for example by clicking the save button in the sidebar.
autosave
This method is used automatically by TW when tiddlers are changed, created or deleted by the user.
download
This message is used when the wiki or a single tiddler should explicitly be downloaded. The control panel for example uses this method to provide a button which saves the wiki as a static HTML file.

A saver module has to export two functions. canSave(wiki) returning true if this module is capable of working and create(wiki) returning an instance of a saver object. This saver object has to provide an info property containing a name, a priority, an array of methods it supports and a method save(text,method,callback). This method is called from TW with the actual text which should be saved, the method which is used and a callback function to report errors: callback("Error while saving") or to notify that saving went well: callback(null, "Saving went well :)"). If the saver method successfully saved the file it has to return true, or false otherwise. Saves are triggered by messages from the UI. The syncer module uses the saver with the highest priority capable of the requested method to save the file.

The core plug-in contains a saver capable of saving the current state of the wiki to the local hard drive by using a special Firefox extension called Tiddlyfox. If this extension is not available, the savers canSave method would return false. A saver with a lower priority would then ask the user to save the current state as a new HTML file.

Syncadaptor

A module with module-type: syncadaptor provides functionality to get a list of tiddlers (this list is provided as SkinnyTiddlers, which are normal tiddlers without the text field) and to load, save and delete single tiddlers. A syncadaptor can also provide functions to login and logout so that syncadaptor modules can be used to synchronise tiddlers with a remote server.

The syncer module only uses one syncadaptor and honours a special system tiddler $:/config/SyncFilter containing a filter string. Tiddlers matching this filter string are saved to the server with a syncadapter. It uses the WebServer API to load modified tiddlers from the server, which returns only non-system tiddlers.

Extending the Store

17th July 2014 at 5:56pm

Event Mechanism

Most of the following mechanisms need a way to get notified, when anything in the wiki store changes. The core -plug-in adds an event system to the bare wiki store. The event system provides the ability to listen to events. The most important is the "change" event which notifies the listeners when tiddlers have changed, with a list of the changed tiddlers. The event mechanism is one of the few mechanisms which needs a hook at the microkernel: The microkernel contains an empty function "enqueueTiddlerEvent(event)" and calls this function when a tiddler is added or deleted. The event mechanism from the core plug-in overwrites this function with it's own implementation. The functions providing the event system are added via the wikimethod module type.

Caching

The core plug-in adds two caches to the wiki store. A global cache and a cache for each tiddler. While the global cache is cleared whenever any tiddler changes, the tiddler cache is only cleared when the associated tiddler changes. The idea of the tiddler cache is, that sometimes we want to calculate information from a tiddler but don't want to recalculate every time we need this information. Imagine a tiddler which contains links to other tiddlers and at one point we want to have a list of the linked tiddlers (LinksFilter). With the tiddler cache we can parse the WikiText, extract the linked tiddlers and put this list in the cache. Until the tiddler is changed, the cache can be used to access this information in the next requests.

The same idea holds for the global cache. For example when a filter string is parsed and a list of matching tiddlers is constructed. This list can be put in the global cache for later reuse until something changes in the store.

Like the Event Mechanism, the cache needs hook functions in the microkernel. The microkernel calls clearGlobalCache() and clearCache(tiddlertitle) when a tiddler changes. The core's cache mechanism overwrites this functions. The functions providing the cache system are added via the wikimethod module type.

System Tiddlers

The core plug-in introduces a segregation of tiddlers. The tiddler model is central in the whole TiddlyWiki application and can be used in various roles. Because of the fact that a TiddlyWiki user works with tiddlers (taking the role of a wiki page) and tiddlers are the building blocks of the whole application (including modules, UI elements, etc.) we need to identify the internal tiddlers.

The core plug-in introduces the concept of system tiddlers. It builds on the convention that application internal tiddler names start with $:/. Then the core plug-in introduces a set of new functions to the wiki store which are used to retrieve tiddlers like getTiddlers(options) and forEachTiddler(callback, options). These functions work with all tiddlers in the store but the options parameter provides the ability to sort tiddlers by a field-name and exclude tiddlers with a specific tag. By default it doesn't return system tiddlers. To get a list of all tiddlers including system tiddlers, this must be requested explicitly via the options. If a function wants to present a list of tiddlers to the user it can use this new functions so that internal application tiddlers wouldn't clutter the resulting list. These functions are added via the wikimethod module type.

Tags and Filter Mechanism

Tags

The core plug-in extends the store by a simple mechanism to tag a tiddler. This provides the functionality to

  • retrieve tiddlers with a specific tag
  • retreive a hashmap of { tag: [tiddler1, tiddler3, tiddler3] }
  • list or iterate tiddlers not tagged with a specific tag

The tags are stored directly in the tiddler. Each tiddler can have a field named "tag", which contains an array of its tags. The above functions use this field to calculate their results and cache them in a global cache.

Organising information with tags is easy, intuitive and is common throughout the web. In most cases the functions mentioned above are not enough and a more sophisticated way of querying is needed. But instead of focusing only on tags TiddlyWiki introduces a querying system that isn't bound to tags or single tiddlers.

Filter mechanism

This filter mechanism is build on the idea of a pipeline of single filter operators. Filters are noted as strings and can look like

[tag[task]!tag[done]interesting[very]]

This example would (implicitly) put all available tiddlers into the pipe. The first operator tag[task] would only pass tiddlers which are tagged with "task". The !tag[done] operator is negated and only passes tiddlers which are not tagged with "done". The last filter operator passes only tiddlers with a field "interesting" set to "very". So as a result this filter would be used to obtain all tiddlers which are marked as task, aren't already done and are very interesting.

There are many filter operators already build into the core plug-in including the mentioned tag- and field operators, filter operators to sort the tiddlerlist by a specified field, etc. But more sophisticated operators are possible, too. An example would be the search-operator. This filter operator looks for the searched string in the text and in the tags of the tiddlers. If the provided filter operators are not enough, a developer can add new filters by adding a module with the filteroperator type.

System Tags

Tags and the filter mechanism are used throughout the core plug-in for internal puproses. Tags which start with $:/ are normally hidden from the casual user, similar to System Tiddlers

The filter mechanism is added to the wiki store with the wikimethod module type.

FakeDomMechanism

19th October 2014 at 12:23pm

The "fake DOM" is a simplified JavaScript implementation of the DOM that can be run on the server. Its use allows modules that need to run under Node.js to generate and manipulate HTML through the DOM API, rather than having to stitch HTML strings together.

The fake DOM only implements a small subset of the full DOM APIs but it is sufficient for the core widgets to be able to be run under Node.js.

Because the fake DOM is faster than the real DOM, it is also used in the browser when interactive rendering to the DOM isn't needed. The best example is stylesheet processing, where WikiText stylesheet content is parsed and rendered, and the plain text extracted from the fake DOM for use as the stylesheet content.

Filter Operators

Overview

Filter operators are modules (tiddlers of type application/javascript) with their module-type field set to filteroperator, exporting one or more functions implementing a filter.

Each function will be called with three arguments:

  • A tiddler iterator representing the results of the previous filter step (or all tiddlers, if this filter appears first in an expression), conventionally named source.
  • An object, conventionally called operator, representing the arguments for this filter step, with the following keys:
    • operator: the name of the filter operator specified in the WikiText;
    • operand: the operand for the filter step (as a string; if the filter specified it in angle brackets or braces, the text reference or variable name will have already been resolved);
    • prefix: (optional) a string containing a single exclamation mark if the filter operator is to be negated;
    • suffix: (optional) a string containing an additional filter argument (typically a tiddler field name) following the filter name (separated by a colon in the filter syntax);
    • regexp: (optional, deprecated) used instead of operand if the filter operand is a regexp.
  • An object, conventionally called options, with the following keys:
    • wiki: The $tw.Wiki object;
    • widget: (optional) a widget node.

The function should return either a new tiddler iterator, or else an array of tiddler titles (as strings). The underlying filter mechanism will convert back and forth between iterators and arrays as needed.

References

There are several filter operators built into the core which can serve as a jumping off point for your own filter operators:

https://github.com/TiddlyWiki/TiddlyWiki5/tree/master/core/modules/filters

Example

Suppose we want to make a filter operator that returns every other tiddler from the input list. A typical invocation might look like [tags[interesting]everyother[]].

We make a new tiddler, set its type and module-type appropriately, and begin writing the code:

(function(){
"use strict";
    
exports.everyother = function(source, operator, options) {
    // TODO
}
})();

For the example filter syntax, our function will be called with

  • source: an iterator over all the tiddlers tagged as interesting
  • operator: an object {operator: "everyother", operand: ""}
  • options: an object with the current Wiki object and a widget object, neither of which we need

As is usually the case, we don't care about operator.operator here (since that information has already been used to look up our function); we also don't care about operator.operand, since there is no meaningful operand for this operation.

We could implement the operator by iterating over the input tiddlers and explicitly building a result array of titles:

(function(){
"use strict";
    
exports.everyother = function(source, operator, options) {
    var result = [];
    var include = true;
    source(function(tiddler, title) {
        if (include) { result.push(title); }
        include = !include;
    });
    return result;
}
})();

That is, we supply a callback to source that negates include each time through (in order to grab every other result) and pushes the title of every other tiddler onto the result.

Alternatively, we can return our own iterator, by returning a function that accepts a similar callback and only calls it on every other tiddler:

(function(){
"use strict";
    
exports.everyother = function(source, operator, options) {
    return function(callback) {
        var include = true;
        source(function(tiddler, title) {
            if (include) { callback(tiddler, title); }
            include = !include;
        });
    };
}
})();

Either way, we could interpret the ! flag on the filter, if present, to mean that we want the other half of the tiddlers, by using it to set the initial value of include: var include = operator.prefix !== "!";

Filter Behaviour

As with JavaScript Macros, filter operators should not make modifications to tiddlers, but only return a list of tiddlers or a tiddler iterator.

GitHub Branches

15th January 2019 at 5:36pm

Development of TiddlyWiki 5 in the GitHub repo at https://github.com/TiddlyWiki/TiddlyWiki5 uses two branches:

When preparing pull requests it is important to target the correct branch.

Hello World demo

1st February 2019 at 11:15pm

[ { "title": "$:/DefaultTiddlers", "text": "[[hello world widget]]" } ] [ { "title": "hello.js", "text": "/*\\\n\nHello, World widget\n\n\\*/\n(function(){\n\n/*jslint node: true, browser: true */\n/*global $tw: false */\n\"use strict\";\n\nvar Widget = require(\"$:/core/modules/widgets/widget.js\").widget;\n\nvar MyWidget = function(parseTreeNode,options) {\n\tthis.initialise(parseTreeNode,options);\n};\n\n/*\nInherit from the base widget class\n*/\nMyWidget.prototype = new Widget();\n\n/*\nRender this widget into the DOM\n*/\nMyWidget.prototype.render = function(parent,nextSibling) {\n\tthis.parentDomNode = parent;\n\tvar textNode = this.document.createTextNode(\"Hello, World!\");\n\tparent.insertBefore(textNode,nextSibling);\n\tthis.domNodes.push(textNode);\n};\n\nexports.hello = MyWidget;\n\n})();\n", "created": "20190201114558816", "modified": "20190201224846870", "module-type": "widget", "tags": "", "type": "application/javascript" } ] [ { "title": "hello world widget", "text": "\n```\n<$hello/>\n```\n\nRenders as:\n\n<$hello/>\n" } ]

Hello World widget tutorial

16th February 2019 at 5:56pm

Now let's create a widget which actually has output.

When tiddlywiki encounters a widget definition like <$hello> it will create an object which is an instance of the class which is exported by the widget code.

In addition to creating an instance of the class, tiddlywiki will call the render method of the resulting object. The render method is expected to create a dom node which will be display in place of the widget.

In the donothing example the core widget was exported. The core widget class doesn't have a render method which creates a dom node and that's why there is no output. Getting output requires writing a widget class which inherits from the core Widget class. Code in the render method of this class will display the hello world message.

The hello.js tiddler has code which accomplishes that:

/*\

Hello, World widget

\*/
(function(){

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

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

var MyWidget = function(parseTreeNode,options) {
	this.initialise(parseTreeNode,options);
};

/*
Inherit from the base widget class
*/
MyWidget.prototype = new Widget();

/*
Render this widget into the DOM
*/
MyWidget.prototype.render = function(parent,nextSibling) {
	this.parentDomNode = parent;
	var textNode = this.document.createTextNode("Hello, World!");
	parent.insertBefore(textNode,nextSibling);
	this.domNodes.push(textNode);
};

exports.hello = MyWidget;

})();
The code for importing the core widget class remains, but now we also have code to create our own class which inherits from it. In addition an implementation of the render method is included. Here is the result:
[ { "title": "$:/DefaultTiddlers", "text": "[[hello world widget]]" } ] [ { "title": "hello.js", "text": "/*\\\n\nHello, World widget\n\n\\*/\n(function(){\n\n/*jslint node: true, browser: true */\n/*global $tw: false */\n\"use strict\";\n\nvar Widget = require(\"$:/core/modules/widgets/widget.js\").widget;\n\nvar MyWidget = function(parseTreeNode,options) {\n\tthis.initialise(parseTreeNode,options);\n};\n\n/*\nInherit from the base widget class\n*/\nMyWidget.prototype = new Widget();\n\n/*\nRender this widget into the DOM\n*/\nMyWidget.prototype.render = function(parent,nextSibling) {\n\tthis.parentDomNode = parent;\n\tvar textNode = this.document.createTextNode(\"Hello, World!\");\n\tparent.insertBefore(textNode,nextSibling);\n\tthis.domNodes.push(textNode);\n};\n\nexports.hello = MyWidget;\n\n})();\n", "created": "20190201114558816", "modified": "20190201224846870", "module-type": "widget", "tags": "", "type": "application/javascript" } ] [ { "title": "hello world widget", "text": "\n```\n<$hello/>\n```\n\nRenders as:\n\n<$hello/>\n" } ]

hello-attribute-optimized.js

5th February 2019 at 2:51am
/*\

Hello, World widget

\*/
(function() {

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

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

var MyWidget = function(parseTreeNode, options) {
	this.initialise(parseTreeNode, options);
};

/*
Inherit from the base widget class
*/
MyWidget.prototype = new Widget();

/*
Render this widget into the DOM
*/
MyWidget.prototype.render = function(parent, nextSibling) {
	this.parentDomNode = parent;
	this.computeAttributes();
	var message = this.getAttribute("message", "World");
	var textNode = this.document.createTextNode("Hello, " + message + "!");
	parent.insertBefore(textNode, nextSibling);
	this.domNodes.push(textNode);
};

/*
Refresh if the attribute value changed since render
*/
MyWidget.prototype.refresh = function(changedTiddlers) {
	// Find which attributes have changed
	var changedAttributes = this.computeAttributes();
	if (changedAttributes.message) {
		this.refreshSelf();
		return true;
	} else {
		return false;
	}
};

exports.hello = MyWidget;

})();

hello-attribute.js

4th February 2019 at 3:03am
/*\

Hello, World widget

\*/
(function() {

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

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

var MyWidget = function(parseTreeNode, options) {
	this.initialise(parseTreeNode, options);
};

/*
Inherit from the base widget class
*/
MyWidget.prototype = new Widget();

/*
Render this widget into the DOM
*/
MyWidget.prototype.render = function(parent, nextSibling) {
	this.parentDomNode = parent;
	this.computeAttributes();
	var message = this.getAttribute("message", "World");
	var textNode = this.document.createTextNode("Hello, " + message + "!");
	parent.insertBefore(textNode, nextSibling);
	this.domNodes.push(textNode);
};

/*
A widget with optimized performance will selectively refresh, but here we refresh always
*/
MyWidget.prototype.refresh = function(changedTiddlers) {
	// Regenerate and rerender the widget and
	// replace the existing DOM node
	this.refreshSelf();
	return true;
};

exports.hello = MyWidget;

})();

hello.js

1st February 2019 at 10:48pm
/*\

Hello, World widget

\*/
(function(){

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

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

var MyWidget = function(parseTreeNode,options) {
	this.initialise(parseTreeNode,options);
};

/*
Inherit from the base widget class
*/
MyWidget.prototype = new Widget();

/*
Render this widget into the DOM
*/
MyWidget.prototype.render = function(parent,nextSibling) {
	this.parentDomNode = parent;
	var textNode = this.document.createTextNode("Hello, World!");
	parent.insertBefore(textNode,nextSibling);
	this.domNodes.push(textNode);
};

exports.hello = MyWidget;

})();

HelloThere

29th October 2022 at 5:57pm

Hook: th-before-importing

8th February 2021 at 3:20pm

This hook allows plugins to inspect or modify the importTiddler object before any tiddlers are imported. It is invoked after the final "Import" button is clicked, but before the selected tiddlers are being imported into the store.

Intended Usecases:

  • Manipulate the import "selection state"
  • Eg: create a customized "log-tiddler" that contains a heading, that should only be written once

Important:

The hook is part of the NavigatorWidget.prototype.handlePerformImportEvent function.

Hook function parameters:

  • importTiddler: an object, that contains information about "selected / unselected" tiddlers and more

Return value:

  • importTiddler: object

The hook must return the importTiddler object. For many usecases the object will be returned unmodified.

Example code how to implement a hook in your project

/*\
title: $:/plugins/<author>/<plugin>/th-before-importing.js
type: application/javascript
module-type: startup

YOUR DISCRCRIPTION COMES HERE!

\*/
(function(){

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

// Export name and synchronous status
exports.name = "<yournamecomesherewithoutspaces>";
exports.platforms = ["browser"];
exports.after = ["startup"];
exports.synchronous = true;

// Define your variables here!

exports.startup = function() {
	$tw.hooks.addHook("th-before-importing",function(importTiddler) {

// YOUR CODE !

		return importTiddler;
	});
};

})();

Hook: th-closing-tiddler

23rd June 2021 at 8:19am

This hook allows plugins to monitor the closing of a tiddler from the story.

Hook function parameters:

  • event: Widget message object with the following properties:
    • event: DOM event object that triggered the widget message
    • tiddlerTitle: the title of the tiddler being closed
    • widget: reference to the widget that sent the message.

Return value:

  • event: Widget message object

The original event widget message object can be returned unmodified by the hook.

Hook: th-deleting-tiddler

9th February 2017 at 2:59pm

Hook: th-editing-tiddler

29th June 2017 at 8:02am

This hook allows plugins to inspect tiddlers before they are edited via the edit toolbar button.

Hook function parameters:

  • event: DOM event object that triggered the edit

Return value:

  • editTiddler: true to continue with editing the tiddler as usual, false to silently suppress editing

Hook: th-importing-file

10th October 2017 at 11:51am

This hook allows plugins to inspect or modify the details of files imported via drag and drop or the "import" button. It is invoked as each File object provided by the browser in response to an import or drag and drop is being read. The hook function can choose to ignore the file, in which case TiddlyWiki's default processing proceeds to read and import the content of the file. Alternatively, the hook function can process the file to extract the tiddlers itself, and then pass them back to TiddlyWiki to be handled by the rest of the import process.

Use this hook if you want to control how tiddlers are extracted from files during an import. See Hook: th-importing-tiddler if you want to process each imported tiddler after they have been extracted from the files.

Hook function parameters:

  • info: an object with properties containing information relating to the current file:
    • file: reference to the browser's File object
    • type: the MIME type of the file. If not provided by the browser then TiddlyWiki attempts to infer the type from the file extension
    • isBinary: flag for whether the file contains binary data (which requires that the file type be recognised by TiddlyWiki)
    • callback: callback function to be called with an array of tiddler field objects extracted from the file object

Return value:

  • true indicates that the hook handled the file, and passed any extracted tiddlers to the callback
  • false indicates that the hook didn't process the file

Hook: th-importing-tiddler

3rd February 2021 at 7:07pm

This hook allows plugins to inspect or modify tiddlers as they are imported via the import mechanism. It is invoked when the final "Import" button is clicked, and the selected tiddlers are being imported into the store.

The hook is part of the NavigatorWidget.prototype.handlePerformImportEvent function.

Use this hook if you want to process each imported tiddler after they have been extracted from the files. See Hook: th-importing-file if you want to control how tiddlers are extracted from files during an import.

Hook function parameters:

  • tiddler: tiddler object about to be imported

Return value:

  • tiddler object to be imported

The original tiddler object can be returned unmodified by the hook. If the hook needs to modify the tiddler then it should return a new tiddler object, for example:

	return new $tw.Tiddler(tiddler,{"my-field": value});

Hooks must not change the title field but can freely modify any other field of the tiddler.

Hook: th-make-tiddler-path

This hook lets a plugin inspect or modify the tiddler file path during the saving process.

Hook function parameters:

  • currentPath: The path the tiddler file will be saved to.
  • originalPath: The original tiddler file path unmodified by any hooks.

Return value:

The path to use for saving the tiddler file.

Hook: th-navigating

19th February 2017 at 2:55pm

This hook allows plugins to monitor and modify navigation events.

Hook function parameters:

  • event: object describing the navigation event:
    • event.navigateTo: title of target tiddler
    • event.navigateFromTitle: title of tiddler containing the
    • event.navigateSuppressNavigation: boolean; when true the target tiddler opens without the usual scrolling
    • event.navigateFromClientRect: rectange in client coordinates of the DOM node triggering the navigation

Return value:

  • possibly modified event object

Hook: th-opening-default-tiddlers-list

9th February 2017 at 11:55am

Hook: th-page-refreshed

4th January 2018 at 4:00pm

This hook notifies plugins that a page refresh has just occurred. It is typically used to apply post-rendering effects.

Hook function parameters:

  • (none)

Return value:

  • (none)

Hook: th-page-refreshing

11th January 2019 at 3:01pm

This hook notifies plugins that a page refresh is just about to occur. It is typically used to apply pre-rendering effects.

Hook function parameters:

  • (none)

Return value:

  • (none)

Hook: th-relinking-tiddler

9th February 2017 at 3:00pm

This hook allows plugins to inspect tiddlers before they are relinked ("relinking" is the optional operation of relinking references to a tiddler when it is renamed).

Hook function parameters:

  • newTiddler: tiddler object incorporating the relinking
  • oldTiddler: optional existing tiddler object that will be overwritten

Return value:

  • newTiddler: tiddler object to be used for the relinking operation.

The original tiddler object can be returned unmodified by the hook. If the hook needs to modify the tiddler then it should return a new tiddler object, for example:

	return new $tw.Tiddler(tiddler,{"my-field": value});

Hooks must not change the title field but can freely modify any other field of the tiddler.

Hook: th-renaming-tiddler

9th February 2017 at 2:56pm

This hook allows plugins to inspect tiddlers before they are modified by the tm-rename-tiddler message.

Hook function parameters:

  • newTiddler: tiddler object incorporating the rename
  • oldTiddler: optional existing tiddler object that will be overwritten

Return value:

  • newTiddler: tiddler object to be used for the renaming operation.

The original tiddler object can be returned unmodified by the hook. If the hook needs to modify the tiddler then it should return a new tiddler object, for example:

	return new $tw.Tiddler(tiddler,{"my-field": value});

Hooks must not change the title field but can freely modify any other field of the tiddler.

Hook: th-rendering-element

30th June 2020 at 12:12pm

This hook provides a notification that a DOM element is about to be rendered by the "element" widget. The hook can optionally provide an alternate parse tree that will be rendered in place of the intended element.

Note the element widget only renders those HTML elements that were parsed as plain HTML elements within wikitext (i.e. using the <tagname> syntax). This means that this hook is not invoked for elements created by other widgets.

Hook function parameters:

  • newParseTreeNodes: optional parse tree nodes provided by a previously called hook
  • widget: instance of the element widget invoking the hook

Return value:

  • newParseTreeNodes: optionally new parse tree nodes to replace the intended element, or a falsey value to leave the element untouched

Here is an example of a handler for this hook:

$tw.hooks.addHook("th-rendering-element",function(parseTreeNodes,widget) {
	// Return the previous mapping if there is one
	if(parseTreeNodes) {
		return parseTreeNodes;
	}
	// Detect the elements we're interested in
	if(someCondition()) {
		// Replace them with a parse tree
		return generateParseTreeNodes();
	}
	// Otherwise do nothing
	return null;
});

Hook: th-saving-tiddler

18th June 2020 at 11:48am

This hook allows plugins to inspect or modify tiddlers before they are saved via the confirm toolbar button; the hook is not invoked for tiddlers that are saved through other means, such as state tiddlers created by the ActionSetFieldWidget.

Hook function parameters:

  • newTiddler: tiddler object about to be saved
  • oldTiddler: tiddler object of draft tiddler that is being saved (from v5.1.23)

Return value:

  • tiddler object to be saved

The original tiddler object can be returned unmodified by the hook. If the hook needs to modify the tiddler then it should return a new tiddler object, for example:

	return new $tw.Tiddler(tiddler,{"my-field": value});

Hooks must not change the title field but can freely modify any other field of the tiddler.

Hook: th-server-command-post-start

9th April 2018 at 2:21pm

This hook allows plugins to extend the TiddlyWiki server command after it initializes. The two most obvious use cases are adding routes (such as an attachments folder for external files) to the SimpleServer instance and adding a websockets handler to the HTTP server.

Hook function parameters:

Return value is ignored.

HookMechanism

23rd September 2023 at 3:13am

The hook mechanism provides a way for plugins to intercept and modify default functionality.

Add a hook

Hooks are added as follows:

/*
name: name of hook function (by convention prefixed with `th-`)
handler: function to be called when hook is invoked
*/
$tw.hooks.addHook(name,handler);

Params and return

The handler function will be called with parameters that depend on the specific hook in question, but they always follow the pattern handler(value,params...)

  • value: an optional value that is to be transformed by the hook function
  • params: one or more optional parameters that are passed to the hook function

If required by the hook in question, the handler function must return the modified value.

Multiple handlers

Multiple handlers can be assigned to the same name using repeated calls. When a hook is invoked by name all registered functions will be called sequentially in their order of addition.

Note that the value passed to the subsequent hook function will be the return value of the previous hook function.

Be careful not to addHook in widget's render method, which will be call several times. You could addHook in methods that only called once, e.g. the constructor of widget class. Otherwise you should removeHook then add it again.

Timing of registration

Though not essential care should be taken to ensure that hooks are added before they are invoked.

For example: Hook: th-opening-default-tiddlers-list should ideally be added before the story startup module is invoked. Otherwise any hook specified additions to the default tiddlers will not be seen on the initial loading of the page, though will be visible if the user clicks the home button.

Remove a hook

You should clean up the callback when your widget is going to unmount.

$tw.hooks.removeHook(handler)

The handler should be the same function instance you used in addHook (check by ===). You can save it to this.xxxHookHandler on your widget, and call removeHook in destroy method.

Example

A working example of a hook that adds "test" to the default tiddlers.

$tw.hooks.addHook("th-opening-default-tiddlers-list",function(list) { 
    list.push("test");
    return list; 
});

How to create a translation for TiddlyWiki

22nd October 2014 at 2:52pm

Prerequisites

Setting Up

  1. Fork the TiddlyWiki GitHub repository (https://github.com/TiddlyWiki/TiddlyWiki5)
  2. Create a branch with the name of the translation you intend to create (eg "cy-GB" for "Welsh (United Kingdom)")
  3. Clone your forked repository to your computer (eg, /MyTranslation/TiddlyWiki5)
  4. Create a sibling directory /MyTranslation/jermolene.github.io
  5. Create a new folder in <repo>/languages for your translation
  6. Copy the contents of <repo>/core/language/en-GB into your translation folder
  7. Create a plugin.info file (see below) in your translation folder
  8. Edit <repo>/editions/tw5.com/tiddlywiki.info to add your language to the list
  9. Run ../build.jermolene.github.io/quick-bld.sh to build TiddlyWiki
  10. Open the TiddlyWiki file at /MyTranslation/jermolene.github.io/index.html
  11. You should see your translation listed in the control panel, but the text of the translation will still be in British English
  12. Edit the .tid and .multids files in your language folder to translate the English text

Content of plugin.info for Joe Bloggs' Welsh translation:

{
	"title": "$:/languages/cy-GB",
	"name": "cy-GB",
	"plugin-type": "language",
	"description": "Welsh (British)",
	"author": "JoeBloggs",
	"core-version": ">=5.0.0"
}

MultiTiddlerFiles make it possible to pack the text of several tiddlers in a single text file, simplifying some editing tasks.

Handling Updates

Sometimes the master en-GB language tiddlers are updated with revised content or new items. The best way to keep track of language-related commits to TiddlyWiki5:master is to monitor this RSS/Atom feed:

https://github.com/TiddlyWiki/TiddlyWiki5/commits/master/core/language.atom

How to create plugins in the browser

2nd May 2015 at 8:24am

The recommended technique for building TiddlyWiki plugins involves running TiddlyWiki on Node.js, but there is now an experimental technique for creating plugins directly in the browser.

Overview

Loading a plugin in the browser has several consequences:

  • The original plugin tiddler itself is unchanged
  • The payload tiddlers are set up as individual ShadowTiddlers

To make a modified copy of a plugin, one edits the constituent shadow tiddlers (doing this actually overrides the shadow tiddler with a new non-shadow tiddler containing the modified content). The repacking process retrieves the current value of all the shadow tiddlers included in the plugin, and then bundles the new values back into the original plugin tiddler.

Step by step

1. Setup your development environment

Start with a blank TiddlyWiki. It is useful to create a HelloThere tiddler that contains links to various tiddlers that you'll be opening frequently during plugin development:

  • The plugin itself (eg $:/plugins/yourname/pluginname)
  • The payload tiddlers that are to be packed into the plugin (eg $:/plugins/yourname/pluginname/mywidget.js)

2. Create the plugin tiddler

Click the link to the plugin tiddler to open it. Assuming it doesn't currently exist, it will open with an italicised title, indicating that it is a missing tiddler. Then switch to edit mode and set the following fields on the tiddler:

FieldValue
dependentsSpace separated list of dependent plugins (use square brackets for titles containing spaces)
descriptionPlugin description
namePlugin name
plugin-typeEither "plugin" for a regular plugin, "theme" for a theme, or "language" for a language pack
typeSet to "application/json"
versionSet to the version number of the plugin (eg "0.0.1")

Then in the body of the tiddler, insert:

{"tiddlers": {}}

Save the plugin tiddler

3. Modify the payload tiddlers

Create the payload tiddlers by clicking on the links in the HelloThere tiddler from step 1.

4. Pack the plugin

Open the browser developer console, and type the following JavaScript statement, but first change the first parameter to the name of your plugin. The second parameter is an optional array of tiddler titles to be added to the plugin:

$tw.utils.repackPlugin("$:/plugins/yourname/pluginname",["$:/plugins/yourname/pluginname/mywidget.js"])

You should see a confirmation message, and then if you inspect the plugin tiddler you should see that it has been filled with the payload tiddlers.

Each time you save the plugin the last portion of the version number is automatically incremented. This will ensure that users with an older version of your plugin will be able to install the new version.

5. Testing the plugin

To test the plugin, first make sure that it has been packed. Then save changes and refresh the page in order to load the new plugin.

6. Repacking the plugin

Once you've built the plugin for the first time you can omit the second parameter to repackPlugin() unless you are adding a new tiddler:

$tw.utils.repackPlugin("$:/plugins/yourname/pluginname")

7. Removing tiddlers from the plugin

To remove tiddlers from the plugin specify their titles in the optional third parameter:

$tw.utils.repackPlugin("$:/plugins/yourname/pluginname",null,["$:/plugins/yourname/pluginname/mywidget.js"])

Notes

Creating theme and language plugins

Before attempting to repack your plugin you should ensure that the plugin is selected as the current theme or language. Otherwise the shadow tiddlers will not be present.

How to customise the password prompt

6th October 2014 at 8:55am

You can customise the text and appearance of the password prompt that is displayed when encrypted TiddlyWiki files are first opened.

To do so, create a tiddler tagged containing:

  1. A JavaScript <script> tag containing code to override the configuration variable $tw.boot.encryptionPrompts.decrypt
  2. CSS <style> definitions targeting the tc-password-wrapper class to apply styles to the form

Raw markup tiddlers are spliced into the top of the standalone HTML file, and are executed before the boot prefix and boot kernel.

See $:/PatchEncryptionPrompt for an example.

How to run a local plugin library for testing

14th November 2022 at 11:05pm

Start the Library Server

The "pluginlibrary" edition contains the components needed to set up a local server for TiddlyWiki plugin library testing or development.

The following commands will create the library files and start a test server at http://localhost:8888

cd /your/path/to/TiddlyWiki5/editions/pluginlibrary
tiddlywiki --build test-server

Important

This server is read-only. Nothing is saved back to the filesystem

Test the Library with a Single File Wiki

Test the Library with a Node.js Wiki

  • Create a new wiki with eg:
cd /temp/
tiddlywiki my-wiki --init server
tiddlywiki my-wiki --listen

ImportLogging

14th November 2014 at 10:00am

Browsers still have significant variations in their handling of drag and drop and clipboard operations.

You can switch on special logging to help debug issues:

  1. Open your browser JavaScript console
  2. Type:
    $tw.log.IMPORT = true
  3. Drag or paste a file to import it
  4. Look for debug information in the console. For example:
    Importing file 'my-image.png', type: 'image/png', isBinary: true

index.svg

8th July 2014 at 8:38am

indexer modules

2nd October 2020 at 1:27am

Indexer modules maintain indexes of tiddlers organized in a manner that's more efficient for different types of access, typically to speed up things like certain filter operators. An example of this would be the tag indexer - it's much faster to maintain a lookup table listing which tiddlers have a given tag than to iterate over all of the tiddlers in a wiki and ask each of them if they have the tag you're interested in!

Indexer modules have a module-type of indexer, and the indexers that are included with TiddlyWiki can be found under core/modules/indexers.

Methods

Indexer modules must export a constructor function, which takes the current wiki object as its only argument. The object built by the construction function must implement the following methods:

init()

This performs any initial setup required by an indexer.

rebuild()

This rebuilds an index from scratch, usually after a large number of changes have happened to a wiki.

update(updateDescriptor)

This is called every time a tiddler is added, changed, or deleted. The updateDescriptor value is an object with two fields - old and new - which represent the pre-update and post-update state of the tiddler, respectively. Each of these has three fields of their own:

  • tiddler - the state of the tiddler (may be null)
  • shadow - a boolean indicating whether or not the tiddler is a shadow
  • exists - a boolean indicating whether or not the tiddler exists

For example, let's say you have an indexer idx and you create a tiddler T with the text "test" - that would result in your indexer's update method being called like this:

idx.update({
  old: { tiddler: null, shadow: false, exists: false },
  new: { tiddler: new $tw.Tiddler({title: 'T', text: 'test'}), shadow: false, exists: true }
});

If you then change the text from "test" to "testing", update would be called like this:

idx.update({
  old: { tiddler: new $tw.Tiddler({title: 'T', text: 'test'}), shadow: false, exists: true },
  new: { tiddler: new $tw.Tiddler({title: 'T', text: 'testing'}), shadow: false, exists: true }
});

And finally, if you delete T, update will be called like this:

idx.update({
  old: { tiddler: new $tw.Tiddler({title: 'T', text: 'testing'}), shadow: false, exists: true },
  new: { tiddler: null, shadow: false, exists: false }
});

Introduction

17th July 2014 at 9:26pm

TiddlyWiki is a personal notebook application based on a wiki application. In addition to a static Web-Site, TiddlyWiki is implemented as a single page application. This is a approach to build rich internet applications, it includes the possibility to put application logic into web-pages to make them dynamically. Furthermore this means the whole application is delivered in one HTML file, consisting of source code to dynamically change the view and behaviour of the application as well as the data of the application. During the runtime nothing must be loaded from a server to the TiddlyWiki application. The HTML file contains everything needed to start the application. TiddlyWiki is highly customisable because of a very sophisticated module concept. Except of a micro-kernel written in JavaScript the whole application consist of a own data-structure called tiddlers and a own markup language called wikiText. Even the modules are realised as tiddlers.

The aim of this documentation is to overview the idea behind the TiddlyWiki application as well as give a overview of the architecture to the reader. This means after reading the documentation the reader is has the knowledge how the overall application works and where the points are where the reader can extend the functionality of the application.

Section Overview:

TiddlyWiki - A quick Overview
This section describes the idea of the TiddlyWiki application. This should give the reader a overview over what TiddlyWiki consists of give a brief introduction to the topics of the main documentation.

Microkernel Architecture
The heart of TiddlyWiki is it's microkernel. This section will describe what bare mechanisms are included in the microkernel and how additional modules are loaded.

TiddlyWiki Core Application
After the microkernel has been explained this section focuses on the core plug-in. It describes the central modules of the core and explains how they work together to build a single file application.

Conclusion
A briefly summary about this documentation bringing out the advantages, disadvantages and experiences with TiddlyWiki.

JavaScript Macros

24th April 2014 at 1:01pm

Macros can be implemented as JavaScript modules as well as via the wikitext syntax.

Overview

JavaScript macros are modules with their module-type field set to macro. They must export these three properties:

  • name: A string giving the name used to invoke the macro
  • params: An array of objects with the following properties:
    • name: name of the parameter
    • default: (optional) default value for the parameter
  • run: Function called when the macro requires evaluation. The parameters are pulled from the macro call and arranged according to the params array. The run function should return the string value of the macro. When invoked, this points to the widget node invoking the macro.

Note that if the params array is missing or blank, then all the supplied parameters are passed to the run() method.

Writing JavaScript macros

There are several JavaScript macros built into the core which can serve as a jumping off point for your own macros:

https://github.com/TiddlyWiki/TiddlyWiki5/tree/master/core/modules/macros

Note that JavaScript macros work on both the client and the server, and so do not have access to the browser DOM.

Macro Behaviour

Macros are just used to return a chunk of wikitext for further processing. They should not make modifications to tiddlers in the wiki store. The reason is that you cannott control when the macro is called; it may be called repeatedly as part of refresh processing. So it is important that macros do not have any other side effects beyond generating their text.

Javascript Widget Tutorial

2nd March 2024 at 11:06am

Introduction

This tutorial provides step-by-step, interactive examples of how to write code for tiddlywiki widgets. The demo javascript code can be modified without having to reload the entire wiki.

Intended audience:

  1. Those who know tiddlywiki well and know programming and javascript and want to write their own widget.
  2. Those who know tiddlywiki well and don't know javascript, but want to understand more about how tiddlywiki works. You should be able to skim through and interact with the demos and learn something.

We don't make any effort to explain javascript here. For that you will need other resources, like MDN.

The tutorial

Notes

tiddlywiki doesn't support dynamically reloading javascript. If you change a javascript tiddler, then you need to save and reload the wiki before the changes will take affect.

To avoid the need for such reloads, the excellent innerwiki plugin is used. This allows an inner wiki to be created from a subset of tiddlers in the outer wiki. Each time the inner wiki is refreshed, its entire wiki is reloaded and the javascript changes in the inner wiki will take affect.

Without the need for reloads, a tiddlywiki instance with the innerwiki plugin installed works great as a playground for interacting with tiddlywiki javascript.

Other documentation on writing TW widgets

Full API doc

Github Pages of TW5-Typed

LazyLoadingMechanism

6th February 2014 at 10:19pm

TiddlyWiki currently only implements LazyLoading when it is running in the browser talking to a TiddlyWeb-compatible server.

In the configuration for TiddlyWeb, the browser first requests a "skinny" version of each tiddler (consisting of all the fields apart from the text field). Subsequently, an attempt to read those skinny tiddlers with wiki.getTiddler() returns just the skinny fields, but an attempt to read one using wiki.getTiddlerText() will trigger an asynchronous load of the full tiddler text, which in turn triggers a refresh cycle, updating the display to reflect the newly loaded tiddler. Widgets that loop through all tiddlers are fine; it's only if they trigger wiki.getTiddlerText() for a tiddler that it will get loaded.

Lazy loading can also be used with TiddlyWiki's own server. The core provides a template that enables images to be lazily loaded while other tiddlers are packaged into the initial HTML file in the usual way.

The browser-based search built into TiddlyWiki5 will only search the text of tiddlers that have been fully loaded. The expectation is that when lazy loading is used in a client-server configuration, then it is the server that really needs to handle search operations, because it is only the server that can "see" the text of all tiddlers. So, the plan is to integrate the built in search with TiddlyWeb's search API. The simplest approach is that any local search triggers an asynchronous server side search. The results of the search would be asynchronously loaded such that they would dynamically appear in the local search results.

Messages

17th July 2014 at 6:16pm

Microkernel

17th July 2014 at 7:55pm

In the universe of TiddlyWiki everything is a tiddler. Even the application logic is stored in tiddlers that are marked as "application/javascript". These tiddlers, which contain application logic, are called modules and a CommonJS compatible module system is responsible for assembling the individual modules into the TiddlyWiki application. The result is a tree representing the whole TiddlyWiki application containing module tiddlers, data tiddlers and some JavaScript functions and objects.

Only a small part of the TiddlyWiki is not managed as tiddlers, the microkernel. The microkernel is the first thing to run, when the application is started and it puts some initial objects and functions into the application tree, which are needed to load and manage tiddlers. After the microkernel built this initial application tree, the remaining parts of the application can be loaded as module tiddlers. Beside some utility functions the most important object that is contributed by the boot kernel is "$tw.wiki", consisting of JavaScript structures and functions that are used to store and manage the loaded tiddlers. Among other things this store can be used to add new tiddlers, remove tiddlers and retrieve tiddlers by name.

The microkernel constructs a initial $tw object containing the needed structures and functions.

Microkernel and Datamodel

9th July 2014 at 9:36am

The microkernel is responsible for creating a bare-bones TW environment. It is running under Node.js or in a HTML5 Browser. The Bootkernel just loads enough functionality to load the modules containing the main logic of the application. This boot-kernel contains a few helper methods, the module mechanism as well as the function to create a tiddler and manage them. The boot-kernel also creates the bare-bones wiki store, which holds all the information of the wiki during the runtime. After creating the store, the boot-kernel is in charge of decrypting the encrypted tiddlers and extracting all the tiddlers e.g. the core module tiddlers embedded in the DOM structure of the HTML file. Furthermore the boot kernel offers the functionality to load tiddlers from a file, when you run TW with Node.js.

Microkernel Architecture

17th July 2014 at 9:16pm

This section describes the architecture of the TiddlyWiki-kernel. TiddlyWiki is based on a micro-kernel which provides only a small stack of functions. This design decision was made to introduce a cleaner mechanism for customisation of TiddlyWiki. This section also describes the data-model of TiddlyWiki called tiddler. And it gives a overview to the module system which developers can use to extend the functionality of the TiddlyWiki application.

Microkernel Description

The TiddlyWiki application is based on a microkernel architecture, that means it separate minimal functional core from the extended functionality. The microkernel provides the functionality to load external extensions to extend its core features. The TiddlyWiki microkernel provides a few helper methods but the main task of the TiddlyWiki kernel is to provide a basic functionality for storing data and loading the extension plug-ins. Within the TiddlyWiki architecture everything is stored as a tiddler. How the architecture of TiddlyWiki stores his data during the runtime of the application is shown in Datamodel, but the kernel provides this datamodel. It also prepares the functionality to store and create these tiddlers. In favour it creates a store to manage the tiddlers during the runtime. Without any extensions the microkernel is not able to persist the tiddlers, this means the data is only holded in the RAM. Here are some example interfaces for working with tiddlers in memory which are provided by the kernel:

  • $tw.Tiddler = function(/* [fields,] fields */)
  • $tw.Wiki.addTiddler = function(tiddler)
  • $tw.Wiki.deleteTiddler = function(title)
  • $tw.Wiki.getTiddler = function(title)

An additional feature of the microkernel is the ability to encrypt and decrypt a block of text. To provide this functionality the microkernel has a built in password-vault to store the current used password. The library used to encrypt and decrypt data is the StandfordJavaScript Crypto Libary. This feature allows the micro-kernel to load encrypted tiddlers from the TiddlyWiki file, but it also allows extension plug-ins to use the encrypt functionality e.g. to persist an encrypted TiddlyWiki.

In order to load extension plug-ins the kernel prepares a interface to load and execute these plug-ins. Therefore the micro-kernel provides some deserializers to extract different type of tiddlers e.g. from the TiddlyWiki-File. Within the microkernel a bunch of different deserializer are installed. These deserializer are needed because every tiddler can include a different type of data for example tiddlers can contain javaScript-code, text, html-text or ~JSON-data. Even after packaging a TiddlyWiki application every plug-in is stored as a tiddler within the TiddlyWiki-Document. This feature is specified in the Module System section. Therefore the micro-kernel need the functionality to parse all the different type of tiddlers. To differ between the different type of tiddlers every tiddler has a file type which are generated by the microkernel

The image below shows the startup process of the TiddlyWiki-kernel. The bootprefix is responsible for preparing the kernel to boot on different engines e.g. browsers and node.js. Afterwards the main boot process which includes the microkernel, with the startup-method, is started. After successfully running these steps the main architecture is loaded. The last step is to run the startup modules. These modules are described later. But in brief this is the point where the TiddlyWiki microkernel can be extended by own functionality. Every module marked as "startup" is started after finishing the boot process of the kernel.

The microkernel builds up the essential functions and structures and initiates a startup sequence.

Datamodel

The micro-kernel creates a TiddlyWiki specific data-structure called tiddler. Here you have to separate the different definition of tiddlers. In the architectural view a tiddler is a JavaScript object holding some data. In the overall concept a tiddler is similar to a wiki page in a normal wiki application like wikipedia. In this section we describe the architectural view of a tiddler. The listing below shows the JSON representation of a tiddler object. During the runtime of the TiddlyWiki everything is saved an object like this. Without any plug-in the architecture is not able to persist any kind of data. All the data is stored in a store. This store is a JavaScript object. This store is constructed like a Map with a bunch of key value pairs. Every tiddler has its name as the key in the map and the JavaScript-Object as the value. The tiddler concept is the main data-model within TiddlyWiki, everything from data up to plug-ins is stored as a tiddler.

{"fields":{
   "text":"example Text",
   "title":"Infrastruktur",
   "tags":["vs"],
   "modified":"2014-07-01T16:25:01.230Z",
   "myField":"myFieldValue",
   "created":"2014-07-01T16:22:10.673Z"
   }
}

Module System

After the boot kernel provides the functions used to load tiddlers, the rest of the TiddlyWiki application is loaded as modules. A module is a tiddler which has the type application/javascript and contains CommonJS compatible JavaScript code. This means a single module provides its public structures and functions in a variable called export. Other modules can obtain these structures and functions by using a global require function.

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

In most cases these module tiddlers are packed into a plug-in. Following the "everything is a tiddler" concept, a plug-in is a tiddler, which contains a bunch of other tiddlers. These tiddlers are first converted into a JSON structure which then becomes the body of the plug-in tiddler. This is not restricted to module tiddlers. A plug-in can contain any tiddlers. This way a developer can put for example simple modules, widgets, UI parts written with WikiText, even new filter operators or extensions to the WikiText parser into a plug-in tiddler. In fact the whole TW core is provided as a single plug-in. Tiddlers provided in a plug-in are called shadow tiddlers and can not be edited. Instead, when trying to edit a shadow tiddler, a new tiddler with the same name is created which then "overrides" the shadow tiddler.

Instead of requiring a specific module directly, a module developer can specify the type of the module he is developing by setting the field "module-type" of the containing tiddler. For example, by providing a module-type of "saver", TiddlyWiki knows that this module implements a way of saving the whole wiki and when the user clicks on the save button, TiddlyWiki automaticly considers the provided module to save the current state.

Microkernel Description

17th July 2014 at 9:33pm

The TiddlyWiki application is based on a microkernel architecture, that means it separate minimal functional core from the extended functionality. The microkernel provides the functionality to load external extensions to extend its core features. The TiddlyWiki microkernel provides a few helper methods but the main task of the TiddlyWiki kernel is to provide a basic functionality for storing data and loading the extension plug-ins. Within the TiddlyWiki architecture everything is stored as a tiddler. How the architecture of TiddlyWiki stores his data during the runtime of the application is shown in Datamodel, but the kernel provides this datamodel. It also prepares the functionality to store and create these tiddlers. In favour it creates a store to manage the tiddlers during the runtime. Without any extensions the microkernel is not able to persist the tiddlers, this means the data is only holded in the RAM. Here are some example interfaces for working with tiddlers in memory which are provided by the kernel:

  • $tw.Tiddler = function(/* [fields,] fields */)
  • $tw.Wiki.addTiddler = function(tiddler)
  • $tw.Wiki.deleteTiddler = function(title)
  • $tw.Wiki.getTiddler = function(title)

An additional feature of the microkernel is the ability to encrypt and decrypt a block of text. To provide this functionality the microkernel has a built in password-vault to store the current used password. The library used to encrypt and decrypt data is the StandfordJavaScript Crypto Libary. This feature allows the micro-kernel to load encrypted tiddlers from the TiddlyWiki file, but it also allows extension plug-ins to use the encrypt functionality e.g. to persist an encrypted TiddlyWiki.

In order to load extension plug-ins the kernel prepares a interface to load and execute these plug-ins. Therefore the micro-kernel provides some deserializers to extract different type of tiddlers e.g. from the TiddlyWiki-File. Within the microkernel a bunch of different deserializer are installed. These deserializer are needed because every tiddler can include a different type of data for example tiddlers can contain javaScript-code, text, html-text or ~JSON-data. Even after packaging a TiddlyWiki application every plug-in is stored as a tiddler within the TiddlyWiki-Document. This feature is specified in the Module System section. Therefore the micro-kernel need the functionality to parse all the different type of tiddlers. To differ between the different type of tiddlers every tiddler has a file type which are generated by the microkernel

The image below shows the startup process of the TiddlyWiki-kernel. The bootprefix is responsible for preparing the kernel to boot on different engines e.g. browsers and node.js. Afterwards the main boot process which includes the microkernel, with the startup-method, is started. After successfully running these steps the main architecture is loaded. The last step is to run the startup modules. These modules are described later. But in brief this is the point where the TiddlyWiki microkernel can be extended by own functionality. Every module marked as "startup" is started after finishing the boot process of the kernel.

The microkernel builds up the essential functions and structures and initiates a startup sequence.

Modularization

15th July 2014 at 8:34am

The whole application is basically built from three parts. At first, the microkernel provides the basic functionality to handle tiddlers. The second part are tiddlers representing core functionality. These are for example modules which extend the store by more sophisticated functions, UI tiddlers and widget modules, a WikiText parser, sophisticated deserializers, savers, syncadapters, etc. These core modules are provided as plug-in to the microkernel. Consequently, a plug-in is a single tiddler which itself contains multiple tiddlers, forming the plug-in. Each of this tiddler might be a module providing new functionality (i.e. a module tiddler marked with "module-type: saver" can extend the application with new methods of saving the current wiki state.). Tiddlers provided in plug-ins are called shadow tiddlers. They are immutable and can not be edited or deleted but we can create a new tiddler with the same name to override a shadow tiddler.

Module System

15th July 2014 at 9:48am

After the boot kernel provides the functions used to load tiddlers, the rest of the TiddlyWiki application is loaded as modules. A module is a tiddler which has the type application/javascript and contains CommonJS compatible JavaScript code. This means a single module provides its public structures and functions in a variable called export. Other modules can obtain these structures and functions by using a global require function.

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

In most cases these module tiddlers are packed into a plug-in. Following the "everything is a tiddler" concept, a plug-in is a tiddler, which contains a bunch of other tiddlers. These tiddlers are first converted into a JSON structure which then becomes the body of the plug-in tiddler. This is not restricted to module tiddlers. A plug-in can contain any tiddlers. This way a developer can put for example simple modules, widgets, UI parts written with WikiText, even new filter operators or extensions to the WikiText parser into a plug-in tiddler. In fact the whole TW core is provided as a single plug-in. Tiddlers provided in a plug-in are called shadow tiddlers and can not be edited. Instead, when trying to edit a shadow tiddler, a new tiddler with the same name is created which then "overrides" the shadow tiddler.

Instead of requiring a specific module directly, a module developer can specify the type of the module he is developing by setting the field "module-type" of the containing tiddler. For example, by providing a module-type of "saver", TiddlyWiki knows that this module implements a way of saving the whole wiki and when the user clicks on the save button, TiddlyWiki automaticly considers the provided module to save the current state.

Motovun Jack.svg

NEW NEW TOC

17th July 2014 at 6:29pm
  1. Introduction
  2. TiddlyWiki - A quick Overview
  3. Microkernel Architecture
  4. TiddlyWiki Core Application
  5. Conclusion

overview.svg

8th July 2014 at 3:54pm

package.json for NW.js

1st January 2014 at 5:48pm
{
    "name": "tiddlywiki",
    "main": "./index.html",
	"window": {
		"toolbar": true,
		"width": 1024,
		"height": 768
	}
}

Parser

17th July 2014 at 8:23pm

The first stage of WikiText processing is the parser. A Parser is provided by a module with module-type: parser and is responsible to transform block of text to a parse-tree. The parse-tree consists of nested nodes like

{type: "element", tag: <string>, attributes: {}, children: []} - an HTML element
{type: "text", text: <string>} - a text node
{type: "entity", entity: <string>} - an HTML entity like &copy; for a copyright symbol
{type: "raw", html: <string>} - raw HTML

The core plug-in provides a recursive descent WikiText parser which loads it's individual rules from individual modules. Thus a developer can provide additional rules by using module-type: wikirule. Each rule can produce a list of parse-tree nodes. A simple example for a wikirule producing a <hr> from --- can be found in horizrule.js

HTML tags can be embedded into WikiText because of the html rule. This rule matches HTML tag syntax and creates type: "element" nodes. But the html-rule has another special purpose. By parsing the HTML tag syntax it implicitly parses WikiText widgets. It the recognises them by the $ character at the beginning of the tag name and instead of producing "element" nodes it uses the tag name for the type:

{type: "list", tag: "$list", attributes: {}, children: []} - a list element

The Widgets part will reveal why this makes sense and how each node is transformed into a widget. Another special characteristic of the html-rule or the parse nodes in general is the attributes property. Attributes in the parse-tree are not stored as simple strings but they are nodes of its own to make indirect text references available as attributes as described in Widgets:

{type: "string", value: <string>} - literal string
{type: "indirect", textReference: <textReference>} - indirect through a text reference

ParserSubclassingMechanism

3rd February 2021 at 3:08pm

Introduction

The wikitext parser subclassing mechanism makes it possible for custom parsers to use the wiki text parsing engine, but to customise the rules that are used.

Example

Here is an example of a subclass of the checkbox widget that adds logging to the event handler:

(function(){

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

var WikiParser = require("$:/core/modules/parsers/wikiparser/wikiparser.js")["text/vnd.tiddlywiki"],
	HtmlParser = $tw.modules.createClassFromModule(require("$:/core/modules/parsers/wikiparser/rules/html.js"),$tw.WikiRuleBase),
	EntityParser = $tw.modules.createClassFromModule(require("$:/core/modules/parsers/wikiparser/rules/entity.js"),$tw.WikiRuleBase);

var MyCustomWikiParser = function(type,text,options) {
	var parser = new WikiParser(type,text,$tw.utils.extend({},options,{
		// Force the parser to parse in inline mode
		parseAsInline: true,
		// Specify which rules will be used
		rules: {
			pragma: [],
			block: [],
			inline: [HtmlParser,EntityParser]
		}
	}));
	this.tree = parser.tree;
	this.prototype = parser.prototype;
};

exports["text/vnd.my-custom-type"] = MyCustomWikiParser;

})();

ParsingMechanism

1st November 2013 at 1:07pm

Introduction

The parsing mechanism analyses the text of a tiddler against a set of parsing rules, producing a tree representing the structure of the text. The RenderingMechanism is used to transform parse trees into render trees of widget nodes.

TiddlyWiki5 includes ParserModules for several types of tiddler:

  • WikiText
  • Raw HTML
  • Plain text
  • Images (bitmap, SVG and PDF)

The WikiText parser is the most complex, comprising separate individual WikiRuleModules encapsulating each parsing rule.

Parse Trees

The output of parsing a tiddler is an object containing a tree of parse nodes corresponding to the original text. For example:

> JSON.stringify($tw.wiki.parseText("text/vnd.tiddlywiki","Some //italics// and a {{Transclusion}}.").tree)

[
	{type: "element", tag: "p", children: [
		{type: "text", text: "Some "},
		{type: "element", tag: "em", children: [
			{type: "text", text: "italics"}
		]},
		{type: "text", text: " and a "},
		{type: "tiddler", attributes:{
			tiddler: {type: "string", value: "Transclusion"}
		}, children:[
			{type: "transclude", attributes:{
				tiddler: {type: "string", value: "Transclusion"}
			}}
		]},
		{type: "text", text: "."}
	]}
]

Parse tree nodes are plain JavaScript objects, and do not have a prototype.

Persist data

8th July 2014 at 8:57am

TiddlyWiki supports a wide range of methods to persist your data. One of this methods is the HTML5 fallback saver. This methods works on almost every browser. With this method a copy of the entire wiki will be downloaded by the browser. This means you get a new file everytime you hit the save button. To avoid this and because every Browser has a different API to allow writing direct to the file system there a some plugins for the different browsers. These plug-ins allow the user to save direct to the current open TiddlyWiki-File. The Listing below shows the HTML5-compliant to save the changes via the HTML5 fallback saver by downloading the TW as a complete HTML-file.

DownloadSaver.prototype.save = function(text,method,callback) {
	...
	var link = document.createElement("a");
	link.setAttribute("target","_blank");
	...
	link.setAttribute("href","data:text/html," + encodeURIComponent(text));
	...
	link.setAttribute("download",filename);
	document.body.appendChild(link);
	link.click();
	document.body.removeChild(link);
	return true;
};

ReadMe for build.jermolene.github.io

These scripts are used to build and release the content for tiddlywiki.com. They are not designed for general purpose use – they resolve problems that are specific to the task of building tiddlywiki.com: pushing to GitHub Pages, handling the prerelease builds and bumping version numbers.

Nonetheless, you may find techniques that are useful for your own scripts.

Hosting

https://tiddlywiki.com is served by GitHub Pages from the repository https://github.com/TiddlyWiki/tiddlywiki.com-gh-pages

The scripts live in the repository https://github.com/Jermolene/build.jermolene.github.io

Directory structure

These scripts require the following directories to be siblings:

The scripts are designed to be executed with the current directory being the TiddlyWiki5 directory.

Configuration

package.json

The package.json in the root of the build.jermolene.github.io repository contains a dependency declaration that specifies the latest official released version of TiddlyWiki to be used when building the release targets:

  "dependencies": {
    "tiddlywiki": "5.1.2"
  }

Environment variables

Some of the scripts use the following environment variables:

  • TW5_BUILD_MAIN_EDITION - the path to the wiki folder to be used as the main edition, generating index.html and encrypted.html
  • TW5_BUILD_OUTPUT - the path to the output folder (defaults to ../jermolene.github.io)
  • TW5_BUILD_TIDDLYWIKI - the pathname of the tiddlywiki.js to be used (defaults to ../build.jermolene.github.io/node_modules/tiddlywiki/tiddlywiki.js)

Scripts

bld.sh

Builds the tiddlywiki.com target files. By default, it uses the version of tiddlywiki specified in the package.json file. This can be overridden with the TW5_BUILD_TIDDLYWIKI environment variable. The following command would select the latest prerelease version of tiddlywiki from the TiddlyWiki5 directory:

    TW5_BUILD_TIDDLYWIKI=./tiddlywiki.js

readme-bld.sh

Builds the readme files for the TiddlyWiki5 and build.jermolene.github.io repos using the released version of TiddlyWiki specified in package.json.

prerelease-bld.sh

Builds the tiddlywiki.com/prerelease target files using the latest TiddlyWiki prerelease code and special prerelease edition for the content.

github-push.sh

Pushes the latest changes to the jermolene.github.io directory to GitHub.

dev-bld.sh

Builds the dev prerelease edition.

quick-bld.sh

Builds the prerelease prerelease edition.

tiddlyspace-upload.sh

Builds the tw5tiddlyweb edition and uploads it to TiddlySpace.

verbump.sh

Bumps the version number of the package.json in the TiddlyWiki5 repo and applies the correct version tag to the repo.

npm-publish.sh

Publishes the TiddlyWiki5 repo to npm.

Procedures

Releasing a new version of TiddlyWiki

Preparation on master

  1. Ensure the new release banner image is up to date
  2. Update master with changes from tiddlywiki-com
  3. Verify the version numbers in $:/config/OfficialPluginLibrary in core/wiki/config/OfficialPluginLibrary.tid
  4. Move the latest release note from the prerelease edition into the tw5.com edition
  5. Adjust the release date and the released field of the latest release tiddler (eg, Release 5.1.3)
  6. Also adjust the github.com comparison link to point to the tag for the new release
  7. Ensure TiddlyWiki Releases has the new version as the default tab
  8. Adjust the modified time of HelloThere
  9. Make sure master is fully committed

Update Readmes

  1. Edit package.json to the new version number
  2. Run ./bin/readme-bld.sh to build the readme files
  3. Commit the new readme files to master
  4. Restore package.json to the previous version number

Make New Release

  1. Run ./bin/verbump "5.1.3" (substituting the correct version number) to update the version number, assign it a tag
  2. Run ./bin/npm-publish.sh to publish the release to npm
  3. Verify that the new release of TiddlyWiki is available at https://www.npmjs.org/package/tiddlywiki

Update tiddlywiki.com release

  1. Update tiddlywiki-com from master and push to GitHub

Cleaning Up

  1. Tweet the release with the text "TiddlyWiki v5.x.x released to https://tiddlywiki.com #newtiddlywikirelease"

Preparation for the next release in master

  1. Adjust version number in package.json
  2. Adjust version number in bin/build-site.sh
  3. Adjust version number in $:/config/OfficialPluginLibrary (both in editions/tw5.com and editions/prerelease/tiddlers/system) and $:/config/LocalPluginLibrary
  4. Adjust new release banner
  5. Create the release note for the new release
  6. Commit changes to master and push to GitHub

Releasing new content for TiddlyWiki

To update https://tiddlywiki.com with new content, make a Pull Request with the updated tiddlers to the tiddlywiki-com branch. As soon as the PR is merged, the Continuous Deployment system will automatically rebuild the site.

Note that the PR should only include updates within the editions folder of the repo.

refreshcount.js

2nd February 2019 at 2:34pm
/*\

widget to count the number of times this widget refreshes

\*/
(function() {

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

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

var MyWidget = function(parseTreeNode, options) {
	this.refreshCount = 0;
	this.initialise(parseTreeNode, options);
};

/*
Inherit from the base widget class
*/
MyWidget.prototype = new Widget();

/*
Render this widget into the DOM
*/
MyWidget.prototype.render = function(parent, nextSibling) {
	this.parentDomNode = parent;
	var textNode = this.document.createTextNode(this.refreshCount + " refreshes");
	parent.insertBefore(textNode, nextSibling);
	this.domNodes.push(textNode);
};

MyWidget.prototype.refresh = function(changedTiddlers) {
	// Regenerate and rerender the widget and replace the existing DOM node
	this.refreshCount++;
	this.refreshSelf();
	return true;
};

exports.refreshcount = MyWidget;

})();

Releasing a new version of TiddlyWiki

9th August 2019 at 12:34pm

Preparation on master

  1. Ensure the new release banner image is up to date
  2. Update master with changes from tiddlywiki-com
  3. Verify the version numbers in $:/config/OfficialPluginLibrary in core/wiki/config/OfficialPluginLibrary.tid
  4. Move the latest release note from the prerelease edition into the tw5.com edition
  5. Adjust the release date and the released field of the latest release tiddler (eg, Release 5.1.3)
  6. Also adjust the github.com comparison link to point to the tag for the new release
  7. Ensure TiddlyWiki Releases has the new version as the default tab
  8. Adjust the modified time of HelloThere
  9. Make sure master is fully committed

Update Readmes

  1. Edit package.json to the new version number
  2. Run ./bin/readme-bld.sh to build the readme files
  3. Commit the new readme files to master
  4. Restore package.json to the previous version number

Make New Release

  1. Run ./bin/verbump "5.1.3" (substituting the correct version number) to update the version number, assign it a tag
  2. Run ./bin/npm-publish.sh to publish the release to npm
  3. Verify that the new release of TiddlyWiki is available at https://www.npmjs.org/package/tiddlywiki

Update tiddlywiki.com release

  1. Update tiddlywiki-com from master and push to GitHub

Cleaning Up

  1. Tweet the release with the text "TiddlyWiki v5.x.x released to https://tiddlywiki.com #newtiddlywikirelease"

Preparation for the next release in master

  1. Adjust version number in package.json
  2. Adjust version number in bin/build-site.sh
  3. Adjust version number in $:/config/OfficialPluginLibrary (both in editions/tw5.com and editions/prerelease/tiddlers/system) and $:/config/LocalPluginLibrary
  4. Adjust new release banner
  5. Create the release note for the new release
  6. Commit changes to master and push to GitHub

Releasing new content for TiddlyWiki

9th August 2019 at 10:42am

To update https://tiddlywiki.com with new content, make a Pull Request with the updated tiddlers to the tiddlywiki-com branch. As soon as the PR is merged, the Continuous Deployment system will automatically rebuild the site.

Note that the PR should only include updates within the editions folder of the repo.

rendering.svg

17th July 2014 at 7:43pm

RenderingMechanism

1st November 2013 at 1:07pm

RootWidget and Rendering Startup

17th July 2014 at 6:23pm

The previous parts of this chapter showed how WikiText is transformed to DOM nodes which dynamically react to tiddler changes and a way to compose tiddlers from other tiddlers. This last part describes how the TiddlyWiki core plug-in starts up a UI build from tiddlers and WikiText.

After the microkernel has loaded it starts executing Startup Modules. The core plug-in contains two startup modules which are responsible to kick off the UI:

rootwidget.js is a startup module and creates an instance of the base widget. This widget is globally accessible $tw.rootWidget. The DOM node associated to this widget is the current browser window's DOM (document).

At first, the root widget has no children but provides some basic event handlers (Messages) like:

  • tm-notify: Displays the message given in param as a notification.
  • tm-save-wiki: Triggered by a save button, the user can click. This handler uses the syncer module described in Extended Persistence to save the current wiki.
  • tm-auto-save-wiki: Similar to tm-save-wiki but not triggered directly by the user but automatically triggered when a wiki page is edited and saved. A Saver implementation which starts a download of the updated wiki file would not support the auto-save method and would only be used when the tm-save-wiki message is used.
  • tm-download-file: This message also uses the syncer module described in Extended Persistence but explicitly demands to choose a saver with the download-method to start downloading a single tiddler.

After the root widget is loaded another startup module $:/core/modules/startup/render.js creates a transclude widget which contains the contents of $:/core/ui/PageTemplate which is now bound to the browsers DOM document. The render function of the transclude widget is initially executed and a listener is registered at the store which executes the refresh function of the transclude widget to trigger the Selective Update process.

Techniques for including other tiddlers and Templates are finally used in $:/core/ui/PageTemplate to build the TiddlyWiki UI only from tiddlers written in WikiText (with widgets implemented in javascript):

For example to implement the list of open wiki pages the $:/core/ui/PageTemplate contains a navigator widget which maintains a list of open tiddlers in a field of $:/StoryList and handles events like tm-navigate by adding a tiddler specified as parameter to the top of the list in $:/StoryList. The story tiddler transcluded in $:/core/ui/PageTemplate then uses a ListWidget to transclude all tiddlers in $:/StoryList through a special template $:/core/ui/ViewTemplate. A event of the type tm-close-tiddler would remove a specified tiddler from $:/StoryList. The Event Mechanism would trigger a changed event which triggers a call of the ListWidget's refresh function which would remove the tiddler from the list, closing the tiddler.

Saver

15th July 2014 at 6:49pm

Modules with module-type: saver provide functionality to save the whole wiki. There are three methods a saver can support:

save
This method is used, when the user requests a save, for example by clicking the save button in the sidebar.
autosave
This method is used automatically by TW when tiddlers are changed, created or deleted by the user.
download
This message is used when the wiki or a single tiddler should explicitly be downloaded. The control panel for example uses this method to provide a button which saves the wiki as a static HTML file.

A saver module has to export two functions. canSave(wiki) returning true if this module is capable of working and create(wiki) returning an instance of a saver object. This saver object has to provide an info property containing a name, a priority, an array of methods it supports and a method save(text,method,callback). This method is called from TW with the actual text which should be saved, the method which is used and a callback function to report errors: callback("Error while saving") or to notify that saving went well: callback(null, "Saving went well :)"). If the saver method successfully saved the file it has to return true, or false otherwise. Saves are triggered by messages from the UI. The syncer module uses the saver with the highest priority capable of the requested method to save the file.

The core plug-in contains a saver capable of saving the current state of the wiki to the local hard drive by using a special Firefox extension called Tiddlyfox. If this extension is not available, the savers canSave method would return false. A saver with a lower priority would then ask the user to save the current state as a new HTML file.

Scripts for building tiddlywiki.com

These scripts are used to build and release the content for tiddlywiki.com. They are not designed for general purpose use – they resolve problems that are specific to the task of building tiddlywiki.com: pushing to GitHub Pages, handling the prerelease builds and bumping version numbers.

Nonetheless, you may find techniques that are useful for your own scripts.

Hosting

https://tiddlywiki.com is served by GitHub Pages from the repository https://github.com/TiddlyWiki/tiddlywiki.com-gh-pages

The scripts live in the repository https://github.com/Jermolene/build.jermolene.github.io

Directory structure

These scripts require the following directories to be siblings:

The scripts are designed to be executed with the current directory being the TiddlyWiki5 directory.

Configuration

package.json

The package.json in the root of the build.jermolene.github.io repository contains a dependency declaration that specifies the latest official released version of TiddlyWiki to be used when building the release targets:

  "dependencies": {
    "tiddlywiki": "5.1.2"
  }

Environment variables

Some of the scripts use the following environment variables:

  • TW5_BUILD_MAIN_EDITION - the path to the wiki folder to be used as the main edition, generating index.html and encrypted.html
  • TW5_BUILD_OUTPUT - the path to the output folder (defaults to ../jermolene.github.io)
  • TW5_BUILD_TIDDLYWIKI - the pathname of the tiddlywiki.js to be used (defaults to ../build.jermolene.github.io/node_modules/tiddlywiki/tiddlywiki.js)

Scripts

bld.sh

Builds the tiddlywiki.com target files. By default, it uses the version of tiddlywiki specified in the package.json file. This can be overridden with the TW5_BUILD_TIDDLYWIKI environment variable. The following command would select the latest prerelease version of tiddlywiki from the TiddlyWiki5 directory:

    TW5_BUILD_TIDDLYWIKI=./tiddlywiki.js

readme-bld.sh

Builds the readme files for the TiddlyWiki5 and build.jermolene.github.io repos using the released version of TiddlyWiki specified in package.json.

prerelease-bld.sh

Builds the tiddlywiki.com/prerelease target files using the latest TiddlyWiki prerelease code and special prerelease edition for the content.

github-push.sh

Pushes the latest changes to the jermolene.github.io directory to GitHub.

dev-bld.sh

Builds the dev prerelease edition.

quick-bld.sh

Builds the prerelease prerelease edition.

tiddlyspace-upload.sh

Builds the tw5tiddlyweb edition and uploads it to TiddlySpace.

verbump.sh

Bumps the version number of the package.json in the TiddlyWiki5 repo and applies the correct version tag to the repo.

npm-publish.sh

Publishes the TiddlyWiki5 repo to npm.

Procedures

Releasing a new version of TiddlyWiki

Preparation on master

  1. Ensure the new release banner image is up to date
  2. Update master with changes from tiddlywiki-com
  3. Verify the version numbers in $:/config/OfficialPluginLibrary in core/wiki/config/OfficialPluginLibrary.tid
  4. Move the latest release note from the prerelease edition into the tw5.com edition
  5. Adjust the release date and the released field of the latest release tiddler (eg, Release 5.1.3)
  6. Also adjust the github.com comparison link to point to the tag for the new release
  7. Ensure TiddlyWiki Releases has the new version as the default tab
  8. Adjust the modified time of HelloThere
  9. Make sure master is fully committed

Update Readmes

  1. Edit package.json to the new version number
  2. Run ./bin/readme-bld.sh to build the readme files
  3. Commit the new readme files to master
  4. Restore package.json to the previous version number

Make New Release

  1. Run ./bin/verbump "5.1.3" (substituting the correct version number) to update the version number, assign it a tag
  2. Run ./bin/npm-publish.sh to publish the release to npm
  3. Verify that the new release of TiddlyWiki is available at https://www.npmjs.org/package/tiddlywiki

Update tiddlywiki.com release

  1. Update tiddlywiki-com from master and push to GitHub

Cleaning Up

  1. Tweet the release with the text "TiddlyWiki v5.x.x released to https://tiddlywiki.com #newtiddlywikirelease"

Preparation for the next release in master

  1. Adjust version number in package.json
  2. Adjust version number in bin/build-site.sh
  3. Adjust version number in $:/config/OfficialPluginLibrary (both in editions/tw5.com and editions/prerelease/tiddlers/system) and $:/config/LocalPluginLibrary
  4. Adjust new release banner
  5. Create the release note for the new release
  6. Commit changes to master and push to GitHub

Releasing new content for TiddlyWiki

To update https://tiddlywiki.com with new content, make a Pull Request with the updated tiddlers to the tiddlywiki-com branch. As soon as the PR is merged, the Continuous Deployment system will automatically rebuild the site.

Note that the PR should only include updates within the editions folder of the repo.

Section Overview

10th July 2014 at 8:45am

TiddlyWiki - A quick Overview
This section describes the idea of the TiddlyWiki application. This should give the reader a overview over what TiddlyWiki consists of give a brief introduction to the topics of the main documentation.

Microkernel Architecture
The heart of TiddlyWiki is it's microkernel. This section will describe what bare mechanisms are included in the microkernel and how additional modules are loaded.

TiddlyWiki Core Application
After the microkernel has been explained this section focuses on the core plug-in. It describes the central modules of the core and explains how they work together to build a single file application.

Conclusion
A briefly summary about this documentation bringing out the advantages, disadvantages and experiences with TiddlyWiki.

Selective Update

17th July 2014 at 6:18pm

With Messages a widget is able to put some kind of events into the TiddlyWiki application which is one part of the dynamic behaviour of widgets. The other part is selective updating. Widgets are often dependant on tiddler states. The ListWidget for example can be configured to list all tiddlers which are tagged with "important". Now, when such a tiddler is changed and it's "important" tag is removed, this change should reflect in the ListWidget. To allow widgets to react on such changes, each widget can provide a function refresh(changedTiddlers). The RootWidget is registered to the wiki store, using the Event Mechanism. When an change event occurs it starts to call the refresh function of its children with a list of the changed tiddlers. Each widget can then decide if it has to change or re-render its DOM representation and call the refresh function of its own children. This way every time a tiddler or the wiki store itself changes, each widget can instantly react on these changes or ignore them.

Another way of updating are text reference attributes (text references explained in Transclusion and TextReference):

{type: "indirect", textReference: <textReference>}

When a widget got a attribute which is a text reference and the refresh function is called, it can check if the text reference references a changed tiddler and update accordingly.

Nearly every state of the UI is stored in tiddlers. A search mechanism for example would use a EditTextWidget which shows an text input field for the search string and a ListWidget to show the results. The EditTextWidget is bound to a tiddler $:/temp/search. Meaning when editing the text in the input field the tiddlers content is changed to this text. On the other hand when the tiddler changes the RootWidget is notified. It then starts calling its childrens refresh methods. Eventually the refresh method of the EditTextWidget is called and it can re-render itself if necessary. This way TiddlyWiki can re-use an already existing data structure which is not only convenient because we don't need to introduce an additional structure but tiddlers managed in the wiki store are already a pretty powerful data structure, supporting an Event Mechanism (so we don't need an additional observer pattern), Caching, Metadata by additional tiddler fields and a way to obtain specific tiddlers.

Server Architecture.svg

14th June 2014 at 12:08pm

Startup Modules.svg

Startup Process

17th July 2014 at 6:05pm

Modules with module-type: startup have to export a function named startup and may export a name property to identify this module. The startup function will be executed by the boot kernel at the end of the boot process.

// From boot.js:
// Gather up any startup modules
$tw.boot.remainingStartupModules = []; // Array of startup modules
$tw.modules.forEachModuleOfType("startup",function(title,module) {
	if(module.startup) {
		$tw.boot.remainingStartupModules.push(module);
	}
});
// Keep track of the startup tasks that have been executed
$tw.boot.executedStartupModules = Object.create(null);
$tw.boot.disabledStartupModules = $tw.boot.disabledStartupModules || [];
// Repeatedly execute the next eligible task
$tw.boot.executeNextStartupTask();

executeNextStartupTask() will execute the remaining startup modules in remainingStartupModules. A startup module can export the variables before and/or after, each containing an array of names of other startup modules. executeNextStartupTask() will use this information to execute the modules in the correct order.

Startup modules can be marked as synchronous by exporting synchronous = true. If synchronous is set to false, the startup function is executed with an callback function as the first argument. The startup function has to call this function to allow subsequent startup modules to get executed. This is necessary when the startup function itself uses asynchronous calls.

StartupMechanism

2nd May 2014 at 2:29pm

The startup mechanism runs the installed startup modules at the end of the boot process.

Startup Modules

Modules with their module-type field set to startup:

  • Must export a startup function
    • For synchronous startup modules the startup function is called as startup(), asynchronous modules are passed a callback they must invoke on completion: startup(callback)
  • May export a name property that is used to identify the task
  • May export a after property containing an array of names of dependent tasks that must be run before this one
  • May export a before property containing an array of names of tasks that must be run after this one
  • May export a platforms property containing an array of names of platforms that are required in order for the startup module to be executed. The defined platforms are node and browser. If the platforms property is not provided it defaults to ["node","browser"]

Startup Processing

Startup modules are executed in sequence according to their declared dependencies.

There is no guarantee of the execution order of tasks that share the same dependencies.

Startup Modules

The core defines the following startup modules:

StartupTimeline.png

10th July 2014 at 8:34am

Syncadaptor

20th July 2021 at 7:32pm

SyncAdaptorModules

25th October 2020 at 4:24pm

Introduction

SyncAdaptorModules encapsulate storage mechanisms that can be used by the SyncMechanism. Two examples are:

SyncAdaptorModules are represented as JavaScript tiddlers with the field module-type set to syncadaptor.

See this pull request for background on the evolution of this API.

Exports

The following properties should be exposed via the exports object:

PropertyDescription
adaptorClassThe JavaScript class for the adaptor

Nothing should be exported if the adaptor detects that it isn't capable of operating successfully (eg, because it only runs on either the browser or the server, or because a dependency is missing).

Adaptor Module Methods

Adaptor modules must handle the methods described below.

Error Handling

The syncadaptor must invoke the provided callback with the err parameter containing a string describing the error.

The syncer has special handling for connection errors. For backwards compatibilty reasons, the syncer identifies connection errors as the string comprised of the content of the tiddler $:/language/Error/XMLHttpRequest with the string ": 0" appended to the end. For example, in English, the string is "XMLHttpRequest error code: 0" and in Brazilian Portuguese it is "Código de erro XMLHttpRequest: 0".

Constructor(options)

Initialises a new adaptor instance.

ParameterDescription
optionsSee below

Options include:

  • options.wiki: reference to wiki to use with this syncadaptor

getTiddlerInfo(tiddler)

Gets the supplemental information that the adaptor needs to keep track of for a particular tiddler. For example, the TiddlyWeb adaptor includes a bag field indicating the original bag of the tiddler.

ParameterDescription
tiddlerTarget tiddler

Returns an object storing any additional information required by the adaptor.

getTiddlerRevision(title)

Gets the revision ID associated with the specified tiddler title.

ParameterDescription
titleTiddler title

Returns a revision ID.

getStatus(callback)

Retrieves status information from the server. This method is optional.

ParameterDescription
callbackCallback function invoked with parameters err,isLoggedIn,username,isReadOnly

login(username,password,callback)

Attempts to login to the server with specified credentials. This method is optional.

ParameterDescription
usernameUsername
passwordPassword
callbackCallback function invoked with parameter err

displayLoginPrompt(syncer)

Invoked by the syncer to display a custom login prompt. This method is optional.

ParameterDescription
syncerReference to the syncer object making the call

The custom login prompt should send the widget message tm-login with the username and password in parameters username and password.

logout(callback)

Attempts to logout of the server. This method is optional.

ParameterDescription
callbackCallback function invoked with parameter err

getUpdatedTiddlers(syncer,callback)

Retrieves the titles of tiddlers that need to be updated from the server.

This method is optional. If an adaptor doesn't implement it then synchronisation will be unidirectional from the TiddlyWiki store to the adaptor, but not the other way.

The syncer will use the getUpdatedTiddlers() method in preference to the getSkinnyTiddlers() method.

ParameterDescription
syncerReference to the syncer object making the call
callbackCallback function invoked with parameter err,data – see below

The data provided by the callback is as follows:

{
modifications: [<array of title>],
deletions: [<array of title>],
}

getSkinnyTiddlers(callback)

Retrieves a list of skinny tiddlers from the server.

This method is optional. If an adaptor doesn't implement it then synchronisation will be unidirectional from the TiddlyWiki store to the adaptor, but not the other way.

The syncer will use the getUpdatedTiddlers() method in preference to the getSkinnyTiddlers() method.

ParameterDescription
callbackCallback function invoked with parameter err,tiddlers, where tiddlers is an array of tiddler field objects

saveTiddler(tiddler,callback)

Saves a tiddler to the server.

ParameterDescription
tiddlerTiddler to be saved
callbackCallback function invoked with parameter err,adaptorInfo,revision
tiddlerInfoThe tiddlerInfo maintained by the syncer for this tiddler

loadTiddler(title,callback)

Loads a tiddler from the server.

ParameterDescription
titleTitle of tiddler to be retrieved
callbackCallback function invoked with parameter err,tiddlerFields

deleteTiddler(title,callback,options)

Delete a tiddler from the server.

ParameterDescription
titleTitle of tiddler to be deleted
callbackCallback function invoked with parameter err
optionsSee below

The options parameter contains the following properties:

PropertyDescription
tiddlerInfoThe tiddlerInfo maintained by the syncer for this tiddler

System Tiddlers

15th July 2014 at 8:48am

The core plug-in introduces a segregation of tiddlers. The tiddler model is central in the whole TiddlyWiki application and can be used in various roles. Because of the fact that a TiddlyWiki user works with tiddlers (taking the role of a wiki page) and tiddlers are the building blocks of the whole application (including modules, UI elements, etc.) we need to identify the internal tiddlers.

The core plug-in introduces the concept of system tiddlers. It builds on the convention that application internal tiddler names start with $:/. Then the core plug-in introduces a set of new functions to the wiki store which are used to retrieve tiddlers like getTiddlers(options) and forEachTiddler(callback, options). These functions work with all tiddlers in the store but the options parameter provides the ability to sort tiddlers by a field-name and exclude tiddlers with a specific tag. By default it doesn't return system tiddlers. To get a list of all tiddlers including system tiddlers, this must be requested explicitly via the options. If a function wants to present a list of tiddlers to the user it can use this new functions so that internal application tiddlers wouldn't clutter the resulting list. These functions are added via the wikimethod module type.

Table of Contents

14th July 2014 at 5:18pm
  1. Introduction
  2. Decomposition of the TiddlyWiki-Architecture
  3. Bootstrap-Process
  4. The Plugin and Module concept
  5. Developing a own Plugin
  6. The TiddlyWiki data management concept
  7. Conclusion

Tags and Filter Mechanism

17th July 2014 at 6:21pm

Tags

The core plug-in extends the store by a simple mechanism to tag a tiddler. This provides the functionality to

  • retrieve tiddlers with a specific tag
  • retreive a hashmap of { tag: [tiddler1, tiddler3, tiddler3] }
  • list or iterate tiddlers not tagged with a specific tag

The tags are stored directly in the tiddler. Each tiddler can have a field named "tag", which contains an array of its tags. The above functions use this field to calculate their results and cache them in a global cache.

Organising information with tags is easy, intuitive and is common throughout the web. In most cases the functions mentioned above are not enough and a more sophisticated way of querying is needed. But instead of focusing only on tags TiddlyWiki introduces a querying system that isn't bound to tags or single tiddlers.

Filter mechanism

This filter mechanism is build on the idea of a pipeline of single filter operators. Filters are noted as strings and can look like

[tag[task]!tag[done]interesting[very]]

This example would (implicitly) put all available tiddlers into the pipe. The first operator tag[task] would only pass tiddlers which are tagged with "task". The !tag[done] operator is negated and only passes tiddlers which are not tagged with "done". The last filter operator passes only tiddlers with a field "interesting" set to "very". So as a result this filter would be used to obtain all tiddlers which are marked as task, aren't already done and are very interesting.

There are many filter operators already build into the core plug-in including the mentioned tag- and field operators, filter operators to sort the tiddlerlist by a specified field, etc. But more sophisticated operators are possible, too. An example would be the search-operator. This filter operator looks for the searched string in the text and in the tags of the tiddlers. If the provided filter operators are not enough, a developer can add new filters by adding a module with the filteroperator type.

System Tags

Tags and the filter mechanism are used throughout the core plug-in for internal puproses. Tags which start with $:/ are normally hidden from the casual user, similar to System Tiddlers

The filter mechanism is added to the wiki store with the wikimethod module type.

TestingMechanism

13th October 2014 at 8:56am

TiddlyWiki5 incorporates the Jasmine JavaScript testing framework (see https://jasmine.github.io/). It allows the same tests to be run both in the browser and under Node.js.

TiddlyWiki5 Testing Components

There are two main elements to the TiddlyWiki5 testing mechanism:

  • The plugin tiddlywiki/jasmine that wraps Jasmine up into a plugin along with some glue code
  • The TiddlyWiki5 edition test that contains the core test specifications and includes the Jasmine plugin

Running the Tests in Node.js

To run the tests under Node.js just load up the test wiki:

node ./tiddlywiki.js \
	./editions/test \

Running the Tests in the Browser

To generate a wiki containing the browser tests load up the test wiki and save it as an HTML file:

node ./tiddlywiki.js \
	./editions/test \
	--verbose \
	--rendertiddler $:/core/save/all test.html text/plain \

The test.html file will be placed in the output folder within the wiki folder. Open it in the browser to see the test results. There is a prebuilt version of test.html at:

https://tiddlywiki.com/test.html

The Heart of TiddlyWiki (Boot-Kernel)

8th July 2014 at 9:02am

The boot-kernel is responsible for creating a bare-bones TW environment. It is running under Node.js or in a HTML5 Browser. The Bootkernel just loads enough functionality to load the modules containing the main logic of the application. This boot-kernel contains a few helper methods, the module mechanism as well as the function to create a tiddler and manage them. The boot-kernel also creates the bare-bones wiki store, which holds all the information of the wiki during the runtime. After creating the store, the boot-kernel is in charge of decrypting the encrypted tiddlers and extracting all the tiddlers e.g. the core module tiddlers embedded in the DOM structure of the HTML file. Furthermore the boot kernel offers the functionality to load tiddlers from a file, when you run TW with Node.js. All other functionality which is not a part of the boot kernel is added dynamically by modules and plugins. The boot kernel is able to load the core plugins and perform the startup plugins. The core contains the startup modules shown in the picture below.

The Plugin and Module concept

17th July 2014 at 8:34pm

The TiddlyWiki data management concept

17th July 2014 at 8:34pm

This section descripes how the data of the wiki is stored within Tiddlywiki during the runtime. And how the complete wiki is persisted.

The User Interface

15th July 2014 at 8:34am

Following the "anything is a tiddler" concept, even the UI consists of tiddlers. This is possible because tiddlers can not only contain plain text or JavaScript (modules) but they also can contain a special markup text called WikiText. By using WikiText the user can put markup elements like tables or images in a tiddler. To provide some more sophisticated UI elements, WikiText can also contain special widgets like text input fields, checkboxes, dynamic lists etc. In most cases, these widgets are used to directly modify or represent the information contained in other tiddlers. If a tiddler is changed and this change should reflect in an UI element e.g. a widget, a process called selective update takes place. Selective updating means when a tiddler or a set of tiddlers changes, each widget is asked, if changes to this tiddlers would affect its appearance. If so, the respective widget is re-rendered otherwise it remains unchanged.

Tiddler as Basic Element

15th July 2014 at 9:47am

By managing nearly every part of the application as tiddlers, the application is only needed to provide some basic functionality to manage the individual tiddlers, load and persist them, render them to HTML output and provide a way to register for the changes made to tiddlers. This way the whole wiki application can be build from these simple concepts. Plug-ins can be used to add new functionality to the existing modules or even to replace individual tiddlers/modules, enabling developers to build whole new applications on the TiddlyWiki base system.

A tiddler is the smallest unit of the TiddlyWiki system. It can contain any data like plain text, WikiText markup, JavaScript code (module tiddler), JSON structures (JSON structures might even contain additional tiddlers. Plug-ins are implemented this way to pack multiple tiddlers in a single plug-in tiddler), images in SVG format or even binary images encoded with base64. Internally Tiddlers are immutable objects containing a bunch of key:value pairs called fields. The only required field of a tiddler is the title field. The Standard fields of a tiddler are listed below. Nearly everything in TiddlyWiki is loaded as tiddlers. Plug-ins for example are a bunch of tiddlers that are distributed as a single JSON tiddler. The only exception is the microkernel which isn't a tiddler.

created
Timestamp number of milliseconds since 01.01.1970.
modified
Timestamp number of milliseconds since 01.01.1970.
tags
list of tags seperated by whitespace. Tags which contain whitespaces are wrapped by , e.g. example Tag.
type
Type of the Tiddler, e.g. text/plain or text/vnd.tiddlywiki .
title
Title of the Tiddler
list
An ordered list of tiddler titles associated with a tiddler

Tiddlers are used in multiple roles and on different levels. A developer uses tiddlers as the basic element containing application code, configuration values and even as a form of variable to save the current UI state. On a different level, a tiddler is also the basic unit of work for the wiki user, e.g. the individual wiki pages are implemented as tiddlers. This makes sense for multiple reasons: Because the UI of TiddlyWiki is build from tiddlers, the wiki user is able to edit the interface of his own TiddlyWiki just by editing a wiki page. For example to add a list of tiddler links to the sidebar, the user just needs to create a new tiddler, put the links into this tiddler and tag this tiddler with $:/tags/SideBar. This way the user can customise his work environment just by using mechanisms he already uses to manage his wiki pages. Tiddlers consist of fields. When using a tiddler as wiki page, the user can use these fields to store meta information, like tags.

Because fields for metadata and especially tags are an easy way for the user to organise his wiki pages, TiddlyWiki provides a special filter mechanism to choose tiddlers using their metadata. A filter string like [tag[learncard]topic[math]!tag[successful]] would filter all tiddlers tagged with "learncard", with the value "math" in the topic-field and are not tagged with "successful". A user could use this filter together with the <$list> widget to display a list of all math learncards which are not yet answered successfully in a wiki page.

Another example which shows how the "anything is a tiddler" concept leads to an environment where a single feature brings great benefit is the drag and drop feature. HTML5 standard comes with a native drag and drop feature. TiddlyWiki uses this feature and makes it possible to drag and drop a tiddler from one instance to another. And because anything is a tiddler, this brings the ability to drag and drop individual wiki pages, JavaScript modules, UI components and whole plug-ins between TiddlyWiki instances.

tiddlerfield-norefresh.js

2nd February 2019 at 3:06am
/*\

Hello, World widget

\*/
(function() {

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

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

var MyWidget = function(parseTreeNode, options) {
	this.initialise(parseTreeNode, options);
};

/*
Inherit from the base widget class
*/
MyWidget.prototype = new Widget();

/*
Render this widget into the DOM
*/
MyWidget.prototype.render = function(parent, nextSibling) {
	this.parentDomNode = parent;
	var text = this.wiki.getTiddlerText("test", "<empty>")
	var textNode = this.document.createTextNode(text);
	parent.insertBefore(textNode, nextSibling);
	this.domNodes.push(textNode);
};

exports.tiddlerfield = MyWidget;

})();

tiddlerfield.js

2nd February 2019 at 3:27am
/*\

Hello, World widget

\*/
(function() {

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

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

var MyWidget = function(parseTreeNode, options) {
	this.initialise(parseTreeNode, options);
};

/*
Inherit from the base widget class
*/
MyWidget.prototype = new Widget();

/*
Render this widget into the DOM
*/
MyWidget.prototype.render = function(parent, nextSibling) {
	this.parentDomNode = parent;
	var text = this.wiki.getTiddlerText("test", "<empty>")
	var textNode = this.document.createTextNode(text);
	parent.insertBefore(textNode, nextSibling);
	this.domNodes.push(textNode);
};

/*
A widget with optimized performance will selectively refresh, but here we refresh always
*/
MyWidget.prototype.refresh = function(changedTiddlers) {
	// Regenerate and rerender the widget and
	// replace the existing DOM node
	this.refreshSelf();
	return true;
};

exports.tiddlerfield = MyWidget;

})();

TiddlyWiki

15th July 2014 at 9:51am

Traditional web applications are bound to ~HTTP-Concepts, including stateless requests to transfer data. In these applications a state is often emulated by the use of sessions, which need to be handled on the client and especially on the server side. These restrictions often lead to a fragmented user experience because the user interface is rebuilt on every data transfer. TiddlyWiki tries to overcome these restrictions and the resulting disadvantages by building on few but basic concepts which loosen the coupling of HTTP and the actual application, eliminating the need of state emulation and resulting in a wiki style single page application with the ability to run in an offline environment.

TiddlyWiki as Single Page Application

TiddlyWiki builds on some basic concepts. First, TiddlyWiki should not be perceived as a dynamic web page like traditional server-side generated web pages. Instead TiddlyWiki can be perceived as an application which is written entirely in JavaScript and uses HTML5 and CSS3 to render a GUI. This way TiddlyWiki can be executed in any JavaScript environment like a browser or a node.js instance, while keeping the advantages of a simple application. One of these advantages is, that TiddlyWiki has no need to emulate an application state, as a traditional web application would need to do.

A second idea concerns the storage of the application data. In contrast to a traditional web application, TiddlyWiki doesn't store the data in an external database but simply uses native data structures already existing in JavaScript to store tiddlers, the basic (atomic) element of the TiddlyWiki application, in the memory. Additional core modules provide a way to persist this storage in simple HTML div elements.

Just by building on these simple and basic concepts,

  • TiddlyWiki is able to store application data in a single HTML page by using div elements as data container.
  • TW is able to store application code (JavaScript) in the same single HTML page.
  • TiddlyWiki can be executed in any JavaScript environment like a browser.

These points already enable TW to be used as an offline-enabled single file web application. Also, by using a server side node.js environment running the same TiddlyWiki application, TiddlyWiki can be used as an online web application. This is realized on server-side by providing an additional module to persist tiddlers into plain text files and on client-side by a module syncing the local data store with the node.js server.

Microkernel

In the universe of TiddlyWiki everything is a tiddler. Even the application logic is stored in tiddlers that are marked as "application/javascript". These tiddlers, which contain application logic, are called modules and a CommonJS compatible module system is responsible for assembling the individual modules into the TiddlyWiki application. The result is a tree representing the whole TiddlyWiki application containing module tiddlers, data tiddlers and some JavaScript functions and objects.

Only a small part of the TiddlyWiki is not managed as tiddlers, the microkernel. The microkernel is the first thing to run, when the application is started and it puts some initial objects and functions into the application tree, which are needed to load and manage tiddlers. After the microkernel built this initial application tree, the remaining parts of the application can be loaded as module tiddlers. Beside some utility functions the most important object that is contributed by the boot kernel is "$tw.wiki", consisting of JavaScript structures and functions that are used to store and manage the loaded tiddlers. Among other things this store can be used to add new tiddlers, remove tiddlers and retrieve tiddlers by name.

The microkernel constructs a initial $tw object containing the needed structures and functions.

Data Persistence

The next important part of the application are deserializers. Deserializers are responsible to load tiddlers from various sources. One of the deserializers provided by the microkernel for example can load new tiddlers from the current DOM tree. The counterpart of deserializers are saver. A saver is used to save the complete store or the complete TiddlyWiki for that matter at once. After a user made changes to some tiddlers, this method can be used to download the current state of the store as a completely new TiddlyWiki application.

Another way of persisting data are syncadaptors. Like deserializer and saver, a syncadaptor can load tiddlers into the store and can persist changed tiddlers. But in contrast to deserializer and saver it can load and persist single tiddlers. A syncadaptor can provide a list of loadable tiddlers registers at the store for changes. Now when a tiddler is changed and these changes are written to the store, the syncadaptor is triggered and persists the new changes, for each tiddler individually.

Tiddlers can be persisted from/to harddisk or synced with a remote server.

The User Interface

Following the "anything is a tiddler" concept, even the UI consists of tiddlers. This is possible because tiddlers can not only contain plain text or JavaScript (modules) but they also can contain a special markup text called WikiText. By using WikiText the user can put markup elements like tables or images in a tiddler. To provide some more sophisticated UI elements, WikiText can also contain special widgets like text input fields, checkboxes, dynamic lists etc. In most cases, these widgets are used to directly modify or represent the information contained in other tiddlers. If a tiddler is changed and this change should reflect in an UI element e.g. a widget, a process called selective update takes place. Selective updating means when a tiddler or a set of tiddlers changes, each widget is asked, if changes to this tiddlers would affect its appearance. If so, the respective widget is re-rendered otherwise it remains unchanged.

Modularization

The whole application is basically built from three parts. At first, the microkernel provides the basic functionality to handle tiddlers. The second part are tiddlers representing core functionality. These are for example modules which extend the store by more sophisticated functions, UI tiddlers and widget modules, a WikiText parser, sophisticated deserializers, savers, syncadapters, etc. These core modules are provided as plug-in to the microkernel. Consequently, a plug-in is a single tiddler which itself contains multiple tiddlers, forming the plug-in. Each of this tiddler might be a module providing new functionality (i.e. a module tiddler marked with "module-type: saver" can extend the application with new methods of saving the current wiki state.). Tiddlers provided in plug-ins are called shadow tiddlers. They are immutable and can not be edited or deleted but we can create a new tiddler with the same name to override a shadow tiddler.

Tiddler as Basic Element

By managing nearly every part of the application as tiddlers, the application is only needed to provide some basic functionality to manage the individual tiddlers, load and persist them, render them to HTML output and provide a way to register for the changes made to tiddlers. This way the whole wiki application can be build from these simple concepts. Plug-ins can be used to add new functionality to the existing modules or even to replace individual tiddlers/modules, enabling developers to build whole new applications on the TiddlyWiki base system.

A tiddler is the smallest unit of the TiddlyWiki system. It can contain any data like plain text, WikiText markup, JavaScript code (module tiddler), JSON structures (JSON structures might even contain additional tiddlers. Plug-ins are implemented this way to pack multiple tiddlers in a single plug-in tiddler), images in SVG format or even binary images encoded with base64. Internally Tiddlers are immutable objects containing a bunch of key:value pairs called fields. The only required field of a tiddler is the title field. The Standard fields of a tiddler are listed below. Nearly everything in TiddlyWiki is loaded as tiddlers. Plug-ins for example are a bunch of tiddlers that are distributed as a single JSON tiddler. The only exception is the microkernel which isn't a tiddler.

created
Timestamp number of milliseconds since 01.01.1970.
modified
Timestamp number of milliseconds since 01.01.1970.
tags
list of tags seperated by whitespace. Tags which contain whitespaces are wrapped by , e.g. example Tag.
type
Type of the Tiddler, e.g. text/plain or text/vnd.tiddlywiki .
title
Title of the Tiddler
list
An ordered list of tiddler titles associated with a tiddler

Tiddlers are used in multiple roles and on different levels. A developer uses tiddlers as the basic element containing application code, configuration values and even as a form of variable to save the current UI state. On a different level, a tiddler is also the basic unit of work for the wiki user, e.g. the individual wiki pages are implemented as tiddlers. This makes sense for multiple reasons: Because the UI of TiddlyWiki is build from tiddlers, the wiki user is able to edit the interface of his own TiddlyWiki just by editing a wiki page. For example to add a list of tiddler links to the sidebar, the user just needs to create a new tiddler, put the links into this tiddler and tag this tiddler with $:/tags/SideBar. This way the user can customise his work environment just by using mechanisms he already uses to manage his wiki pages. Tiddlers consist of fields. When using a tiddler as wiki page, the user can use these fields to store meta information, like tags.

Because fields for metadata and especially tags are an easy way for the user to organise his wiki pages, TiddlyWiki provides a special filter mechanism to choose tiddlers using their metadata. A filter string like [tag[learncard]topic[math]!tag[successful]] would filter all tiddlers tagged with "learncard", with the value "math" in the topic-field and are not tagged with "successful". A user could use this filter together with the <$list> widget to display a list of all math learncards which are not yet answered successfully in a wiki page.

Another example which shows how the "anything is a tiddler" concept leads to an environment where a single feature brings great benefit is the drag and drop feature. HTML5 standard comes with a native drag and drop feature. TiddlyWiki uses this feature and makes it possible to drag and drop a tiddler from one instance to another. And because anything is a tiddler, this brings the ability to drag and drop individual wiki pages, JavaScript modules, UI components and whole plug-ins between TiddlyWiki instances.

WikiText Markup

The WikiText is a markup language, created especially for the requirements of the TiddlyWiki application. It is based on Markdown, but extended with some TiddlyWiki specific features. On one hand its a text-to-HTML conversion language and on the other hand its used to provide the interactive features of TiddlyWiki. The aim of this language is to allow the user of the software to focus on the writing. The WikiText is used to format Tiddlers within the TiddlyWiki application. The tags of the WikiText syntax can be used within the standard text input field. During the saving process these tags renders to HTML elements for example:

WikiText:--- 
Renders as:
HTML:<hr>
WikiText:[img[https://tiddlywiki.com/favicon.ico]]
Renders as: TW
HTML:<img src="https://tiddlywiki.com/favicon.ico">

Furthermore the WikiText is used to access the widgets which are integrated in the application.These widgets are used to enhance the the WikiText with a rich functionality. Widgets are based on the ~HTML-Syntax but always starts with a $.

WikiText:
<$button message="tm-close-tiddler">Close Me!</$button> 

TiddlyWiki - A quick Overview

17th July 2014 at 9:14pm

Traditional web applications are bound to ~HTTP-Concepts, including stateless requests to transfer data. In these applications a state is often emulated by the use of sessions, which need to be handled on the client and especially on the server side. These restrictions often lead to a fragmented user experience because the user interface is rebuilt on every data transfer. TiddlyWiki tries to overcome these restrictions and the resulting disadvantages by building on few but basic concepts which loosen the coupling of HTTP and the actual application, eliminating the need of state emulation and resulting in a wiki style single page application with the ability to run in an offline environment.

TiddlyWiki as Single Page Application

TiddlyWiki builds on some basic concepts. First, TiddlyWiki should not be perceived as a dynamic web page like traditional server-side generated web pages. Instead TiddlyWiki can be perceived as an application which is written entirely in JavaScript and uses HTML5 and CSS3 to render a GUI. This way TiddlyWiki can be executed in any JavaScript environment like a browser or a node.js instance, while keeping the advantages of a simple application. One of these advantages is, that TiddlyWiki has no need to emulate an application state, as a traditional web application would need to do.

A second idea concerns the storage of the application data. In contrast to a traditional web application, TiddlyWiki doesn't store the data in an external database but simply uses native data structures already existing in JavaScript to store tiddlers, the basic (atomic) element of the TiddlyWiki application, in the memory. Additional core modules provide a way to persist this storage in simple HTML div elements.

Just by building on these simple and basic concepts,

  • TiddlyWiki is able to store application data in a single HTML page by using div elements as data container.
  • TW is able to store application code (JavaScript) in the same single HTML page.
  • TiddlyWiki can be executed in any JavaScript environment like a browser.

These points already enable TW to be used as an offline-enabled single file web application. Also, by using a server side node.js environment running the same TiddlyWiki application, TiddlyWiki can be used as an online web application. This is realized on server-side by providing an additional module to persist tiddlers into plain text files and on client-side by a module syncing the local data store with the node.js server.

Microkernel

In the universe of TiddlyWiki everything is a tiddler. Even the application logic is stored in tiddlers that are marked as "application/javascript". These tiddlers, which contain application logic, are called modules and a CommonJS compatible module system is responsible for assembling the individual modules into the TiddlyWiki application. The result is a tree representing the whole TiddlyWiki application containing module tiddlers, data tiddlers and some JavaScript functions and objects.

Only a small part of the TiddlyWiki is not managed as tiddlers, the microkernel. The microkernel is the first thing to run, when the application is started and it puts some initial objects and functions into the application tree, which are needed to load and manage tiddlers. After the microkernel built this initial application tree, the remaining parts of the application can be loaded as module tiddlers. Beside some utility functions the most important object that is contributed by the boot kernel is "$tw.wiki", consisting of JavaScript structures and functions that are used to store and manage the loaded tiddlers. Among other things this store can be used to add new tiddlers, remove tiddlers and retrieve tiddlers by name.

The microkernel constructs a initial $tw object containing the needed structures and functions.

Data Persistence

The next important part of the application are deserializers. Deserializers are responsible to load tiddlers from various sources. One of the deserializers provided by the microkernel for example can load new tiddlers from the current DOM tree. The counterpart of deserializers are saver. A saver is used to save the complete store or the complete TiddlyWiki for that matter at once. After a user made changes to some tiddlers, this method can be used to download the current state of the store as a completely new TiddlyWiki application.

Another way of persisting data are syncadaptors. Like deserializer and saver, a syncadaptor can load tiddlers into the store and can persist changed tiddlers. But in contrast to deserializer and saver it can load and persist single tiddlers. A syncadaptor can provide a list of loadable tiddlers registers at the store for changes. Now when a tiddler is changed and these changes are written to the store, the syncadaptor is triggered and persists the new changes, for each tiddler individually.

Tiddlers can be persisted from/to harddisk or synced with a remote server.

The User Interface

Following the "anything is a tiddler" concept, even the UI consists of tiddlers. This is possible because tiddlers can not only contain plain text or JavaScript (modules) but they also can contain a special markup text called WikiText. By using WikiText the user can put markup elements like tables or images in a tiddler. To provide some more sophisticated UI elements, WikiText can also contain special widgets like text input fields, checkboxes, dynamic lists etc. In most cases, these widgets are used to directly modify or represent the information contained in other tiddlers. If a tiddler is changed and this change should reflect in an UI element e.g. a widget, a process called selective update takes place. Selective updating means when a tiddler or a set of tiddlers changes, each widget is asked, if changes to this tiddlers would affect its appearance. If so, the respective widget is re-rendered otherwise it remains unchanged.

Modularization

The whole application is basically built from three parts. At first, the microkernel provides the basic functionality to handle tiddlers. The second part are tiddlers representing core functionality. These are for example modules which extend the store by more sophisticated functions, UI tiddlers and widget modules, a WikiText parser, sophisticated deserializers, savers, syncadapters, etc. These core modules are provided as plug-in to the microkernel. Consequently, a plug-in is a single tiddler which itself contains multiple tiddlers, forming the plug-in. Each of this tiddler might be a module providing new functionality (i.e. a module tiddler marked with "module-type: saver" can extend the application with new methods of saving the current wiki state.). Tiddlers provided in plug-ins are called shadow tiddlers. They are immutable and can not be edited or deleted but we can create a new tiddler with the same name to override a shadow tiddler.

Tiddler as Basic Element

By managing nearly every part of the application as tiddlers, the application is only needed to provide some basic functionality to manage the individual tiddlers, load and persist them, render them to HTML output and provide a way to register for the changes made to tiddlers. This way the whole wiki application can be build from these simple concepts. Plug-ins can be used to add new functionality to the existing modules or even to replace individual tiddlers/modules, enabling developers to build whole new applications on the TiddlyWiki base system.

A tiddler is the smallest unit of the TiddlyWiki system. It can contain any data like plain text, WikiText markup, JavaScript code (module tiddler), JSON structures (JSON structures might even contain additional tiddlers. Plug-ins are implemented this way to pack multiple tiddlers in a single plug-in tiddler), images in SVG format or even binary images encoded with base64. Internally Tiddlers are immutable objects containing a bunch of key:value pairs called fields. The only required field of a tiddler is the title field. The Standard fields of a tiddler are listed below. Nearly everything in TiddlyWiki is loaded as tiddlers. Plug-ins for example are a bunch of tiddlers that are distributed as a single JSON tiddler. The only exception is the microkernel which isn't a tiddler.

created
Timestamp number of milliseconds since 01.01.1970.
modified
Timestamp number of milliseconds since 01.01.1970.
tags
list of tags seperated by whitespace. Tags which contain whitespaces are wrapped by , e.g. example Tag.
type
Type of the Tiddler, e.g. text/plain or text/vnd.tiddlywiki .
title
Title of the Tiddler
list
An ordered list of tiddler titles associated with a tiddler

Tiddlers are used in multiple roles and on different levels. A developer uses tiddlers as the basic element containing application code, configuration values and even as a form of variable to save the current UI state. On a different level, a tiddler is also the basic unit of work for the wiki user, e.g. the individual wiki pages are implemented as tiddlers. This makes sense for multiple reasons: Because the UI of TiddlyWiki is build from tiddlers, the wiki user is able to edit the interface of his own TiddlyWiki just by editing a wiki page. For example to add a list of tiddler links to the sidebar, the user just needs to create a new tiddler, put the links into this tiddler and tag this tiddler with $:/tags/SideBar. This way the user can customise his work environment just by using mechanisms he already uses to manage his wiki pages. Tiddlers consist of fields. When using a tiddler as wiki page, the user can use these fields to store meta information, like tags.

Because fields for metadata and especially tags are an easy way for the user to organise his wiki pages, TiddlyWiki provides a special filter mechanism to choose tiddlers using their metadata. A filter string like [tag[learncard]topic[math]!tag[successful]] would filter all tiddlers tagged with "learncard", with the value "math" in the topic-field and are not tagged with "successful". A user could use this filter together with the <$list> widget to display a list of all math learncards which are not yet answered successfully in a wiki page.

Another example which shows how the "anything is a tiddler" concept leads to an environment where a single feature brings great benefit is the drag and drop feature. HTML5 standard comes with a native drag and drop feature. TiddlyWiki uses this feature and makes it possible to drag and drop a tiddler from one instance to another. And because anything is a tiddler, this brings the ability to drag and drop individual wiki pages, JavaScript modules, UI components and whole plug-ins between TiddlyWiki instances.

WikiText Markup

The WikiText is a markup language, created especially for the requirements of the TiddlyWiki application. It is based on Markdown, but extended with some TiddlyWiki specific features. On one hand its a text-to-HTML conversion language and on the other hand its used to provide the interactive features of TiddlyWiki. The aim of this language is to allow the user of the software to focus on the writing. The WikiText is used to format Tiddlers within the TiddlyWiki application. The tags of the WikiText syntax can be used within the standard text input field. During the saving process these tags renders to HTML elements for example:

WikiText:--- 
Renders as:
HTML:<hr>
WikiText:[img[https://tiddlywiki.com/favicon.ico]]
Renders as: TW
HTML:<img src="https://tiddlywiki.com/favicon.ico">

Furthermore the WikiText is used to access the widgets which are integrated in the application.These widgets are used to enhance the the WikiText with a rich functionality. Widgets are based on the ~HTML-Syntax but always starts with a $.

WikiText:
<$button message="tm-close-tiddler">Close Me!</$button> 

TiddlyWiki Architecture

14th June 2014 at 12:08pm

The heart of TiddlyWiki is an extensible representation transformation engine for text and images. Given the text of a tiddler and its associated ContentType, the engine can produce a rendering of the tiddler in a new ContentType. Furthermore, it can efficiently selectively update the rendering to track any changes in the tiddler or its dependents.

Overview

The processing pipeline shows how WikiText is parsed by a stack of parse rules into a parse tree. The parse tree is rendered as a tree of widgets, which is synchronised into the DOM via the RefreshMechanism.

DOM events trigger actions on widgets which update the tiddler store. The updates trigger a change event which in turn triggers the refresh mechanism to update the DOM.

Client/Server Architecture

When programming TiddlyWiki 5 plugins which make changes outside the scope of the included examples, you will need a more in-depth understanding of TiddlyWiki's internal architecture.

Model

TiddlyWiki's data model is a fairly simple key-value store.

  • Each tiddler is simply an object with a fields member containing members such as title, text, tags and so on.
  • The core Wiki class defined in boot/boot.js implements simple associative array behaviours like insertion, deletion, iteration, listing keys, and get-by-key.
  • The code in core/modules/wiki.js then extends the Wiki class with functionality such as event dispatch and various cache-backed methods such as getTiddlersWithTag.

The active tiddler store can be accessed via $tw.wiki as in this example:

$tw.wiki.makeTiddlerIterator($tw.wiki.getTiddlersWithTag('timeline')
    )(function(tiddler, title) {
        // Skip templates
        if (tiddler.fields.tags.indexOf('templates') >= 0) { return; }

        do_something(tiddler);
});

Data which should not be visible to end users under normal operation (eg. internal components, plugins, persisted state for GUI widgets) is stored in system tiddlers organised via a set of namespaces.

The similarity between filesystem paths and system tiddler names is intentional and will be used to provide a hierarchical browsing interface in a future TiddlyWiki release.

View

TiddlyWiki's view layer has a lot in common with desktop widget toolkits and this is the part which is most likely to trip up newcomers. There are two facets to it:

Role of the DOM

Because TiddlyWiki may re-render content, plugins should treat the DOM as write-only.

In other words, any state you store in the DOM could vanish at any instant and you need to use TiddlyWiki's internal StateMechanism instead.

In a desktop application, the base widget class defines a method such as paint(canvas) which is called in response to expose events or "data has changed" messages.

In TiddlyWiki, the Widget class in core/modules/widgets/widgets.js defines a render(parent, nextSibling) method which TiddlyWiki calls in response to various events such as changes in the model.

(The potential inefficiency of this approach is mitigated via a refresh(changedTiddlers) method which TiddlyWiki calls to ask your widget whether its current rendering is stale.)

Locus of Control

While TiddlyWiki's extended WikiText is similar in design to HTML templating languages with logic constructs, you can't just ignore it and assemble all of your content in raw Javascript because it is used to define most of TiddlyWiki's UI.

In this respect, it's closer to a glue language like Qt Quick or Python with Javascript filling the "create new components" role of C/C++ in widget toolkits like Qt and GTK+.

To familiarise yourself with this, read Widgets in WikiText and Introduction to Filters. then examine the internals for a tiddler like TaskManagementExample.

TiddlyWiki Architecture.svg

14th June 2014 at 12:08pm

TiddlyWiki as Single Page Application

15th July 2014 at 9:45am

TiddlyWiki builds on some basic concepts. First, TiddlyWiki should not be perceived as a dynamic web page like traditional server-side generated web pages. Instead TiddlyWiki can be perceived as an application which is written entirely in JavaScript and uses HTML5 and CSS3 to render a GUI. This way TiddlyWiki can be executed in any JavaScript environment like a browser or a node.js instance, while keeping the advantages of a simple application. One of these advantages is, that TiddlyWiki has no need to emulate an application state, as a traditional web application would need to do.

A second idea concerns the storage of the application data. In contrast to a traditional web application, TiddlyWiki doesn't store the data in an external database but simply uses native data structures already existing in JavaScript to store tiddlers, the basic (atomic) element of the TiddlyWiki application, in the memory. Additional core modules provide a way to persist this storage in simple HTML div elements.

Just by building on these simple and basic concepts,

  • TiddlyWiki is able to store application data in a single HTML page by using div elements as data container.
  • TW is able to store application code (JavaScript) in the same single HTML page.
  • TiddlyWiki can be executed in any JavaScript environment like a browser.

These points already enable TW to be used as an offline-enabled single file web application. Also, by using a server side node.js environment running the same TiddlyWiki application, TiddlyWiki can be used as an online web application. This is realized on server-side by providing an additional module to persist tiddlers into plain text files and on client-side by a module syncing the local data store with the node.js server.

TiddlyWiki Coding Style Guidelines

30th April 2015 at 3:33pm

Motivation

TiddlyWiki is a large project with many interested parties. It benefits everyone if the code is as easy to read as possible. A key part of that it must be written and laid out consistently – the code should read as though it were written by a single author.

Guidelines

This list of guidelines isn't exhaustive but captures some of the common problems. The ultimate guide is the existing TiddlyWiki code-base. There are still some places where the coding guidelines aren't used consistently within the core; pull requests are welcome to help resolve those issues.

Tabs and whitespace

TiddlyWiki uses 4-character tabs for indenting.

One blank line is used to separate blocks of code. Occasional blank lines are permitted within blocks for clarity, but should be avoided unless they solve a specific readability problem.

Layout of basic constructs

See the following example for layout of basic JavaScript constructs:

/*
Multiline comments are used to introduce a block of code such as a function definition
*/
function demoFunction(param,more) {
	// Proper sentence capitalisation for comments; prefer strict equality checks
	if(condition === "something") {
		// Always use braces, even when optional; no space between "if" and the brackets; always spaces around binary operators
		something = somethingElse;
		myOtherFunction(one,two); // No whitespace within function parameters
		do {
			myCondition.explore(); // Always use semicolons
		} while(myCondition < worsens);
	}	
}

Strings

Double quotes are preferred over single quotes for string literals.

TiddlyWiki Core Application

17th July 2014 at 8:38pm

The microkernel builds up the base functionality to manage tiddlers by providing a basic wiki store and a barebone tiddler model. The microkernel can also load a set of (decrypted) tiddlers and provides a module system, enabling a developer to extend the kernel and add functionality with module tiddlers and plug-ins.

For instance, the TiddlyWiki Core Application is provided as a single plug-in. In this part we want to focus on the TiddlyWiki core plug-in. After describing how new functionality is added directly to the wiki store, we show how the core plug-in realises persistence of tiddlers. The last part describes how TiddlyWiki builds an UI out of tiddlers and WikiText.

The TiddlyWiki Application consists of a microkernel and several modules building up the full application.

Startup Process

Modules with module-type: startup have to export a function named startup and may export a name property to identify this module. The startup function will be executed by the boot kernel at the end of the boot process.

// From boot.js:
// Gather up any startup modules
$tw.boot.remainingStartupModules = []; // Array of startup modules
$tw.modules.forEachModuleOfType("startup",function(title,module) {
	if(module.startup) {
		$tw.boot.remainingStartupModules.push(module);
	}
});
// Keep track of the startup tasks that have been executed
$tw.boot.executedStartupModules = Object.create(null);
$tw.boot.disabledStartupModules = $tw.boot.disabledStartupModules || [];
// Repeatedly execute the next eligible task
$tw.boot.executeNextStartupTask();

executeNextStartupTask() will execute the remaining startup modules in remainingStartupModules. A startup module can export the variables before and/or after, each containing an array of names of other startup modules. executeNextStartupTask() will use this information to execute the modules in the correct order.

Startup modules can be marked as synchronous by exporting synchronous = true. If synchronous is set to false, the startup function is executed with an callback function as the first argument. The startup function has to call this function to allow subsequent startup modules to get executed. This is necessary when the startup function itself uses asynchronous calls.

Extending the Store

Event Mechanism

Most of the following mechanisms need a way to get notified, when anything in the wiki store changes. The core -plug-in adds an event system to the bare wiki store. The event system provides the ability to listen to events. The most important is the "change" event which notifies the listeners when tiddlers have changed, with a list of the changed tiddlers. The event mechanism is one of the few mechanisms which needs a hook at the microkernel: The microkernel contains an empty function "enqueueTiddlerEvent(event)" and calls this function when a tiddler is added or deleted. The event mechanism from the core plug-in overwrites this function with it's own implementation. The functions providing the event system are added via the wikimethod module type.

Caching

The core plug-in adds two caches to the wiki store. A global cache and a cache for each tiddler. While the global cache is cleared whenever any tiddler changes, the tiddler cache is only cleared when the associated tiddler changes. The idea of the tiddler cache is, that sometimes we want to calculate information from a tiddler but don't want to recalculate every time we need this information. Imagine a tiddler which contains links to other tiddlers and at one point we want to have a list of the linked tiddlers (LinksFilter). With the tiddler cache we can parse the WikiText, extract the linked tiddlers and put this list in the cache. Until the tiddler is changed, the cache can be used to access this information in the next requests.

The same idea holds for the global cache. For example when a filter string is parsed and a list of matching tiddlers is constructed. This list can be put in the global cache for later reuse until something changes in the store.

Like the Event Mechanism, the cache needs hook functions in the microkernel. The microkernel calls clearGlobalCache() and clearCache(tiddlertitle) when a tiddler changes. The core's cache mechanism overwrites this functions. The functions providing the cache system are added via the wikimethod module type.

System Tiddlers

The core plug-in introduces a segregation of tiddlers. The tiddler model is central in the whole TiddlyWiki application and can be used in various roles. Because of the fact that a TiddlyWiki user works with tiddlers (taking the role of a wiki page) and tiddlers are the building blocks of the whole application (including modules, UI elements, etc.) we need to identify the internal tiddlers.

The core plug-in introduces the concept of system tiddlers. It builds on the convention that application internal tiddler names start with $:/. Then the core plug-in introduces a set of new functions to the wiki store which are used to retrieve tiddlers like getTiddlers(options) and forEachTiddler(callback, options). These functions work with all tiddlers in the store but the options parameter provides the ability to sort tiddlers by a field-name and exclude tiddlers with a specific tag. By default it doesn't return system tiddlers. To get a list of all tiddlers including system tiddlers, this must be requested explicitly via the options. If a function wants to present a list of tiddlers to the user it can use this new functions so that internal application tiddlers wouldn't clutter the resulting list. These functions are added via the wikimethod module type.

Tags and Filter Mechanism

Tags

The core plug-in extends the store by a simple mechanism to tag a tiddler. This provides the functionality to

  • retrieve tiddlers with a specific tag
  • retreive a hashmap of { tag: [tiddler1, tiddler3, tiddler3] }
  • list or iterate tiddlers not tagged with a specific tag

The tags are stored directly in the tiddler. Each tiddler can have a field named "tag", which contains an array of its tags. The above functions use this field to calculate their results and cache them in a global cache.

Organising information with tags is easy, intuitive and is common throughout the web. In most cases the functions mentioned above are not enough and a more sophisticated way of querying is needed. But instead of focusing only on tags TiddlyWiki introduces a querying system that isn't bound to tags or single tiddlers.

Filter mechanism

This filter mechanism is build on the idea of a pipeline of single filter operators. Filters are noted as strings and can look like

[tag[task]!tag[done]interesting[very]]

This example would (implicitly) put all available tiddlers into the pipe. The first operator tag[task] would only pass tiddlers which are tagged with "task". The !tag[done] operator is negated and only passes tiddlers which are not tagged with "done". The last filter operator passes only tiddlers with a field "interesting" set to "very". So as a result this filter would be used to obtain all tiddlers which are marked as task, aren't already done and are very interesting.

There are many filter operators already build into the core plug-in including the mentioned tag- and field operators, filter operators to sort the tiddlerlist by a specified field, etc. But more sophisticated operators are possible, too. An example would be the search-operator. This filter operator looks for the searched string in the text and in the tags of the tiddlers. If the provided filter operators are not enough, a developer can add new filters by adding a module with the filteroperator type.

System Tags

Tags and the filter mechanism are used throughout the core plug-in for internal puproses. Tags which start with $:/ are normally hidden from the casual user, similar to System Tiddlers

The filter mechanism is added to the wiki store with the wikimethod module type.

Extended Persistence

The microkernel only contains a bare store and some deserializers to load tiddlers from JSON files or from the DOM of the current HTML file. The core plug-in adds some more deserializers and a new mechanism for persisting and synchronising tiddlers.

This mechanism is provided as a global module in $:/core/modules/syncer.js. The saver module has three responsibilities:

  1. Save the whole wiki.
  2. Provide the ability to download single tiddlers as files.
  3. Synchronise the local wiki store with a remote wiki store, i.e. running in Node.js

The syncer module is connected mainly to two other modules. For one it registers to changes at the wiki store (Event Mechanism) and if any changes occur they are synced to the remote store. Then it provides a function saveWiki(options). This function can be used by other modules. For example the RootWidget uses this function to save the whole wiki or start downloading single tiddlers.

The syncer itself does not provide a concrete implementation of saving, downloading or syncing the tiddlers. Instead it loads modules of type saver and syncadaptor and manages the saving/syncing process.

Deserializer

Modules with module-type: tiddlerdeserializer can provide functions to create tiddlers out of any textual representation. Each function must be associated with a type like application/json or text/html. They get the textual representation of the tiddlers, some default field values and the type of the text block as arguments. They return an array of JavaScript objects representing the resulting tiddlers.

Deserializers are not managed by the syncer module. Instead the concept of deserializers is in fact part of the microkernel. This is necessary because the microkernel needs a way of loading tiddlers before it can load the core plug-in and execute it's startup modules. (Due to the fact that the core plug-in and it's modules are represented as tiddlers.)

The load-modules startup module loads additional deserializers and pushes them into the store. The core plug-in for example contains a few deserializers which can read a whole TiddlyWiki 5 or classic HTML file and load the individual tiddlers into the store.

Saver

Modules with module-type: saver provide functionality to save the whole wiki. There are three methods a saver can support:

save
This method is used, when the user requests a save, for example by clicking the save button in the sidebar.
autosave
This method is used automatically by TW when tiddlers are changed, created or deleted by the user.
download
This message is used when the wiki or a single tiddler should explicitly be downloaded. The control panel for example uses this method to provide a button which saves the wiki as a static HTML file.

A saver module has to export two functions. canSave(wiki) returning true if this module is capable of working and create(wiki) returning an instance of a saver object. This saver object has to provide an info property containing a name, a priority, an array of methods it supports and a method save(text,method,callback). This method is called from TW with the actual text which should be saved, the method which is used and a callback function to report errors: callback("Error while saving") or to notify that saving went well: callback(null, "Saving went well :)"). If the saver method successfully saved the file it has to return true, or false otherwise. Saves are triggered by messages from the UI. The syncer module uses the saver with the highest priority capable of the requested method to save the file.

The core plug-in contains a saver capable of saving the current state of the wiki to the local hard drive by using a special Firefox extension called Tiddlyfox. If this extension is not available, the savers canSave method would return false. A saver with a lower priority would then ask the user to save the current state as a new HTML file.

Syncadaptor

A module with module-type: syncadaptor provides functionality to get a list of tiddlers (this list is provided as SkinnyTiddlers, which are normal tiddlers without the text field) and to load, save and delete single tiddlers. A syncadaptor can also provide functions to login and logout so that syncadaptor modules can be used to synchronise tiddlers with a remote server.

The syncer module only uses one syncadaptor and honours a special system tiddler $:/config/SyncFilter containing a filter string. Tiddlers matching this filter string are saved to the server with a syncadapter. It uses the WebServer API to load modified tiddlers from the server, which returns only non-system tiddlers.

UI and Rendering Pipeline

The microkernel provides basic functionality to store tiddlers and manage modules and plugins. The Startup Process then loads the core plug-in including extensions to the store providing a Event Mechanism, Caching, Tags and a Filter Mechanism giving the ability to query for specific tiddlers.

Using some of this techniques the core plug-in also adds some functionalities to load and save single tiddlers or the whole wiki, described in Extended Persistence.

This next chapter will focus on the parts of the core plug-in that provide the UI of TiddlyWiki.

The rendering pipeline [https://tiddlywiki.com/talkytalky, 17.07.2014]

Parser

The first stage of WikiText processing is the parser. A Parser is provided by a module with module-type: parser and is responsible to transform block of text to a parse-tree. The parse-tree consists of nested nodes like

{type: "element", tag: <string>, attributes: {}, children: []} - an HTML element
{type: "text", text: <string>} - a text node
{type: "entity", entity: <string>} - an HTML entity like &copy; for a copyright symbol
{type: "raw", html: <string>} - raw HTML

The core plug-in provides a recursive descent WikiText parser which loads it's individual rules from individual modules. Thus a developer can provide additional rules by using module-type: wikirule. Each rule can produce a list of parse-tree nodes. A simple example for a wikirule producing a <hr> from --- can be found in horizrule.js

HTML tags can be embedded into WikiText because of the html rule. This rule matches HTML tag syntax and creates type: "element" nodes. But the html-rule has another special purpose. By parsing the HTML tag syntax it implicitly parses WikiText widgets. It the recognises them by the $ character at the beginning of the tag name and instead of producing "element" nodes it uses the tag name for the type:

{type: "list", tag: "$list", attributes: {}, children: []} - a list element

The Widgets part will reveal why this makes sense and how each node is transformed into a widget. Another special characteristic of the html-rule or the parse nodes in general is the attributes property. Attributes in the parse-tree are not stored as simple strings but they are nodes of its own to make indirect text references available as attributes as described in Widgets:

{type: "string", value: <string>} - literal string
{type: "indirect", textReference: <textReference>} - indirect through a text reference

Widgets

When the WikiText has been transformed into a parse-tree the next step is to transform this parse-tree into a widget-tree. We talked about widgets as parts of the WikiText markup but in fact each node of the parse-tree is transformed to a widget object. The core plug-in provides a basic widget object which gets the parse node it should represent and a DOM node. The widget then must create the DOM structure which represents the parse node and add it to the provided DOM node. A LinkWidget for example would create the DOM node for a <a>...</a> tag and put it in the provided DOM node. When a widget gets a parse node with child nodes it must create the corresponding child widgets.

All this functionality is basically provided with the base widget. But when creating the widget for a parse node it loads additional modules with module-type: widget. These modules can export multiple widgets which extend the base widget and can override it's methods like the rendering and refresh functions. As described in Parser each parse node contains a "type" property. This type directly determines which widget module is used.

{type: "text", text: <string>} - a text node

Would be transformed to a TextWidget. (Note that the TextWidget module exports a property "text".)

So in fact when talking about widgets in WikiText, they are not a feature added to WikiText but the ability to directly use a specific widget instead of the parser choosing a widget type.

In the beginning we talked about widgets and how they enable dynamic behaviour. But up until now we only described how widgets create DOM nodes from parse nodes. Widgets add dynamic behaviour in two ways.

Messages

Messages are events that are triggered by the user. They are generated by widgets for example when the user clicks on a ButtonWidget. Each message has a type property like "tm-delete-tiddler" and a parameter.

{type: "tm-delete-tiddler", param: "MyOldTiddler"}

When such a message is created by a widget it sends the message to it's parent widget which sends it to it's own parent widget and so on. On this way each widget can try to dispatch the message. This concept is realised in the base widget object. It provides a function dispatchEvent(message) which is called by the children to bubble the message up the widget tree. Another function addEventListener(type,listener) can be used to bind a function to a specific message type. If the listener returns false, the message is send to the parent widget.

The TiddlyWiki core plug-in handles a lot of messages in the NavigatorWidget.

Selective Update

With Messages a widget is able to put some kind of events into the TiddlyWiki application which is one part of the dynamic behaviour of widgets. The other part is selective updating. Widgets are often dependant on tiddler states. The ListWidget for example can be configured to list all tiddlers which are tagged with "important". Now, when such a tiddler is changed and it's "important" tag is removed, this change should reflect in the ListWidget. To allow widgets to react on such changes, each widget can provide a function refresh(changedTiddlers). The RootWidget is registered to the wiki store, using the Event Mechanism. When an change event occurs it starts to call the refresh function of its children with a list of the changed tiddlers. Each widget can then decide if it has to change or re-render its DOM representation and call the refresh function of its own children. This way every time a tiddler or the wiki store itself changes, each widget can instantly react on these changes or ignore them.

Another way of updating are text reference attributes (text references explained in Transclusion and TextReference):

{type: "indirect", textReference: <textReference>}

When a widget got a attribute which is a text reference and the refresh function is called, it can check if the text reference references a changed tiddler and update accordingly.

Nearly every state of the UI is stored in tiddlers. A search mechanism for example would use a EditTextWidget which shows an text input field for the search string and a ListWidget to show the results. The EditTextWidget is bound to a tiddler $:/temp/search. Meaning when editing the text in the input field the tiddlers content is changed to this text. On the other hand when the tiddler changes the RootWidget is notified. It then starts calling its childrens refresh methods. Eventually the refresh method of the EditTextWidget is called and it can re-render itself if necessary. This way TiddlyWiki can re-use an already existing data structure which is not only convenient because we don't need to introduce an additional structure but tiddlers managed in the wiki store are already a pretty powerful data structure, supporting an Event Mechanism (so we don't need an additional observer pattern), Caching, Metadata by additional tiddler fields and a way to obtain specific tiddlers.

Transclusion and TextReference

The previous parts about Widgets and the Parser explained how a block of WikiText is transformed into a DOM representation and how this presentation can react on changes to the wiki store. The previous chapters also describe that WikiText is saved in individual tiddlers, including the WikiText describing the UI components. This raises the question, how these multiple tiddlers are build up to a single UI. But before answering this question we need to introduce text references and transclusion.

TextReference

A text reference describes a special notation to indirectly refer to the contents of a specified tiddler field. The syntax of a text reference is:

<tiddlertitle>
<tiddlertitle>!!<fieldname>
!!<fieldname> - specifies a field of the current tiddlers

To obtain the actual text, the core plug-in adds a function to the wiki store getTextReference(textRef,defaultText,currTiddlerTitle). The "currentTiddlerTitle" is the title of a tiddler which is used when no tiddlerTitle is specified in the text reference. What the currentTiddler should be, depends on where the text reference is used. If it is for example used as a widget attribute, the current tiddler is the tiddler which contains this widget. Text references are used by widgets (attributes), filteroperators (parameter) and in transclusions. These elements use the getTextReference(textRef,defaultText,currTiddlerTitle) function.

Transclusion

Transclusion means including the contents of a different tiddler. This is realized with a transclude widget which parses the tiddler to be included and adds the resulting nodes as children of its own parse node. The trick with transclusion is, that it shows the content of a different tiddler but by default it does not change the current tiddler. This enables us to create tiddlers with text references and use them as a templates.

For example: Tiddler MyTask having the fields and the content of:

important: "very"
assoc.person: "Hans Dampf"

<$transclude tiddler="TaskHeaderTemplate" />

Hans needs some more Dampf.

And Tiddler TaskHeaderTemplate with a content of:

<$view field="assoc.person"/> has a <$view field="important"/> important task for us:

When showing tiddler MyTask it would result in:

Hans Dampf has a very important task for us:

Hans needs some more Dampf.

Transclusion and templates is one of the most important concepts in TiddlyWiki. It allows us to include other tiddlers using the metadata for our current tiddler. It also allows us to transclude a template tiddler with a third tiddler set as the currentTiddler with the TiddlerWidget:

<$tiddler tiddler="MyTiddler">
<$transclude tiddler="EditTemplate" />
</$tiddler>

This way we can create a different view on a tiddler which does not only show it's title and it's content but shows it's content and metadata in editable text fields and allows us to edit this tiddler. Also the template concept is used by the ListWidget to show the tiddlers through a specified template. Finally, when wanting to download the wiki as a new html file, this html file is created by binding a list of all tiddlers to a template and sending the resulting text to the syncer module described in Extended Persistence to save the current wiki.

RootWidget and Rendering Startup

The previous parts of this chapter showed how WikiText is transformed to DOM nodes which dynamically react to tiddler changes and a way to compose tiddlers from other tiddlers. This last part describes how the TiddlyWiki core plug-in starts up a UI build from tiddlers and WikiText.

After the microkernel has loaded it starts executing Startup Modules. The core plug-in contains two startup modules which are responsible to kick off the UI:

rootwidget.js is a startup module and creates an instance of the base widget. This widget is globally accessible $tw.rootWidget. The DOM node associated to this widget is the current browser window's DOM (document).

At first, the root widget has no children but provides some basic event handlers (Messages) like:

  • tm-notify: Displays the message given in param as a notification.
  • tm-save-wiki: Triggered by a save button, the user can click. This handler uses the syncer module described in Extended Persistence to save the current wiki.
  • tm-auto-save-wiki: Similar to tm-save-wiki but not triggered directly by the user but automatically triggered when a wiki page is edited and saved. A Saver implementation which starts a download of the updated wiki file would not support the auto-save method and would only be used when the tm-save-wiki message is used.
  • tm-download-file: This message also uses the syncer module described in Extended Persistence but explicitly demands to choose a saver with the download-method to start downloading a single tiddler.

After the root widget is loaded another startup module $:/core/modules/startup/render.js creates a transclude widget which contains the contents of $:/core/ui/PageTemplate which is now bound to the browsers DOM document. The render function of the transclude widget is initially executed and a listener is registered at the store which executes the refresh function of the transclude widget to trigger the Selective Update process.

Techniques for including other tiddlers and Templates are finally used in $:/core/ui/PageTemplate to build the TiddlyWiki UI only from tiddlers written in WikiText (with widgets implemented in javascript):

For example to implement the list of open wiki pages the $:/core/ui/PageTemplate contains a navigator widget which maintains a list of open tiddlers in a field of $:/StoryList and handles events like tm-navigate by adding a tiddler specified as parameter to the top of the list in $:/StoryList. The story tiddler transcluded in $:/core/ui/PageTemplate then uses a ListWidget to transclude all tiddlers in $:/StoryList through a special template $:/core/ui/ViewTemplate. A event of the type tm-close-tiddler would remove a specified tiddler from $:/StoryList. The Event Mechanism would trigger a changed event which triggers a call of the ListWidget's refresh function which would remove the tiddler from the list, closing the tiddler.

TiddlyWiki Drag and Drop Interoperability

It is straightforward to allow any HTML file to interoperate with TiddlyWiki's drag and drop implementation.

This example shows how to attach draggable data to a DOM element. The data is provided in two different forms:

  • the string data is used if the element is dragged onto a text editing area
  • the tiddler data is used if the element is dragged into TiddlyWiki's import area

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Drag and Drop Interoperability with TiddlyWiki Demo</title>
    <style>
        #draggable {
            padding: 1em;
            margin: 1em;
            background: #ecc;
        }
    </style>
</head>
<body>
    <div id="draggable" draggable="true">
        Drag me to a TiddlyWiki window
    </div>
    <button id="copy">
        Click to copy two tiddlers to the clipboard
    </button>

</body>
<script>

    var titleString = "This is the string that appears when the block is dragged to a text input";
    var tiddlerData = [
        {title: "Tiddler One", text: "This is one of the payload tiddlers"},
        {title: "Tiddler Two", text: "This is another of the payload tiddlers", "custom-field": "A custom field value"}
    ];

    document.getElementById("draggable").addEventListener("dragstart",function(event) {
        event.dataTransfer.setData("URL","data:text/vnd.tiddler," + encodeURIComponent(JSON.stringify(tiddlerData)));
        event.dataTransfer.setData("Text",titleString);
        event.stopPropagation();
        return false;
    });

    document.getElementById("copy").addEventListener("click",function(event) {

    function listener(event) {
        event.clipboardData.setData("URL","data:text/vnd.tiddler," + encodeURIComponent(JSON.stringify(tiddlerData)));
        event.preventDefault();
    }

    document.addEventListener("copy",listener);
    document.execCommand("copy");
    document.removeEventListener("copy",listener);

    });
</script>
</html>

TiddlyWiki for Developers

18th March 2014 at 9:07pm

TiddlyWiki is published as OpenSource which means that anyone can read the code and contribute to its development.

Resources

If you're interested in understanding more about the internal operation of TiddlyWiki, TiddlyWiki Architecture gives an overview of how TiddlyWiki is structured. Then read the code – start with the boot kernel $:/boot/boot.js.

The one thing you need to know

TiddlyWiki's architecture is very different from an HTML page written using jQuery. This section concisely explains what TiddlyWiki does differently. It may not make much sense on the first reading.

The key to understanding how it works internally is to see that the RefreshMechanism requires that any region of the DOM can be regenerated at any time. This means that the entire state of the user interface must reside in the tiddler store, from where it is synchronised into the DOM. This is done to improve performance by minimising the DOM interactions during the refresh cycles.

It also determines the standard UI flow:

  1. An event handler on a widget is triggered
  2. The event handler can manipulate the DOM nodes directly created by the widget, and/or modify the state of the tiddler store
  3. The core then issues a store change event which triggers the refresh cycle
  4. Each widget in the tree then gets a chance to refresh itself to reflect the changes in the store if they need to

From a technical perspective, TiddlyWiki is a fairly classic MVC architecture, with strict separation of concerns. The model is the tiddler store, the view is a rendering tree (such as the one created from $:/core/ui/PageTemplate in startup.js), and the controller is the core code itself.

TiddlyWiki on NW.js

1st January 2014 at 5:53pm

NW.js (previously known as "node-webkit") allows TiddlyWiki to be set up as a native application for Windows, Mac OS X or Linux.

Setting up

  1. Create a folder with the following contents:
    1. The appropriate copy of NW.js for your platform, downloaded from https://github.com/nwjs/nw.js
    2. Your TiddlyWiki HTML file as index.html
    3. A file called package.json with the following content:
      {
          "name": "tiddlywiki",
          "main": "./index.html",
      	"window": {
      		"toolbar": true,
      		"width": 1024,
      		"height": 768
      	}
      }
  2. Run the NW.js application
  3. Try saving changes in the usual way

Limitations

MP3 audio and H264 video are not supported without special steps described on the NW.js wiki.

TiddlyWiki5 Development Environment

26th August 2013 at 8:48pm

This information is for people who are working on the development of TiddlyWiki5 itself, and isn't relevant for end users

Setting up npm

Installing TiddlyWiki5 with NPM downloads a snapshot release of TiddlyWiki5. To use a development copy of the TiddlyWiki5 repository instead of the copy installed by NPM, use this command within the root of the TiddlyWiki5 repo:

npm link

Bumping version numbers

As releases are made during development it is necessary to adjust the version number of the TiddlyWiki5 core. This is done with the npm version command. For example:

npm version 5.0.0-alpha.10

As described in #10 in this article by npm's author, when run from within a git repo this command will also commit the change and tag it

Timeline of the startup Process

8th July 2014 at 9:02am

This section shows a quick and short overview over the startup process of TW, from the first step of the boot mechanism until the loading of the different startup modules. The image shown below shall point out the main parts of this startup-process.

Toc

14th July 2014 at 5:18pm

Transclusion and TextReference

17th July 2014 at 6:18pm

The previous parts about Widgets and the Parser explained how a block of WikiText is transformed into a DOM representation and how this presentation can react on changes to the wiki store. The previous chapters also describe that WikiText is saved in individual tiddlers, including the WikiText describing the UI components. This raises the question, how these multiple tiddlers are build up to a single UI. But before answering this question we need to introduce text references and transclusion.

TextReference

A text reference describes a special notation to indirectly refer to the contents of a specified tiddler field. The syntax of a text reference is:

<tiddlertitle>
<tiddlertitle>!!<fieldname>
!!<fieldname> - specifies a field of the current tiddlers

To obtain the actual text, the core plug-in adds a function to the wiki store getTextReference(textRef,defaultText,currTiddlerTitle). The "currentTiddlerTitle" is the title of a tiddler which is used when no tiddlerTitle is specified in the text reference. What the currentTiddler should be, depends on where the text reference is used. If it is for example used as a widget attribute, the current tiddler is the tiddler which contains this widget. Text references are used by widgets (attributes), filteroperators (parameter) and in transclusions. These elements use the getTextReference(textRef,defaultText,currTiddlerTitle) function.

Transclusion

Transclusion means including the contents of a different tiddler. This is realized with a transclude widget which parses the tiddler to be included and adds the resulting nodes as children of its own parse node. The trick with transclusion is, that it shows the content of a different tiddler but by default it does not change the current tiddler. This enables us to create tiddlers with text references and use them as a templates.

For example: Tiddler MyTask having the fields and the content of:

important: "very"
assoc.person: "Hans Dampf"

<$transclude tiddler="TaskHeaderTemplate" />

Hans needs some more Dampf.

And Tiddler TaskHeaderTemplate with a content of:

<$view field="assoc.person"/> has a <$view field="important"/> important task for us:

When showing tiddler MyTask it would result in:

Hans Dampf has a very important task for us:

Hans needs some more Dampf.

Transclusion and templates is one of the most important concepts in TiddlyWiki. It allows us to include other tiddlers using the metadata for our current tiddler. It also allows us to transclude a template tiddler with a third tiddler set as the currentTiddler with the TiddlerWidget:

<$tiddler tiddler="MyTiddler">
<$transclude tiddler="EditTemplate" />
</$tiddler>

This way we can create a different view on a tiddler which does not only show it's title and it's content but shows it's content and metadata in editable text fields and allows us to edit this tiddler. Also the template concept is used by the ListWidget to show the tiddlers through a specified template. Finally, when wanting to download the wiki as a new html file, this html file is created by binding a list of all tiddlers to a template and sending the resulting text to the syncer module described in Extended Persistence to save the current wiki.

UI and Rendering Pipeline

17th July 2014 at 7:52pm

The microkernel provides basic functionality to store tiddlers and manage modules and plugins. The Startup Process then loads the core plug-in including extensions to the store providing a Event Mechanism, Caching, Tags and a Filter Mechanism giving the ability to query for specific tiddlers.

Using some of this techniques the core plug-in also adds some functionalities to load and save single tiddlers or the whole wiki, described in Extended Persistence.

This next chapter will focus on the parts of the core plug-in that provide the UI of TiddlyWiki.

The rendering pipeline [https://tiddlywiki.com/talkytalky, 17.07.2014]

Parser

The first stage of WikiText processing is the parser. A Parser is provided by a module with module-type: parser and is responsible to transform block of text to a parse-tree. The parse-tree consists of nested nodes like

{type: "element", tag: <string>, attributes: {}, children: []} - an HTML element
{type: "text", text: <string>} - a text node
{type: "entity", entity: <string>} - an HTML entity like &copy; for a copyright symbol
{type: "raw", html: <string>} - raw HTML

The core plug-in provides a recursive descent WikiText parser which loads it's individual rules from individual modules. Thus a developer can provide additional rules by using module-type: wikirule. Each rule can produce a list of parse-tree nodes. A simple example for a wikirule producing a <hr> from --- can be found in horizrule.js

HTML tags can be embedded into WikiText because of the html rule. This rule matches HTML tag syntax and creates type: "element" nodes. But the html-rule has another special purpose. By parsing the HTML tag syntax it implicitly parses WikiText widgets. It the recognises them by the $ character at the beginning of the tag name and instead of producing "element" nodes it uses the tag name for the type:

{type: "list", tag: "$list", attributes: {}, children: []} - a list element

The Widgets part will reveal why this makes sense and how each node is transformed into a widget. Another special characteristic of the html-rule or the parse nodes in general is the attributes property. Attributes in the parse-tree are not stored as simple strings but they are nodes of its own to make indirect text references available as attributes as described in Widgets:

{type: "string", value: <string>} - literal string
{type: "indirect", textReference: <textReference>} - indirect through a text reference

Widgets

When the WikiText has been transformed into a parse-tree the next step is to transform this parse-tree into a widget-tree. We talked about widgets as parts of the WikiText markup but in fact each node of the parse-tree is transformed to a widget object. The core plug-in provides a basic widget object which gets the parse node it should represent and a DOM node. The widget then must create the DOM structure which represents the parse node and add it to the provided DOM node. A LinkWidget for example would create the DOM node for a <a>...</a> tag and put it in the provided DOM node. When a widget gets a parse node with child nodes it must create the corresponding child widgets.

All this functionality is basically provided with the base widget. But when creating the widget for a parse node it loads additional modules with module-type: widget. These modules can export multiple widgets which extend the base widget and can override it's methods like the rendering and refresh functions. As described in Parser each parse node contains a "type" property. This type directly determines which widget module is used.

{type: "text", text: <string>} - a text node

Would be transformed to a TextWidget. (Note that the TextWidget module exports a property "text".)

So in fact when talking about widgets in WikiText, they are not a feature added to WikiText but the ability to directly use a specific widget instead of the parser choosing a widget type.

In the beginning we talked about widgets and how they enable dynamic behaviour. But up until now we only described how widgets create DOM nodes from parse nodes. Widgets add dynamic behaviour in two ways.

Messages

Messages are events that are triggered by the user. They are generated by widgets for example when the user clicks on a ButtonWidget. Each message has a type property like "tm-delete-tiddler" and a parameter.

{type: "tm-delete-tiddler", param: "MyOldTiddler"}

When such a message is created by a widget it sends the message to it's parent widget which sends it to it's own parent widget and so on. On this way each widget can try to dispatch the message. This concept is realised in the base widget object. It provides a function dispatchEvent(message) which is called by the children to bubble the message up the widget tree. Another function addEventListener(type,listener) can be used to bind a function to a specific message type. If the listener returns false, the message is send to the parent widget.

The TiddlyWiki core plug-in handles a lot of messages in the NavigatorWidget.

Selective Update

With Messages a widget is able to put some kind of events into the TiddlyWiki application which is one part of the dynamic behaviour of widgets. The other part is selective updating. Widgets are often dependant on tiddler states. The ListWidget for example can be configured to list all tiddlers which are tagged with "important". Now, when such a tiddler is changed and it's "important" tag is removed, this change should reflect in the ListWidget. To allow widgets to react on such changes, each widget can provide a function refresh(changedTiddlers). The RootWidget is registered to the wiki store, using the Event Mechanism. When an change event occurs it starts to call the refresh function of its children with a list of the changed tiddlers. Each widget can then decide if it has to change or re-render its DOM representation and call the refresh function of its own children. This way every time a tiddler or the wiki store itself changes, each widget can instantly react on these changes or ignore them.

Another way of updating are text reference attributes (text references explained in Transclusion and TextReference):

{type: "indirect", textReference: <textReference>}

When a widget got a attribute which is a text reference and the refresh function is called, it can check if the text reference references a changed tiddler and update accordingly.

Nearly every state of the UI is stored in tiddlers. A search mechanism for example would use a EditTextWidget which shows an text input field for the search string and a ListWidget to show the results. The EditTextWidget is bound to a tiddler $:/temp/search. Meaning when editing the text in the input field the tiddlers content is changed to this text. On the other hand when the tiddler changes the RootWidget is notified. It then starts calling its childrens refresh methods. Eventually the refresh method of the EditTextWidget is called and it can re-render itself if necessary. This way TiddlyWiki can re-use an already existing data structure which is not only convenient because we don't need to introduce an additional structure but tiddlers managed in the wiki store are already a pretty powerful data structure, supporting an Event Mechanism (so we don't need an additional observer pattern), Caching, Metadata by additional tiddler fields and a way to obtain specific tiddlers.

Transclusion and TextReference

The previous parts about Widgets and the Parser explained how a block of WikiText is transformed into a DOM representation and how this presentation can react on changes to the wiki store. The previous chapters also describe that WikiText is saved in individual tiddlers, including the WikiText describing the UI components. This raises the question, how these multiple tiddlers are build up to a single UI. But before answering this question we need to introduce text references and transclusion.

TextReference

A text reference describes a special notation to indirectly refer to the contents of a specified tiddler field. The syntax of a text reference is:

<tiddlertitle>
<tiddlertitle>!!<fieldname>
!!<fieldname> - specifies a field of the current tiddlers

To obtain the actual text, the core plug-in adds a function to the wiki store getTextReference(textRef,defaultText,currTiddlerTitle). The "currentTiddlerTitle" is the title of a tiddler which is used when no tiddlerTitle is specified in the text reference. What the currentTiddler should be, depends on where the text reference is used. If it is for example used as a widget attribute, the current tiddler is the tiddler which contains this widget. Text references are used by widgets (attributes), filteroperators (parameter) and in transclusions. These elements use the getTextReference(textRef,defaultText,currTiddlerTitle) function.

Transclusion

Transclusion means including the contents of a different tiddler. This is realized with a transclude widget which parses the tiddler to be included and adds the resulting nodes as children of its own parse node. The trick with transclusion is, that it shows the content of a different tiddler but by default it does not change the current tiddler. This enables us to create tiddlers with text references and use them as a templates.

For example: Tiddler MyTask having the fields and the content of:

important: "very"
assoc.person: "Hans Dampf"

<$transclude tiddler="TaskHeaderTemplate" />

Hans needs some more Dampf.

And Tiddler TaskHeaderTemplate with a content of:

<$view field="assoc.person"/> has a <$view field="important"/> important task for us:

When showing tiddler MyTask it would result in:

Hans Dampf has a very important task for us:

Hans needs some more Dampf.

Transclusion and templates is one of the most important concepts in TiddlyWiki. It allows us to include other tiddlers using the metadata for our current tiddler. It also allows us to transclude a template tiddler with a third tiddler set as the currentTiddler with the TiddlerWidget:

<$tiddler tiddler="MyTiddler">
<$transclude tiddler="EditTemplate" />
</$tiddler>

This way we can create a different view on a tiddler which does not only show it's title and it's content but shows it's content and metadata in editable text fields and allows us to edit this tiddler. Also the template concept is used by the ListWidget to show the tiddlers through a specified template. Finally, when wanting to download the wiki as a new html file, this html file is created by binding a list of all tiddlers to a template and sending the resulting text to the syncer module described in Extended Persistence to save the current wiki.

RootWidget and Rendering Startup

The previous parts of this chapter showed how WikiText is transformed to DOM nodes which dynamically react to tiddler changes and a way to compose tiddlers from other tiddlers. This last part describes how the TiddlyWiki core plug-in starts up a UI build from tiddlers and WikiText.

After the microkernel has loaded it starts executing Startup Modules. The core plug-in contains two startup modules which are responsible to kick off the UI:

rootwidget.js is a startup module and creates an instance of the base widget. This widget is globally accessible $tw.rootWidget. The DOM node associated to this widget is the current browser window's DOM (document).

At first, the root widget has no children but provides some basic event handlers (Messages) like:

  • tm-notify: Displays the message given in param as a notification.
  • tm-save-wiki: Triggered by a save button, the user can click. This handler uses the syncer module described in Extended Persistence to save the current wiki.
  • tm-auto-save-wiki: Similar to tm-save-wiki but not triggered directly by the user but automatically triggered when a wiki page is edited and saved. A Saver implementation which starts a download of the updated wiki file would not support the auto-save method and would only be used when the tm-save-wiki message is used.
  • tm-download-file: This message also uses the syncer module described in Extended Persistence but explicitly demands to choose a saver with the download-method to start downloading a single tiddler.

After the root widget is loaded another startup module $:/core/modules/startup/render.js creates a transclude widget which contains the contents of $:/core/ui/PageTemplate which is now bound to the browsers DOM document. The render function of the transclude widget is initially executed and a listener is registered at the store which executes the refresh function of the transclude widget to trigger the Selective Update process.

Techniques for including other tiddlers and Templates are finally used in $:/core/ui/PageTemplate to build the TiddlyWiki UI only from tiddlers written in WikiText (with widgets implemented in javascript):

For example to implement the list of open wiki pages the $:/core/ui/PageTemplate contains a navigator widget which maintains a list of open tiddlers in a field of $:/StoryList and handles events like tm-navigate by adding a tiddler specified as parameter to the top of the list in $:/StoryList. The story tiddler transcluded in $:/core/ui/PageTemplate then uses a ListWidget to transclude all tiddlers in $:/StoryList through a special template $:/core/ui/ViewTemplate. A event of the type tm-close-tiddler would remove a specified tiddler from $:/StoryList. The Event Mechanism would trigger a changed event which triggers a call of the ListWidget's refresh function which would remove the tiddler from the list, closing the tiddler.

Undefined widget demo

1st February 2019 at 9:31pm

[ { "title": "$:/DefaultTiddlers", "text": "[[Undefined widget]]" } ] [ { "title": "Undefined widget", "text": "\n```\n<$donothing/>\n```\n\nRenders as:\n\n<$donothing/>\n" } ]

Undefined widget tutorial

2nd February 2019 at 2:56pm

Let's start be defining a minimal widget which does nothing. It doesn't support any attributes, no child elements, and it doesn't output anything. The name of the widget will be donothing. If it does nothing, then how can we verify after writing the code that we accomplished anything? Well, let's see what happens when an undefined widget is referenced.

[ { "title": "$:/DefaultTiddlers", "text": "[[Undefined widget]]" } ] [ { "title": "Undefined widget", "text": "\n```\n<$donothing/>\n```\n\nRenders as:\n\n<$donothing/>\n" } ]

Since we haven't written the code, the attempt to render the widget gives an undefined widget error. So we will know the donothing code accomplishes something if we don't get the undefined widget error when rendering.

Using ES2016 for Writing Plugins

2nd March 2024 at 11:07am

With the advent of ES2015 (also known as ES6) and the availability of Babel.js plugin developers can leverage ES2015 when writing TiddlyWiki plugins. Understanding the nuances between TiddlyWiki's module sandbox and how Babel compiles it's output ready for a module system like CommonJS/AMD.

Please understand how the PluginMechanism works since this is all about writing a plugin using Babel to compile the output that will be included in the final TiddlyWiki (for example TiddlyWiki on Node.js).

Use a framework

It is recommended to use develop toolkit managed by community. For example,

  1. Modern.TiddlyDev

They are known as "JavaScript Meta-Framework". With them, you can start developing in a few minutes, without hours of configuration and debugging the build steps.

Installing and Configuring Babel by yourself

You can install Babel using

$ npm install --global babel-cli babel-presets-es2015

If your developing the plugin for inclusion to the npm registry (or for convenience) you can avoid the global install and save it to the local package.json file with

$ npm install --save-dev babel-cli babel-presets-es2015

Inside your plugin project edit the file .babelrc and enter the following:

{
  "presets": [
    "es2015"
  ]
}

I found it easier to manage my plugins as if they were npm modules complete with a package.json that compiles the output via npm run build. See npm-scripts documentation for details.

Another benefit of using such a "Meta-Framework" is that you can easily maintain your configuration, you will find it difficult to upgrade those config files after several months.

Compiling the Output

Pick a folder to store the ES2015 JavaScript and a folder to output the TiddlyWiki ready JavaScript. In this example I will use src and lib respectively. With Babel installed and working I can compile all the JavaScript in the src folder to the lib folder by running this command:

$ babel src -d lib

Babel will not copy over non-JavaScript files. It is up to the developer to include all the supporting files themselves. Babel only converts the JavaScript files (ending in .js) from the src folder to the lib folder.

Imports and Exports

In a plugin written pre-ES2015 one would require a module through TiddlyWiki like so:

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

But in ES2015 the following would look like:

import { widget as Widget } from '$:/core/modules/widgets/widget.js';

Conveniently when Babel compiles this it will essentially output the same JavaScript as the first pre-ES2016 code.

Also, in ES2016 you are required to declare your imports at the beginning and can not dynamically require things. This means you can not have an import statement in an if block or in a function. If that functionality is desired then you will have to go back to using the require() statement directly. But conciser that by doing so that you may be missing an oppertunity to make your code cleaner and better.

Exporting is the same thing. Instead of assigning to a property of the exports variable you use the export keyword:

export { MyWidget as mywidget };

It is illegal JavaScript to export with a name that is not an identifier even though it is ok to use a non-identifier (string) as a property key. What this means is if you want a widget to have a dash in it then you have to revert to using the exports['my-widget'] = MyWidget; syntax.

It is important to understand that in ES2016 the default export is not supported in TiddlyWiki. This is mostly because the core code expects specific properties to be attached to the exports variable. Bable's export conversion plays well with this except with the default export.

Classes

In the example of a widget ES2016 plays well with class inheritance. To contrast the typical Widget definition would look something like this:

function MyWidget() {
  Widget.call(this);
}
MyWidget.prototype = new Widget();
MyWidget.prototype.render = function(parent, nextSibling) {…};
// And so on…

With classes this ceremony can be tightened up:

class MyWidget extends Widget {
  render(parent, nextSibling) {…}
  // And so on…
}

With classes one could eliminate much of the Widget.execute() cruft using getters. I found this to be more readable then the typical mass assignment to this. It gave me the added benefit of allowing calculations in properties that normally would have conflated the execute() method. For example developing a compound property like so:

class NameWidget extends Widget {
  get title() { return this.getAttribute('title'); }
  get firstName() { return this.getAttribute('first'); }
  get lastName() { return this.getAttribute('last'); }
  get fullName() { return `${this.title}. ${this.firstName} ${this.lastName}`; }
}

Non Class Modules

For non class modules you can use the export keyword. Here is a simple Startup Module:

export function startup() {
  // Do stuff here
}

Or in the case of a Macro:

export const name = 'my-macro';
export const params = {};
export function run() {…}

Polyfills

ES2015 comes with some features that are part of the JavaScript core objects. These are not supported by all browsers. To use these features in most browsers you will need a polyfill. Babel has a polyfill package that you can include. See Adding Babel Polyfill to TiddlyWiki for how to accomplish this.

Example

Here is an example ES2015 plugin/widget that will show the time and update it:


/*\
title: $:/plugins/sukima/clock-widget.js
type: application/javascript
module-type: widget

A updating time stamp

\*/
import { widget as Widget } from '$:/core/modules/widgets/widget.js';

class ClockWidget extends Widget {
  constructor(parseTreeNode, options) {
    super(parseTreeNode, options);
    this.logger = new $tw.utils.Logger('clock-widget');
  }

  render(parent, nextSibling) {
    if (!$tw.browser) { return; }
    this.logger.log('Rendering clock DOM nodes');
    this.computeAttributes()
    this.parentDomNode = parent;
    this.domNode = $tw.utils.domMaker('div', {
      document: this.document,
      class: 'tc-clock-widget'
    });
    parent.insertBefore(this.domNode, nextSibling);
    this.tick();
  }

  tick() {
    this.logger.log('Tick!');
    if (!document.contains(this.domNode)) {
      // Apparently the widget was removed from the DOM. Do some clean up.
      return this.stop();
    }
    this.start();
    this.domNode.innerHTML = this.dateString;
  }

  start() {
    if (!this.clockTicker) {
      this.logger.log('Starting clock');
      this.clockTicker = setInterval(this.tick.bind(this), 1000);
    }
  }

  stop() {
    this.logger.log('Stopping clock');
    clearInterval(this.clockTicker);
    this.clockTicker = null;
  }

  get dateString() {
    const format = 'DDth MMM YYYY at hh12:0mm:0ss am';
    return $tw.utils.formatDateString(new Date(), format);
  }
}

export { ClockWidget as clock };

Adding an extra space at the top causes Babel's output the preamble tiddler comment without any obscene indenting. Although it doesn't affect TiddlyWiki any, when reading the output it can be confusing when the tiddler information is rendered off the screen to the right.

Using Modules to build a Single File Application

8th July 2014 at 9:02am

TW is built up from the micro kernel and uses the module mechanism to provide various ways of loading and saving tiddlers, including the ability to load and save to a single HTML file. Furthermore a developer can extend the application by providing modules with a specific module-type. TW searches for modules with these specific module-types and handles them accordingly.

The last sequence of the boot kernel is to execute startup modules. One of these startup modules ("load-modules") is responsible for registering some modules with specific module types at the right place. For example, the methods exported by wikimethod modules are put in \textit{\$tw.Wiki.prototype}. Other startup modules build up the initial UI and link events to certain modules.

Using Notifications

Overview

The notifier module is part of TiddlyWiki's suite of built-in utilities. It displays the text of a tiddler as a notification that briefly appears at the top right top of the screen. They are automatically dismissed after a few seconds.

Notifications are an easy and visual way to notify the user of key information that is transient – in other words, it shouldn't matter whether the user misses the notification (modals or alerts that require manual dismissal should be used in these cases).

An example is the Saved wiki notification seen each time the "save" button is clicked in the single file configuration of TiddlyWiki.

The notification module makes it easy to display such notifcations, including all the heavy lifting of the notification creation, appending it to the DOM, animating it and finally removing it from the DOM.

Parameters

The notifier module only has one method (display(title,options)) that accepts two parameters:

  • title: The title of an existing tiddler
    • If the tiddler does not exist, then no notification will take place
    • The content of this tiddler will be transcluded into the notification body
  • Options: An object containing a variables property that will be applied in the tiddler transclusion

Usage

A notifier instance is availably directly on the global $tw object:

$tw.notifier.display("$:/language/Notifications/Save/Starting");

Using TiddlyWiki as a library in another Node.js application

Node.js applications can include TiddlyWiki as a library so that they can use wikitext rendering.

See the demo at https://github.com/TiddlyWiki/TiddlyWiki5DemoApp

Widget `destroy` method examples

1st June 2023 at 12:50pm

When using a v-dom library

Virtual DOM libraries manages its internal state and apply state to DOM periodically, this is so called "controlled" component. When Tiddlywiki remove a DOM element controlled by a v-dom library, it may throws error.

So when creating a plugin providing v-dom library binding, you need to tell v-dom library (for example, React.js) the DOM element is removed. We will use destroy method for this.

  render() {
    // ...other render related code
    if (this.root === undefined || this.containerElement === undefined) {
      // initialize the v-dom library
      this.root = ReactDom.createRoot(document.createElement('div'));
    }
  }

  destroy() {
    // end the lifecycle of v-dom library
    this.root && this.root.unmount();
  }

The destroy method will be called by parent widget. If you widget don't have any child widget, you can just write your own tear down logic. If it may have some child widget, don't forget to call original destroy method in the Widget class to destroy children widgets.

Widget.prototype.destroy();
this.root && this.root.unmount();
/** if you are using ESNext
super.destroy();
this.root?.unmount();
*/

Widget attributes demo I

4th February 2019 at 3:15am

[ { "title": "$:/DefaultTiddlers", "text": "[[hello world widget]]" } ] [ { "title": "hello-attribute.js", "text": "/*\\\n\nHello, World widget\n\n\\*/\n(function() {\n\n/*jslint node: true, browser: true */\n/*global $tw: false */\n\"use strict\";\n\nvar Widget = require(\"$:/core/modules/widgets/widget.js\").widget;\n\nvar MyWidget = function(parseTreeNode, options) {\n\tthis.initialise(parseTreeNode, options);\n};\n\n/*\nInherit from the base widget class\n*/\nMyWidget.prototype = new Widget();\n\n/*\nRender this widget into the DOM\n*/\nMyWidget.prototype.render = function(parent, nextSibling) {\n\tthis.parentDomNode = parent;\n\tthis.computeAttributes();\n\tvar message = this.getAttribute(\"message\", \"World\");\n\tvar textNode = this.document.createTextNode(\"Hello, \" + message + \"!\");\n\tparent.insertBefore(textNode, nextSibling);\n\tthis.domNodes.push(textNode);\n};\n\n/*\nA widget with optimized performance will selectively refresh, but here we refresh always\n*/\nMyWidget.prototype.refresh = function(changedTiddlers) {\n\t// Regenerate and rerender the widget and\n\t// replace the existing DOM node\n\tthis.refreshSelf();\n\treturn true;\n};\n\nexports.hello = MyWidget;\n\n})();\n", "created": "20190204020011193", "modified": "20190204030332147", "module-type": "widget", "tags": "", "type": "application/javascript" } ] [ { "title": "hello world widget", "text": "\n```\n<$hello/>\n```\n\nRenders as:\n\n<$hello/>\n\n---\n\n```\n<$hello message=\"pale blue dot\"/>\n```\n\nRenders as:\n\n<$hello message=\"pale blue dot\"/>\n\n---\n\n<$edit-text focus=yes tiddler=test tag=input/>\n\n```\n<$hello message={{test!!text}}/>\n```\n\nRenders as:\n\n<$hello message={{test!!text}}/>\n\n" } ] [ { "title": "test", "text": "Alice" } ]

Widget attributes demo II

5th February 2019 at 2:50am

[ { "title": "$:/DefaultTiddlers", "text": "[[hello world widget]]" } ] [ { "title": "hello-attribute-optimized.js", "text": "/*\\\n\nHello, World widget\n\n\\*/\n(function() {\n\n/*jslint node: true, browser: true */\n/*global $tw: false */\n\"use strict\";\n\nvar Widget = require(\"$:/core/modules/widgets/widget.js\").widget;\n\nvar MyWidget = function(parseTreeNode, options) {\n\tthis.initialise(parseTreeNode, options);\n};\n\n/*\nInherit from the base widget class\n*/\nMyWidget.prototype = new Widget();\n\n/*\nRender this widget into the DOM\n*/\nMyWidget.prototype.render = function(parent, nextSibling) {\n\tthis.parentDomNode = parent;\n\tthis.computeAttributes();\n\tvar message = this.getAttribute(\"message\", \"World\");\n\tvar textNode = this.document.createTextNode(\"Hello, \" + message + \"!\");\n\tparent.insertBefore(textNode, nextSibling);\n\tthis.domNodes.push(textNode);\n};\n\n/*\nRefresh if the attribute value changed since render\n*/\nMyWidget.prototype.refresh = function(changedTiddlers) {\n\t// Find which attributes have changed\n\tvar changedAttributes = this.computeAttributes();\n\tif (changedAttributes.message) {\n\t\tthis.refreshSelf();\n\t\treturn true;\n\t} else {\n\t\treturn false;\n\t}\n};\n\nexports.hello = MyWidget;\n\n})();\n", "created": "20190205024846183", "modified": "20190205025110882", "module-type": "widget", "tags": "", "type": "application/javascript" } ] [ { "title": "hello world widget", "text": "\n```\n<$hello/>\n```\n\nRenders as:\n\n<$hello/>\n\n---\n\n```\n<$hello message=\"pale blue dot\"/>\n```\n\nRenders as:\n\n<$hello message=\"pale blue dot\"/>\n\n---\n\n<$edit-text focus=yes tiddler=test tag=input/>\n\n```\n<$hello message={{test!!text}}/>\n```\n\nRenders as:\n\n<$hello message={{test!!text}}/>\n\n" } ] [ { "title": "test", "text": "Alice" } ]

Widget attributes tutorial part I

5th February 2019 at 2:35am

So far none of the widgets we've implemented have had any code for handling widget attributes A vast majority of useful widgets will need to support attributes in order to make them useful.

As an example, let's change the Hello World widget so it can greet not just the world, but whoever/whatever the user wants. To do that we can add support for an attribute named what and call it like <$hello what="pale blue dot"/>.

The tiddlywiki widget class provides methods computeAttributes and getAttribute which can together be used by the widget code to discover what values are passed into the widget.

The computeAttributes and getAttribute methods can be called like this (the second parameter to getAttribute will be used as the default value if the user doesn't provide that attribute in the widget call):

this.computeAttributes();
var message = this.getAttribute("message", "World");

Then the message variable can be used to construct the Hello XXX string:

var textNode = this.document.createTextNode("Hello, " + message + "!");

The original hello.js code only implements a render method. The refresh method is not needed because the output from the widget can never be different...it will always be "Hello, World!".

Even with a message attribute, you might think the render by itself is enough. After all, the value of the input parameter message can only be changed by modifying the wiki text which means the tiddler will be redisplayed from scratch.

However, tiddlywiki has a syntax which allows parameter values to vary without modifying the wiki text. See https://tiddlywiki.com/#Widgets%20in%20WikiText for details. As one example, if the widget were called like <$hello message={{MyTiddler!!field}}/>, then every time the field field of MyTiddler were modified, only the refresh method would be called. Therefore, in order to get the widget display to update, the refresh method needs to be implemented.

If the computeAttributes and getAttribute calls are placed in the render method then we can implement a performance unoptimized version of refresh as was done in Widget refresh tutorial part II:

/*
A widget with optimized performance will selectively refresh, but here we refresh always
*/
MyWidget.prototype.refresh = function(changedTiddlers) {
	// Regenerate and rerender the widget and
	// replace the existing DOM node
	this.refreshSelf();
	return true;
};

The full code can be seen at hello-attribute.js and here is the result (Widget attributes demo I):

[ { "title": "$:/DefaultTiddlers", "text": "[[hello world widget]]" } ] [ { "title": "hello-attribute.js", "text": "/*\\\n\nHello, World widget\n\n\\*/\n(function() {\n\n/*jslint node: true, browser: true */\n/*global $tw: false */\n\"use strict\";\n\nvar Widget = require(\"$:/core/modules/widgets/widget.js\").widget;\n\nvar MyWidget = function(parseTreeNode, options) {\n\tthis.initialise(parseTreeNode, options);\n};\n\n/*\nInherit from the base widget class\n*/\nMyWidget.prototype = new Widget();\n\n/*\nRender this widget into the DOM\n*/\nMyWidget.prototype.render = function(parent, nextSibling) {\n\tthis.parentDomNode = parent;\n\tthis.computeAttributes();\n\tvar message = this.getAttribute(\"message\", \"World\");\n\tvar textNode = this.document.createTextNode(\"Hello, \" + message + \"!\");\n\tparent.insertBefore(textNode, nextSibling);\n\tthis.domNodes.push(textNode);\n};\n\n/*\nA widget with optimized performance will selectively refresh, but here we refresh always\n*/\nMyWidget.prototype.refresh = function(changedTiddlers) {\n\t// Regenerate and rerender the widget and\n\t// replace the existing DOM node\n\tthis.refreshSelf();\n\treturn true;\n};\n\nexports.hello = MyWidget;\n\n})();\n", "created": "20190204020011193", "modified": "20190204030332147", "module-type": "widget", "tags": "", "type": "application/javascript" } ] [ { "title": "hello world widget", "text": "\n```\n<$hello/>\n```\n\nRenders as:\n\n<$hello/>\n\n---\n\n```\n<$hello message=\"pale blue dot\"/>\n```\n\nRenders as:\n\n<$hello message=\"pale blue dot\"/>\n\n---\n\n<$edit-text focus=yes tiddler=test tag=input/>\n\n```\n<$hello message={{test!!text}}/>\n```\n\nRenders as:\n\n<$hello message={{test!!text}}/>\n\n" } ] [ { "title": "test", "text": "Alice" } ]

The third example above is the only one which requires the refresh method in order to behave properly.

Widget attributes tutorial part II

17th February 2019 at 1:21am

This example will build on the previous one. The only modification will be to add a check to the refresh method. The refreshSelf will only be called if a change to the attributes is detected.

The computeAttributes method returns a Javascript object containing properties for each attribute which has changed. So a check like if (changedAttributes.attr1 || changedAttributes.attr2 || changedAttributes.attr3) etc. can be used to detect the change. See the refresh method in $:/core/modules/widgets/view.js for an example showing the check for multiple attributes.

For this example, message is the only attribute implemented.

/*
Refresh if the attribute value changed since render
*/
MyWidget.prototype.refresh = function(changedTiddlers) {
	// Find which attributes have changed
	var changedAttributes = this.computeAttributes();
	if (changedAttributes.message) {
		this.refreshSelf();
		return true;
	} else {
		return false;
	}
};

The full code can be seen at hello-attribute-optimized.js and here is the result (Widget attributes demo II):

[ { "title": "$:/DefaultTiddlers", "text": "[[hello world widget]]" } ] [ { "title": "hello-attribute-optimized.js", "text": "/*\\\n\nHello, World widget\n\n\\*/\n(function() {\n\n/*jslint node: true, browser: true */\n/*global $tw: false */\n\"use strict\";\n\nvar Widget = require(\"$:/core/modules/widgets/widget.js\").widget;\n\nvar MyWidget = function(parseTreeNode, options) {\n\tthis.initialise(parseTreeNode, options);\n};\n\n/*\nInherit from the base widget class\n*/\nMyWidget.prototype = new Widget();\n\n/*\nRender this widget into the DOM\n*/\nMyWidget.prototype.render = function(parent, nextSibling) {\n\tthis.parentDomNode = parent;\n\tthis.computeAttributes();\n\tvar message = this.getAttribute(\"message\", \"World\");\n\tvar textNode = this.document.createTextNode(\"Hello, \" + message + \"!\");\n\tparent.insertBefore(textNode, nextSibling);\n\tthis.domNodes.push(textNode);\n};\n\n/*\nRefresh if the attribute value changed since render\n*/\nMyWidget.prototype.refresh = function(changedTiddlers) {\n\t// Find which attributes have changed\n\tvar changedAttributes = this.computeAttributes();\n\tif (changedAttributes.message) {\n\t\tthis.refreshSelf();\n\t\treturn true;\n\t} else {\n\t\treturn false;\n\t}\n};\n\nexports.hello = MyWidget;\n\n})();\n", "created": "20190205024846183", "modified": "20190205025110882", "module-type": "widget", "tags": "", "type": "application/javascript" } ] [ { "title": "hello world widget", "text": "\n```\n<$hello/>\n```\n\nRenders as:\n\n<$hello/>\n\n---\n\n```\n<$hello message=\"pale blue dot\"/>\n```\n\nRenders as:\n\n<$hello message=\"pale blue dot\"/>\n\n---\n\n<$edit-text focus=yes tiddler=test tag=input/>\n\n```\n<$hello message={{test!!text}}/>\n```\n\nRenders as:\n\n<$hello message={{test!!text}}/>\n\n" } ] [ { "title": "test", "text": "Alice" } ]

The visible behavior here should be identical to the unoptimized behavior of the previous tutorial.

Widget refresh demo I

29th October 2022 at 7:48pm

[ { "title": "$:/DefaultTiddlers", "text": "[[tiddler field widget]]" } ] [ { "title": "test", "text": "type new text here" } ] [ { "title": "tiddlerfield-norefresh.js", "text": "/*\\\n\nHello, World widget\n\n\\*/\n(function() {\n\n/*jslint node: true, browser: true */\n/*global $tw: false */\n\"use strict\";\n\nvar Widget = require(\"$:/core/modules/widgets/widget.js\").widget;\n\nvar MyWidget = function(parseTreeNode, options) {\n\tthis.initialise(parseTreeNode, options);\n};\n\n/*\nInherit from the base widget class\n*/\nMyWidget.prototype = new Widget();\n\n/*\nRender this widget into the DOM\n*/\nMyWidget.prototype.render = function(parent, nextSibling) {\n\tthis.parentDomNode = parent;\n\tvar text = this.wiki.getTiddlerText(\"test\", \"<empty>\")\n\tvar textNode = this.document.createTextNode(text);\n\tparent.insertBefore(textNode, nextSibling);\n\tthis.domNodes.push(textNode);\n};\n\nexports.tiddlerfield = MyWidget;\n\n})();\n", "created": "20190201233714872", "modified": "20190202030615781", "module-type": "widget", "tags": "", "type": "application/javascript" } ] [ { "title": "tiddler field widget", "text": "\n<$edit-text focus=yes tiddler=test tag=input/>\n<$button set=\"!!refresh\" setTo={{test}}>Force refresh</$button>\n<$list filter=\"[{!!refresh}]\">\n\n<div>\n<div style=\"display:inline-block;width: 49%;vertical-align: text-top;word-wrap: break-word;}\">\n\n```\n<$tiddlerfield/>\n```\n\nRenders as:\n\n<$tiddlerfield/>\n</div>\n<div style=\"display:inline-block;width: 49%;vertical-align: text-top;word-wrap: break-word;}\">\n\n```\n<$view tiddler=\"test\"/>\n```\n\nRenders as:\n\n<$view tiddler=\"test\"/>\n</div>\n</div>\n</$list>\n" } ]

Widget refresh demo II

17th February 2019 at 12:55am

[ { "title": "$:/DefaultTiddlers", "text": "[[tiddler field widget]]" } ] [ { "title": "test", "text": "type new text here" } ] [ { "title": "tiddlerfield.js", "text": "/*\\\n\nHello, World widget\n\n\\*/\n(function() {\n\n/*jslint node: true, browser: true */\n/*global $tw: false */\n\"use strict\";\n\nvar Widget = require(\"$:/core/modules/widgets/widget.js\").widget;\n\nvar MyWidget = function(parseTreeNode, options) {\n\tthis.initialise(parseTreeNode, options);\n};\n\n/*\nInherit from the base widget class\n*/\nMyWidget.prototype = new Widget();\n\n/*\nRender this widget into the DOM\n*/\nMyWidget.prototype.render = function(parent, nextSibling) {\n\tthis.parentDomNode = parent;\n\tvar text = this.wiki.getTiddlerText(\"test\", \"<empty>\")\n\tvar textNode = this.document.createTextNode(text);\n\tparent.insertBefore(textNode, nextSibling);\n\tthis.domNodes.push(textNode);\n};\n\n/*\nA widget with optimized performance will selectively refresh, but here we refresh always\n*/\nMyWidget.prototype.refresh = function(changedTiddlers) {\n\t// Regenerate and rerender the widget and\n\t// replace the existing DOM node\n\tthis.refreshSelf();\n\treturn true;\n};\n\nexports.tiddlerfield = MyWidget;\n\n})();\n", "created": "20190202032530728", "modified": "20190202032700995", "module-type": "widget", "tags": "", "type": "application/javascript" } ] [ { "title": "tiddler field widget", "text": "\n<$edit-text focus=yes tiddler=test tag=input/>\n \n<div>\n<div style=\"display:inline-block;width: 49%;vertical-align: text-top;word-wrap: break-word;}\">\n\n```\n<$tiddlerfield/>\n```\n\nRenders as:\n\n<$tiddlerfield/>\n</div>\n<div style=\"display:inline-block;width: 49%;vertical-align: text-top;word-wrap: break-word;}\">\n\n```\n<$view tiddler=\"test\"/>\n```\n\nRenders as:\n\n<$view tiddler=\"test\"/>\n</div>\n" } ]

Widget refresh demo III

16th February 2019 at 7:19pm

[ { "title": "$:/DefaultTiddlers", "text": "[[refresh count widget]]" } ] [ { "title": "test", "text": "Text field of tiddler='test'" } ] [ { "title": "refreshcount.js", "text": "/*\\\n\nwidget to count the number of times this widget refreshes\n\n\\*/\n(function() {\n\n/*jslint node: true, browser: true */\n/*global $tw: false */\n\"use strict\";\n\nvar Widget = require(\"$:/core/modules/widgets/widget.js\").widget;\n\nvar MyWidget = function(parseTreeNode, options) {\n\tthis.refreshCount = 0;\n\tthis.initialise(parseTreeNode, options);\n};\n\n/*\nInherit from the base widget class\n*/\nMyWidget.prototype = new Widget();\n\n/*\nRender this widget into the DOM\n*/\nMyWidget.prototype.render = function(parent, nextSibling) {\n\tthis.parentDomNode = parent;\n\tvar textNode = this.document.createTextNode(this.refreshCount + \" refreshes\");\n\tparent.insertBefore(textNode, nextSibling);\n\tthis.domNodes.push(textNode);\n};\n\nMyWidget.prototype.refresh = function(changedTiddlers) {\n\t// Regenerate and rerender the widget and replace the existing DOM node\n\tthis.refreshCount++;\n\tthis.refreshSelf();\n\treturn true;\n};\n\nexports.refreshcount = MyWidget;\n\n})();\n", "created": "20190201005026324", "modified": "20190202143451303", "module-type": "widget", "tags": "", "type": "application/javascript" } ] [ { "title": "refresh count widget", "text": "\n\n*<$button set=\"test!!test\" setTo=\"hello\">Modify a different tiddler</$button>\n*<$button set=\"!!test\" setTo=\"hello\">Modify this tiddler</$button>\n*<$edit-text focus=yes tiddler=test tag=input/>\n\n<div>\n<div style=\"display:inline-block;width: 49%;vertical-align: text-top;word-wrap: break-word;}\">\n\n```\n<$refreshcount/>\n```\n\nRenders as:\n\n<$refreshcount/>\n</div>\n\n<div style=\"display:inline-block;width: 49%;vertical-align: text-top;word-wrap: break-word;}\">\n\n```\n<$list filter=\"[[test]get[text]]\">\n<<currentTiddler>><br>\n<$refreshcount/>\n</$list>\n```\n\nRenders as:\n\n<$list filter=\"[[test]get[text]]\">\n<<currentTiddler>><br>\n<$refreshcount/>\n</$list>\n" } ] </div> </div>

Widget refresh tutorial part I

29th October 2022 at 8:35pm

But what if we want to display dynamic content? How can we display information and keep it up to date as it changes? Let's display the content of a tiddler field.

The tiddlerfield-norefresh.js which defines the tiddlerfield widget is almost the same as hello.js except for this part:

MyWidget.prototype.render = function(parent,nextSibling) {
	this.parentDomNode = parent;
	var text = this.wiki.getTiddlerText("test", "<empty>")
	var textNode = this.document.createTextNode(text);
	parent.insertBefore(textNode,nextSibling);
	this.domNodes.push(textNode);
};

Instead of creating the text dom node from a static string, the text field of the test tiddler is used. This is similar to using the view widget like this: <$view tiddler="test"/>

Here's how it looks (see Widget refresh demo I to look at the code):

[ { "title": "$:/DefaultTiddlers", "text": "[[tiddler field widget]]" } ] [ { "title": "test", "text": "type new text here" } ] [ { "title": "tiddlerfield-norefresh.js", "text": "/*\\\n\nHello, World widget\n\n\\*/\n(function() {\n\n/*jslint node: true, browser: true */\n/*global $tw: false */\n\"use strict\";\n\nvar Widget = require(\"$:/core/modules/widgets/widget.js\").widget;\n\nvar MyWidget = function(parseTreeNode, options) {\n\tthis.initialise(parseTreeNode, options);\n};\n\n/*\nInherit from the base widget class\n*/\nMyWidget.prototype = new Widget();\n\n/*\nRender this widget into the DOM\n*/\nMyWidget.prototype.render = function(parent, nextSibling) {\n\tthis.parentDomNode = parent;\n\tvar text = this.wiki.getTiddlerText(\"test\", \"<empty>\")\n\tvar textNode = this.document.createTextNode(text);\n\tparent.insertBefore(textNode, nextSibling);\n\tthis.domNodes.push(textNode);\n};\n\nexports.tiddlerfield = MyWidget;\n\n})();\n", "created": "20190201233714872", "modified": "20190202030615781", "module-type": "widget", "tags": "", "type": "application/javascript" } ] [ { "title": "tiddler field widget", "text": "\n<$edit-text focus=yes tiddler=test tag=input/>\n<$button set=\"!!refresh\" setTo={{test}}>Force refresh</$button>\n<$list filter=\"[{!!refresh}]\">\n\n<div>\n<div style=\"display:inline-block;width: 49%;vertical-align: text-top;word-wrap: break-word;}\">\n\n```\n<$tiddlerfield/>\n```\n\nRenders as:\n\n<$tiddlerfield/>\n</div>\n<div style=\"display:inline-block;width: 49%;vertical-align: text-top;word-wrap: break-word;}\">\n\n```\n<$view tiddler=\"test\"/>\n```\n\nRenders as:\n\n<$view tiddler=\"test\"/>\n</div>\n</div>\n</$list>\n" } ]

Notice if you change the text in the input box, the output from the tiddlerfield widget doesn't change, but the output of the view widget does. Only after the Force refresh button is clicked does the output of tiddlerfield update to match the input box contents.

What's going on here? The render method of the widget code is only called by tiddlywiki core when the widget is first created. After that, it isn't called again unless the widget is completely destroyed and then created again.

The tiddlywiki ViewWidget has a properly written refresh method so typing in the input box will cause its content to update. However, the tiddlerfield widget does not have a refresh method at all. It has no way of being notified that the test tiddler content has changed. Its output will not change until the Force refresh button is clicked.

See the next example for an implementation of the refresh method for the tiddlerfield widget.

The code for the refresh button looks like this:

<$button set="!!refresh" setTo={{test}}>Force refresh</$button>

and the widgets are enclosed in a list widget like this:

<$list filter="[{!!refresh}]">...</$list>

When the button is clicked the field refresh in the containing tiddler is modified and it causes the children of the list widget to be created from scratch. The render method is called and the output of the tiddlerfield widget updates.

Widget refresh tutorial part II

17th February 2019 at 1:43am

This example is like Widget refresh tutorial part I except the widget output will be automatically refreshed when the tiddler field changes.

tiddlerfield.js is the same as tiddlerfield-norefresh.js, except this refresh method is added:

/*
A widget with optimized performance will selectively refresh, but here we refresh always
*/
MyWidget.prototype.refresh = function(changedTiddlers) {
	// Regenerate and rerender the widget and
	// replace the existing DOM node
	this.refreshSelf();
	return true;
};

The refreshSelf method called above is implemented by the core widget class and it takes care of cleaning the old dom node and calling the render function.

Here is the result (Widget refresh demo II):

[ { "title": "$:/DefaultTiddlers", "text": "[[tiddler field widget]]" } ] [ { "title": "test", "text": "type new text here" } ] [ { "title": "tiddlerfield.js", "text": "/*\\\n\nHello, World widget\n\n\\*/\n(function() {\n\n/*jslint node: true, browser: true */\n/*global $tw: false */\n\"use strict\";\n\nvar Widget = require(\"$:/core/modules/widgets/widget.js\").widget;\n\nvar MyWidget = function(parseTreeNode, options) {\n\tthis.initialise(parseTreeNode, options);\n};\n\n/*\nInherit from the base widget class\n*/\nMyWidget.prototype = new Widget();\n\n/*\nRender this widget into the DOM\n*/\nMyWidget.prototype.render = function(parent, nextSibling) {\n\tthis.parentDomNode = parent;\n\tvar text = this.wiki.getTiddlerText(\"test\", \"<empty>\")\n\tvar textNode = this.document.createTextNode(text);\n\tparent.insertBefore(textNode, nextSibling);\n\tthis.domNodes.push(textNode);\n};\n\n/*\nA widget with optimized performance will selectively refresh, but here we refresh always\n*/\nMyWidget.prototype.refresh = function(changedTiddlers) {\n\t// Regenerate and rerender the widget and\n\t// replace the existing DOM node\n\tthis.refreshSelf();\n\treturn true;\n};\n\nexports.tiddlerfield = MyWidget;\n\n})();\n", "created": "20190202032530728", "modified": "20190202032700995", "module-type": "widget", "tags": "", "type": "application/javascript" } ] [ { "title": "tiddler field widget", "text": "\n<$edit-text focus=yes tiddler=test tag=input/>\n \n<div>\n<div style=\"display:inline-block;width: 49%;vertical-align: text-top;word-wrap: break-word;}\">\n\n```\n<$tiddlerfield/>\n```\n\nRenders as:\n\n<$tiddlerfield/>\n</div>\n<div style=\"display:inline-block;width: 49%;vertical-align: text-top;word-wrap: break-word;}\">\n\n```\n<$view tiddler=\"test\"/>\n```\n\nRenders as:\n\n<$view tiddler=\"test\"/>\n</div>\n" } ]

And now any typing into the input box will cause both the tiddlerfield and the view widget output to refresh immediately.

Note this is a naive version of refresh. It unconditionally refreshes itself. This is far from optimal since the refresh method for all visible widgets is called every time the tiddler store changes. But the way we've defined our widget, the output ONLY depends on the tiddler titled text.

In tiddlywiki the tiddler store is used for everything and almost any interaction will result in an update to the store. This means almost any interaction will cause the refresh method to be called. If you type into the search box, for example, the tiddlerfield widget will be refreshed with every keystroke.

Adding and removing dom elements is a relatively expensive operation, so if someone has used the list widget to create a few hundred instances of this widget, then such tiddlywiki interactions might gain a noticable lag. Therefore, it usually makes sense to avoid modifying the dom when possible by writing a smarter refresh method.

Exercise - change the refresh method above to only call refreshSelf when the changedTiddlers input array contains test as one of its entries (Hint: see the refresh method in $:/core/modules/widgets/view.js for an example).

Widget refresh tutorial part III

29th October 2022 at 8:10pm

This tutorial is intended to demonstrate a few examples of when calls to the refresh method happen vs. when a widget is recreated from scratch.

This is accomplished with a refreshcount.js widget which sets a counter to zero when the widget is created and increments the counter every time the refresh method is called. Here is the full code:

/*\

widget to count the number of times this widget refreshes

\*/
(function() {

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

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

var MyWidget = function(parseTreeNode, options) {
	this.refreshCount = 0;
	this.initialise(parseTreeNode, options);
};

/*
Inherit from the base widget class
*/
MyWidget.prototype = new Widget();

/*
Render this widget into the DOM
*/
MyWidget.prototype.render = function(parent, nextSibling) {
	this.parentDomNode = parent;
	var textNode = this.document.createTextNode(this.refreshCount + " refreshes");
	parent.insertBefore(textNode, nextSibling);
	this.domNodes.push(textNode);
};

MyWidget.prototype.refresh = function(changedTiddlers) {
	// Regenerate and rerender the widget and replace the existing DOM node
	this.refreshCount++;
	this.refreshSelf();
	return true;
};

exports.refreshcount = MyWidget;

})();

These are the key parts of the code from above:

this.refreshCount = 0;
this.document.createTextNode(this.refreshCount + " refreshes");
this.refreshCount++;

In the following example (see Widget refresh demo III for the code), two instances of the refreshcount widget are created. One at the top level and the other inside a list widget whose filter results depend on the value in the text field of the test widget. The tiddler store can be modified in a few ways via two buttons and an input box:

[ { "title": "$:/DefaultTiddlers", "text": "[[refresh count widget]]" } ] [ { "title": "test", "text": "Text field of tiddler='test'" } ] [ { "title": "refreshcount.js", "text": "/*\\\n\nwidget to count the number of times this widget refreshes\n\n\\*/\n(function() {\n\n/*jslint node: true, browser: true */\n/*global $tw: false */\n\"use strict\";\n\nvar Widget = require(\"$:/core/modules/widgets/widget.js\").widget;\n\nvar MyWidget = function(parseTreeNode, options) {\n\tthis.refreshCount = 0;\n\tthis.initialise(parseTreeNode, options);\n};\n\n/*\nInherit from the base widget class\n*/\nMyWidget.prototype = new Widget();\n\n/*\nRender this widget into the DOM\n*/\nMyWidget.prototype.render = function(parent, nextSibling) {\n\tthis.parentDomNode = parent;\n\tvar textNode = this.document.createTextNode(this.refreshCount + \" refreshes\");\n\tparent.insertBefore(textNode, nextSibling);\n\tthis.domNodes.push(textNode);\n};\n\nMyWidget.prototype.refresh = function(changedTiddlers) {\n\t// Regenerate and rerender the widget and replace the existing DOM node\n\tthis.refreshCount++;\n\tthis.refreshSelf();\n\treturn true;\n};\n\nexports.refreshcount = MyWidget;\n\n})();\n", "created": "20190201005026324", "modified": "20190202143451303", "module-type": "widget", "tags": "", "type": "application/javascript" } ] [ { "title": "refresh count widget", "text": "\n\n*<$button set=\"test!!test\" setTo=\"hello\">Modify a different tiddler</$button>\n*<$button set=\"!!test\" setTo=\"hello\">Modify this tiddler</$button>\n*<$edit-text focus=yes tiddler=test tag=input/>\n\n<div>\n<div style=\"display:inline-block;width: 49%;vertical-align: text-top;word-wrap: break-word;}\">\n\n```\n<$refreshcount/>\n```\n\nRenders as:\n\n<$refreshcount/>\n</div>\n\n<div style=\"display:inline-block;width: 49%;vertical-align: text-top;word-wrap: break-word;}\">\n\n```\n<$list filter=\"[[test]get[text]]\">\n<<currentTiddler>><br>\n<$refreshcount/>\n</$list>\n```\n\nRenders as:\n\n<$list filter=\"[[test]get[text]]\">\n<<currentTiddler>><br>\n<$refreshcount/>\n</$list>\n" } ] </div> </div>

  • Modify a different tiddler - every time this button is clicked, both counters increment, indicating the refresh method is being called
  • Modify this tiddler - clicking this button modifies the tiddler itself. In earlier TiddlyWiki versions that caused both widgets to be recreated from scratch and the counters are thereby reset to zero. Since version 5.2.0 modifying another field in this tiddler does not cause the widgets to be recreated and the counters are not reset.
  • Text field of tiddler='test' - typing text into the input box causes the counter in the standalone refreshcount widget to be incremented. But the other instance of the widget is embedded inside a list widget whose filter output depends on the value of the tiddler field which is being modified. This causes it to recreate its children from scratch and the counter is reset every time. So with every keystroke, the counter on the left is incremented, but the counter on the right goes to/stays at zero.

WidgetModules

1st June 2023 at 1:06pm

Introduction

Widget modules are used as part of the RenderingMechanism to implement each type of renderable entity. As well as the widgets that are familiar to end users, the following primitives are also implemented as widgets:

  • HTML text nodes
  • HTML element nodes
  • HTML entities

All widgets inherit from a base widget class that is defined in $:/core/modules/widgets/widget.js.

Widget Properties

The following widget properties are defined by the core. The lifecycle of a widget object is largely a matter of maintaining the consistency of these internal properties in the face of external state changes. Individual widgets usually add their own additional properties too.

NameDescription
parseTreeNodeReference to the parse tree node corresponding to this widget
wikiReference to the Wiki object associated with this widget
variablesHashmap of information about each widget variable (see below)
parentWidgetReference to the parent widget
documentReference to the document object associated with this widget. Usually either the browser global document variable or a reference to the FakeDomMechanism's $tw.fakeDocument
attributesHashmap of information about each attribute attached to this widget (see below)
childrenArray of child widgets
domNodesFor widgets that directly generate DOM nodes, an array of the generated nodes
eventListenersArray of event listener definitions

Widget Variables

The widget variables defined on a widget are stored in a hashmap of the variable name. The hashmap contains:

  • name: name of variable
  • params: array of parameters for macro definitions, each {name: "<name>", default: "<optionaldefault>"}
  • value: string value of variable

Widget Attributes

The widget attributes associated with a widget are stored in a hashmap of the attribute name. The hashmap contains an object that describes the attribute value. Currently three attribute value types are supported:

  • Strings: {type: "string", value: "<value>"}
  • Tiddler text reference indirection: {type: "indirect", textReference: "<textref>"}
  • Macro invocation: {type: "macro", value: {name: "<macroname>", params: [{name: "<paramname>", value: "<paramvalue>"}, ... ]}

Widget Event Listeners

The event listeners attached to a widget are stored as a hashmap by event type. Each value is a handler function that accepts a single event parameter.

Widget methods

The individual methods defined by the widget object are documented in the source code of $:/core/modules/widgets/widget.js. Here we give an overview of the overall lifecycle, and how the methods fit together

Widget initialise method

Widget widgetClasses method

Widget render method

Widget execute method

Widget getVariable method

Widget substituteVariableParameters method

Widget substituteVariableReferences method

Widget evaluateMacroModule method

Widget setVariable method

Widget hasVariable method

Widget getStateQualifier method

Widget computeAttributes method

Widget hasAttribute method

Widget getAttribute method

Widget assignAttributes method

Widget makeChildWidgets method

Widget makeChildWidget method

Widget renderChildren method

Widget addEventListeners method

Widget addEventListener method

Widget dispatchEvent method

Widget refresh method

Widget refreshSelf method

Widget refreshChildren method

Widget findNextSiblingDomNode method

Widget findFirstDomNode method

Widget destroy method

New in: 5.3.0 Gets called when any parent widget is unmounted from the widget tree.

Examples

Widget removeChildDomNodes method

Widgets

17th July 2014 at 6:15pm

When the WikiText has been transformed into a parse-tree the next step is to transform this parse-tree into a widget-tree. We talked about widgets as parts of the WikiText markup but in fact each node of the parse-tree is transformed to a widget object. The core plug-in provides a basic widget object which gets the parse node it should represent and a DOM node. The widget then must create the DOM structure which represents the parse node and add it to the provided DOM node. A LinkWidget for example would create the DOM node for a <a>...</a> tag and put it in the provided DOM node. When a widget gets a parse node with child nodes it must create the corresponding child widgets.

All this functionality is basically provided with the base widget. But when creating the widget for a parse node it loads additional modules with module-type: widget. These modules can export multiple widgets which extend the base widget and can override it's methods like the rendering and refresh functions. As described in Parser each parse node contains a "type" property. This type directly determines which widget module is used.

{type: "text", text: <string>} - a text node

Would be transformed to a TextWidget. (Note that the TextWidget module exports a property "text".)

So in fact when talking about widgets in WikiText, they are not a feature added to WikiText but the ability to directly use a specific widget instead of the parser choosing a widget type.

In the beginning we talked about widgets and how they enable dynamic behaviour. But up until now we only described how widgets create DOM nodes from parse nodes. Widgets add dynamic behaviour in two ways.

Messages

Messages are events that are triggered by the user. They are generated by widgets for example when the user clicks on a ButtonWidget. Each message has a type property like "tm-delete-tiddler" and a parameter.

{type: "tm-delete-tiddler", param: "MyOldTiddler"}

When such a message is created by a widget it sends the message to it's parent widget which sends it to it's own parent widget and so on. On this way each widget can try to dispatch the message. This concept is realised in the base widget object. It provides a function dispatchEvent(message) which is called by the children to bubble the message up the widget tree. Another function addEventListener(type,listener) can be used to bind a function to a specific message type. If the listener returns false, the message is send to the parent widget.

The TiddlyWiki core plug-in handles a lot of messages in the NavigatorWidget.

Selective Update

With Messages a widget is able to put some kind of events into the TiddlyWiki application which is one part of the dynamic behaviour of widgets. The other part is selective updating. Widgets are often dependant on tiddler states. The ListWidget for example can be configured to list all tiddlers which are tagged with "important". Now, when such a tiddler is changed and it's "important" tag is removed, this change should reflect in the ListWidget. To allow widgets to react on such changes, each widget can provide a function refresh(changedTiddlers). The RootWidget is registered to the wiki store, using the Event Mechanism. When an change event occurs it starts to call the refresh function of its children with a list of the changed tiddlers. Each widget can then decide if it has to change or re-render its DOM representation and call the refresh function of its own children. This way every time a tiddler or the wiki store itself changes, each widget can instantly react on these changes or ignore them.

Another way of updating are text reference attributes (text references explained in Transclusion and TextReference):

{type: "indirect", textReference: <textReference>}

When a widget got a attribute which is a text reference and the refresh function is called, it can check if the text reference references a changed tiddler and update accordingly.

Nearly every state of the UI is stored in tiddlers. A search mechanism for example would use a EditTextWidget which shows an text input field for the search string and a ListWidget to show the results. The EditTextWidget is bound to a tiddler $:/temp/search. Meaning when editing the text in the input field the tiddlers content is changed to this text. On the other hand when the tiddler changes the RootWidget is notified. It then starts calling its childrens refresh methods. Eventually the refresh method of the EditTextWidget is called and it can re-render itself if necessary. This way TiddlyWiki can re-use an already existing data structure which is not only convenient because we don't need to introduce an additional structure but tiddlers managed in the wiki store are already a pretty powerful data structure, supporting an Event Mechanism (so we don't need an additional observer pattern), Caching, Metadata by additional tiddler fields and a way to obtain specific tiddlers.

WidgetSubclassingMechanism

17th March 2019 at 12:19pm

Introduction

The widget subclassing mechanism allows widgets to be subclassed with additional methods and properties. The subclassed widgets can either overwrite the existing definition of the baseclass widget, or it can be made available as a new widget

Widget subclasses are defined in modules of module-type widget-subclass. They should export the following properties:

  • baseClass: the name of the widget class being subclassed
  • name: (optional) the name to be used for the subclassed widget (defaults to overwriting the baseclass widget)
  • constructor: constructor function for creating the widget object
  • prototype: object whose properties are added to the subclass

Example

Here is an example of a subclass of the checkbox widget that adds logging to the event handler:

/*\
title: $:/my-customised-checkbox-widget.js
type: application/javascript
module-type: widget-subclass

Widget base class

\*/
(function(){

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

exports.baseClass = "checkbox"; // Extend the <$checkbox> widget

// Specify a different name to make the subclass available as a new widget instead of overwriting the baseclass:
// exports.name = "my-enhanced-checkbox";

exports.constructor = function(parseTreeNode,options) {
	this.initialise(parseTreeNode,options);
};

exports.prototype = {};

exports.prototype.handleChangeEvent = function(event) {
	// Call the base class handleChangeEvent function
	Object.getPrototypeOf(Object.getPrototypeOf(this)).handleChangeEvent.call(this,event);
	// Print our message
	console.log("Checkbox status:",this.inputDomNode.checked);
};

})();

wikimethod module type

2nd October 2020 at 1:01am

The startup module $:/core/modules/startup/load-modules.js in the TiddlyWiki core plug-in loads all modules of typewikimethod and puts their exported functions into the wiki store.

WikiRuleModules

21st January 2021 at 5:46am

WikiRuleModules have a module-type of wikirule, and may have following types: pragma, block, inline. Modules of these types encapsulate the logic of individual parsing rules used by the WikiParser engine. For example, entity.js rule module identifies references to HTML entities by matching the pattern &<chars>;.

Pragma rules are applied at the start of a block of text, and cover definitions and declarations that affect the parsing of the rest of the text. Block rules are only applied at the beginning of a block of wikitext, while inline rules can appear anywhere. The only current example of a pragma rule is for macro definitions.

Examples of block rules:

  • Headings
  • Tables
  • Lists

Examples of inline rules:

  • Entities
  • HTML tags
  • Wiki links

Parser rule modules extend the $tw.WikiParserRule class. This is done by instantiating the class and then copying the exports of the rule module onto the instance. In this way, the parser rule can override the base behaviour of the $tw.WikiParserRule class. In particular, the base class incorporates logic for using regular expressions to match parse rules but this logic could be overridden by a parse rule that wanted to, say, use indexOf() instead of regular expressions.

The standard methods and properties of parser rules are as follows:

  • name: a string containing the name of this parse rule
  • types: an object that defines the module's type. For example, exports.types = {block: true};
  • init(parser): initialisation function called immediately after the constructor with a pointer back to the parser containing this rule
  • findNextMatch(pos): returns the position of the next match after the specified position
  • parse(): parses the most recent match, returning an array of the generated parse tree nodes. Pragma rules don't return parse tree nodes but instead modify the parser object directly (for example, to add local macro definitions)

The built in parser rules use regular expression matching. Such rules can take advantage of the implementation of findNextMatch() in the base $tw.WikiRule class by ensuring that their init() method creates a matchRegExp property containing the regular expression to match. The match property contains the details of the match for use in the parse() method.

WikiText

17th July 2014 at 6:24pm

The WikiText is a markup language, created especially for the requirements of the TiddlyWiki application. It is based on Markdown, but extended with some TiddlyWiki specific features. On one hand its a text-to-HTML conversion language and on the other hand its used to provide the interactive features of TiddlyWiki. The aim of this language is to allow the user of the software to focus on the writing. The WikiText is used to format Tiddlers within the TiddlyWiki application. The tags of the WikiText syntax can be used within the standard text input field. During the saving process these tags renders to HTML elements for example:

WikiText:--- 
Renders as:
HTML:<hr>
WikiText:[img[https://tiddlywiki.com/favicon.ico]]
Renders as: TW
HTML:<img src="https://tiddlywiki.com/favicon.ico">

Furthermore the WikiText is used to access the widgets which are integrated in the application.These widgets are used to enhance the the WikiText with a rich functionality. Widgets are based on the ~HTML-Syntax but always starts with a $.

WikiText:
<$button message="tm-close-tiddler">Close Me!</$button> 

WikiText Markup

17th July 2014 at 9:26pm

The WikiText is a markup language, created especially for the requirements of the TiddlyWiki application. It is based on Markdown, but extended with some TiddlyWiki specific features. On one hand its a text-to-HTML conversion language and on the other hand its used to provide the interactive features of TiddlyWiki. The aim of this language is to allow the user of the software to focus on the writing. The WikiText is used to format Tiddlers within the TiddlyWiki application. The tags of the WikiText syntax can be used within the standard text input field. During the saving process these tags renders to HTML elements for example:

WikiText:--- 
Renders as:
HTML:<hr>
WikiText:[img[https://tiddlywiki.com/favicon.ico]]
Renders as: TW
HTML:<img src="https://tiddlywiki.com/favicon.ico">

Furthermore the WikiText is used to access the widgets which are integrated in the application.These widgets are used to enhance the the WikiText with a rich functionality. Widgets are based on the ~HTML-Syntax but always starts with a $.

WikiText:
<$button message="tm-close-tiddler">Close Me!</$button>