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.

Tip
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');
}

Warning
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/> 12:32, 5th December 2024 12:32, 5th December 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": "12:32, 5th December 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