1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2026-02-14 22:19:50 +00:00

Compare commits

..

10 Commits

Author SHA1 Message Date
Jeremy Ruston
4022532455 Merge branch 'master' into dynamic-build-commands 2025-03-21 17:25:50 +00:00
Jeremy Ruston
b2b00324c2 Merge branch 'master' into dynamic-build-commands 2025-02-18 10:49:45 +00:00
Jeremy Ruston
e3714929f9 Add support for a transformFilter for prompts 2024-10-20 13:02:47 +01:00
Jeremy Ruston
b31e2cd0db Mark version in docs 2024-10-20 12:55:21 +01:00
Jeremy Ruston
8b2afd1cc2 Fix bug processing empty string tokens 2024-10-20 12:55:11 +01:00
Jeremy Ruston
bd86723b72 Enhance terminal colour support 2024-10-19 17:05:34 +01:00
Jeremy Ruston
2d89228d25 Example should match the documentation 2024-10-19 16:16:01 +01:00
Jeremy Ruston
5233d72642 Documentation 2024-10-19 16:13:28 +01:00
Jeremy Ruston
4a6501778a Add support for prompts within build scripts 2024-10-19 15:54:31 +01:00
Jeremy Ruston
50118dbe13 Initial commit
This commit adds support for dynamic tokens within build commands. See the tiddlywiki.info file. Also adds an echo command to make debugging easier.

I also intend to add a node type for prompting the user for a string.
2024-10-19 12:26:11 +01:00
10 changed files with 692 additions and 81 deletions

317
.eslintrc.json Normal file
View File

@@ -0,0 +1,317 @@
{
"env": {
"browser": true,
"commonjs": true,
"node": true
},
"extends": [
"eslint:recommended"
],
"globals": {
"$tw": "writable"
},
"parserOptions": {
"ecmaVersion": 5
},
"plugins": [],
"rules": {
"array-bracket-newline": "off",
"array-bracket-spacing": "off",
"array-callback-return": "off",
"array-element-newline": "off",
"arrow-body-style": "error",
"arrow-parens": [
"error",
"as-needed"
],
"arrow-spacing": [
"error",
{
"after": true,
"before": true
}
],
"block-scoped-var": "off",
"block-spacing": "off",
"brace-style": "off",
"callback-return": "off",
"camelcase": "off",
"capitalized-comments": "off",
"class-methods-use-this": "error",
"comma-dangle": "off",
"comma-spacing": "off",
"comma-style": "off",
"complexity": "off",
"computed-property-spacing": "off",
"consistent-return": "off",
"consistent-this": "off",
"curly": "off",
"default-case": "off",
"default-case-last": "error",
"default-param-last": "error",
"dot-location": "off",
"dot-notation": "off",
"eol-last": "off",
"eqeqeq": "off",
"func-call-spacing": "off",
"func-name-matching": "off",
"func-names": "off",
"func-style": "off",
"function-call-argument-newline": "off",
"function-paren-newline": "off",
"generator-star-spacing": "error",
"global-require": "off",
"grouped-accessor-pairs": "error",
"guard-for-in": "off",
"handle-callback-err": "off",
"id-blacklist": "error",
"id-denylist": "error",
"id-length": "off",
"id-match": "error",
"implicit-arrow-linebreak": "error",
"indent": "off",
"indent-legacy": "off",
"init-declarations": "off",
"jsx-quotes": "error",
"key-spacing": "off",
"keyword-spacing": [
"error",
{
"before": true,
"after": false,
"overrides": {
"case": {
"after": true
},
"do": {
"after": true
},
"else": {
"after": true
},
"return": {
"after": true
},
"throw": {
"after": true
},
"try": {
"after": true
},
"catch": {
"after": true
}
}
}
],
"line-comment-position": "off",
"linebreak-style": "off",
"lines-around-comment": "off",
"lines-around-directive": "off",
"lines-between-class-members": "error",
"max-classes-per-file": "error",
"max-depth": "off",
"max-len": "off",
"max-lines": "off",
"max-lines-per-function": "off",
"max-nested-callbacks": "error",
"max-params": "off",
"max-statements": "off",
"max-statements-per-line": "off",
"multiline-comment-style": "off",
"multiline-ternary": "off",
"new-parens": "off",
"newline-after-var": "off",
"newline-before-return": "off",
"newline-per-chained-call": "off",
"no-alert": "off",
"no-array-constructor": "off",
"no-await-in-loop": "error",
"no-bitwise": "off",
"no-buffer-constructor": "off",
"no-caller": "error",
"no-catch-shadow": "off",
"no-confusing-arrow": "error",
"no-console": "off",
"no-constant-condition": [
"error",
{
"checkLoops": false
}
],
"no-constructor-return": "error",
"no-continue": "off",
"no-div-regex": "off",
"no-duplicate-imports": "error",
"no-else-return": "off",
"no-empty-function": "off",
"no-eq-null": "off",
"no-eval": "off",
"no-extend-native": "off",
"no-extra-bind": "off",
"no-extra-label": "off",
"no-extra-parens": "off",
"no-floating-decimal": "off",
"no-implicit-coercion": [
"error",
{
"boolean": false,
"number": false,
"string": false
}
],
"no-implicit-globals": "off",
"no-implied-eval": "error",
"no-inline-comments": "off",
"no-invalid-this": "off",
"no-iterator": "error",
"no-label-var": "off",
"no-labels": "off",
"no-lone-blocks": "off",
"no-lonely-if": "off",
"no-loop-func": "off",
"no-loss-of-precision": "error",
"no-magic-numbers": "off",
"no-mixed-operators": "off",
"no-mixed-requires": "off",
"no-multi-assign": "off",
"no-multi-spaces": "off",
"no-multi-str": "error",
"no-multiple-empty-lines": ["warn", { "max": 4, "maxEOF": 0 }],
"no-native-reassign": "off",
"no-negated-condition": "off",
"no-negated-in-lhs": "error",
"no-nested-ternary": "off",
"no-new": "off",
"no-new-func": "off",
"no-new-object": "off",
"no-new-require": "error",
"no-new-wrappers": "error",
"no-octal-escape": "error",
"no-param-reassign": "off",
"no-path-concat": "error",
"no-plusplus": "off",
"no-process-env": "off",
"no-process-exit": "off",
"no-promise-executor-return": "error",
"no-proto": "off",
"no-restricted-exports": "error",
"no-restricted-globals": "error",
"no-restricted-imports": "error",
"no-restricted-modules": "error",
"no-restricted-properties": "error",
"no-restricted-syntax": "error",
"no-return-assign": "off",
"no-return-await": "error",
"no-script-url": "off",
"no-self-compare": "off",
"no-sequences": "off",
"no-shadow": "off",
"no-spaced-func": "off",
"no-sync": "off",
"no-tabs": "off",
"no-template-curly-in-string": "error",
"no-ternary": "off",
"no-throw-literal": "off",
"no-trailing-spaces": "error",
"no-undef-init": "off",
"no-undefined": "off",
"no-underscore-dangle": "off",
"no-unmodified-loop-condition": "off",
"no-unneeded-ternary": "off",
"no-unreachable-loop": "error",
"no-unused-expressions": "off",
"no-use-before-define": "off",
"no-useless-backreference": "error",
"no-useless-call": "off",
"no-useless-computed-key": "error",
"no-useless-concat": "off",
"no-useless-constructor": "error",
"no-useless-rename": "error",
"no-useless-return": "off",
"no-var": "off",
"no-void": "off",
"no-warning-comments": "off",
"no-whitespace-before-property": "error",
"nonblock-statement-body-position": [
"error",
"any"
],
"object-curly-newline": "off",
"object-curly-spacing": "off",
"object-property-newline": "off",
"object-shorthand": "off",
"one-var": "off",
"one-var-declaration-per-line": "off",
"operator-assignment": "off",
"operator-linebreak": "off",
"padded-blocks": "off",
"padding-line-between-statements": "error",
"prefer-arrow-callback": "off",
"prefer-const": "off",
"prefer-destructuring": "off",
"prefer-exponentiation-operator": "off",
"prefer-named-capture-group": "off",
"prefer-numeric-literals": "error",
"prefer-object-spread": "off",
"prefer-promise-reject-errors": "error",
"prefer-reflect": "off",
"prefer-regex-literals": "off",
"prefer-rest-params": "off",
"prefer-spread": "off",
"prefer-template": "off",
"quote-props": "off",
"quotes": "off",
"radix": "off",
"require-atomic-updates": "error",
"require-await": "error",
"require-jsdoc": "off",
"require-unicode-regexp": "off",
"rest-spread-spacing": "error",
"semi": "off",
"semi-spacing": "off",
"semi-style": "off",
"sort-imports": "error",
"sort-keys": "off",
"sort-vars": "off",
"space-before-blocks": "off",
"space-before-function-paren": "off",
"space-in-parens": "off",
"space-infix-ops": "off",
"space-unary-ops": "off",
"spaced-comment": "off",
"strict": "off",
"switch-colon-spacing": "off",
"symbol-description": "error",
"template-curly-spacing": "error",
"template-tag-spacing": "error",
"unicode-bom": [
"error",
"never"
],
"valid-jsdoc": "off",
"valid-typeof": [
"error",
{
"requireStringLiterals": false
}
],
"vars-on-top": "off",
"wrap-iife": "off",
"wrap-regex": "off",
"yield-star-spacing": "error",
"yoda": "off",
"no-useless-escape": "off",
"no-unused-vars": "off",
"no-empty": "off",
"no-extra-semi": "off",
"no-redeclare": "off",
"no-control-regex": "off",
"no-mixed-spaces-and-tabs": "off",
"no-extra-boolean-cast": "off",
"no-prototype-builtins": "off",
"no-undef": "off",
"no-unreachable": "off",
"no-self-assign": "off"
}
}

View File

@@ -0,0 +1,8 @@
title: $:/language/Help/echo
description: Displays all the passed arguments
Displays all the passed arguments to a command. Useful for debugging.
```
--echo <text> *
```

View File

@@ -13,7 +13,7 @@ The $tw.Commander class is a command interpreter
Parse a sequence of commands
commandTokens: an array of command string tokens
wiki: reference to the wiki store object
streams: {output:, error:}, each of which has a write(string) method
streams: {output:, input:, error:}
callback: a callback invoked as callback(err) where err is null if there was no error
*/
var Commander = function(commandTokens,callback,wiki,streams) {
@@ -61,69 +61,172 @@ Commander.prototype.execute = function() {
this.executeNextCommand();
};
/*
Returns the next string token without consuming it, or null if there are none left. Callback invoked(err,data)
*/
Commander.prototype.peekNextToken = function(callback) {
var self = this;
if(this.nextToken >= this.commandTokens.length) {
return callback(null,null);
} else {
return this.stringifyToken(this.nextToken,function(err,data) {
if(!err) {
// Save the stringified token for next time so that we don't run prompts twice
self.commandTokens[self.nextToken] = data;
}
callback(err,data);
});
}
};
/*
Returns and consumes the next string token, or null if there are none left. Callback invoked(err,data)
*/
Commander.prototype.getNextToken = function(callback) {
if(this.nextToken >= this.commandTokens.length) {
return callback(null,null);
} else {
return this.stringifyToken(this.nextToken++,callback);
}
};
/*
Returns and consumes the string tokens until the end of the token stream or the first token that starts with "--".
Callback invoked(err,tokenArray)
*/
Commander.prototype.getTokensUntilCommand = function(callback) {
var self = this,
tokens = [];
function processNextToken() {
self.peekNextToken(function(err,data) {
if(err) {
return callback(err);
}
if(data === null || data.substr(0,2) === "--") {
return callback(null,tokens);
} else {
self.getNextToken(function(err,data) {
if(err) {
return callback(err);
}
tokens.push(data);
processNextToken();
});
}
});
}
processNextToken();
};
/*
Returns a specified stringified token, or null if the index does not exist. Callback invoked(err,data)
*/
Commander.prototype.stringifyToken = function(index,callback) {
var self = this;
if(index >= this.commandTokens.length) {
return callback(null,null);
} else {
var token = this.commandTokens[index];
if(typeof token === "string") {
return callback(null,token);
} else if(typeof token === "object") {
switch(token.type) {
case "filter":
return callback(null,this.wiki.filterTiddlers(token.text)[0] || "");
case "wikify":
return callback(null,this.wiki.renderText("text/plain","text/vnd.tiddlywiki",token.text,{
parseAsInline: false,
parentWidget: $tw.rootWidget
}));
case "prompt":
$tw.utils.terminalQuestion({
promptText: token.prompt || "Please enter a value",
defaultResult: token["default"] || "",
callback: function(err,userText) {
if(err) {
callback(err);
} else {
if(token.transformFilter) {
userText = self.wiki.filterTiddlers(token.transformFilter,null,self.wiki.makeTiddlerIterator([userText]))[0] || "";
}
callback(null,userText);
}
},
input: self.streams.input,
output: self.streams.output,
});
break;
default:
throw "Unknown dynamic command token type: " + token.type;
}
}
}
};
/*
Execute the next command in the sequence
*/
Commander.prototype.executeNextCommand = function() {
var self = this;
// Invoke the callback if there are no more commands
if(this.nextToken >= this.commandTokens.length) {
this.callback(null);
} else {
// Get and check the command token
var commandName = this.commandTokens[this.nextToken++];
// Get and check the command token
var commandName = this.getNextToken(function(err,commandName) {
if(err) {
return self.callback(err);
}
if(!commandName) {
return self.callback(null);
}
if(commandName.substr(0,2) !== "--") {
this.callback("Missing command: " + commandName);
return self.callback("Missing command: " + commandName);
} else {
commandName = commandName.substr(2); // Trim off the --
// Accumulate the parameters to the command
var params = [];
while(this.nextToken < this.commandTokens.length &&
this.commandTokens[this.nextToken].substr(0,2) !== "--") {
params.push(this.commandTokens[this.nextToken++]);
}
// Get the command info
var command = $tw.commands[commandName],
c,err;
if(!command) {
this.callback("Unknown command: " + commandName);
} else {
if(this.verbose) {
this.streams.output.write("Executing command: " + commandName + " " + params.join(" ") + "\n");
// Get the parameters to the command
self.getTokensUntilCommand(function(err,params) {
if(err) {
return self.callback(err);
}
// Parse named parameters if required
if(command.info.namedParameterMode) {
params = this.extractNamedParameters(params,command.info.mandatoryParameters);
if(typeof params === "string") {
return this.callback(params);
}
}
if(command.info.synchronous) {
// Synchronous command
c = new command.Command(params,this);
err = c.execute();
if(err) {
this.callback(err);
} else {
this.executeNextCommand();
}
var command = $tw.commands[commandName],
c,err;
if(!command) {
self.callback("Unknown command: " + commandName);
} else {
// Asynchronous command
c = new command.Command(params,this,function(err) {
if(self.verbose) {
self.streams.output.write("Executing command: " + commandName + " " + params.join(" ") + "\n");
}
// Parse named parameters if required
if(command.info.namedParameterMode) {
params = self.extractNamedParameters(params,command.info.mandatoryParameters);
if(typeof params === "string") {
return self.callback(params);
}
}
if(command.info.synchronous) {
// Synchronous command
c = new command.Command(params,self);
err = c.execute();
if(err) {
self.callback(err);
} else {
self.executeNextCommand();
}
});
err = c.execute();
if(err) {
this.callback(err);
} else {
// Asynchronous command
c = new command.Command(params,self,function(err) {
if(err) {
self.callback(err);
} else {
self.executeNextCommand();
}
});
err = c.execute();
if(err) {
self.callback(err);
}
}
}
}
});
}
}
});
};
/*

View File

@@ -0,0 +1,32 @@
/*\
title: $:/core/modules/commands/echo.js
type: application/javascript
module-type: command
Command to echo input parameters
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.info = {
name: "echo",
synchronous: true
};
var Command = function(params,commander) {
this.params = params;
this.commander = commander;
};
Command.prototype.execute = function() {
this.commander.streams.output.write(JSON.stringify(this.params,null,4) + "\n");
return null;
};
exports.Command = Command;
})();

View File

@@ -26,7 +26,11 @@ exports.startup = function(callback) {
callback();
},
$tw.wiki,
{output: process.stdout, error: process.stderr}
{
output: process.stdout,
input: process.stdin,
error: process.stderr
}
);
commander.execute();
};

View File

@@ -18,29 +18,127 @@ exports.log = function(text,colour) {
console.log($tw.node ? exports.terminalColour(colour) + text + exports.terminalColour() : text);
};
exports.terminalColour = function(colour) {
/*
Prompts the user for input and handles the input using a callback function. Options:
promptText: Prompt text
defaultResult: Default result returned if the user types enter
callback: callback invoked (err,usertext)
input: optional input stream
output: optional output stream
*/
exports.terminalQuestion = function(options) {
var readline = require("readline"),
promptText = options.promptText,
defaultResult = options.defaultResult,
callback = options.callback,
inputStream = options.input || process.stdin,
outputStream = options.output || process.stdout;
var rl = readline.createInterface({
input: inputStream,
output: outputStream
});
// Handle errors on the input stream
rl.on("error",function(err) {
rl.close();
callback(err); // Pass the error to the callback
});
// Prompt user for input
var prompt = exports.terminalColourText(promptText,["yellow","bold"]);
if(defaultResult) {
prompt += " ";
prompt += exports.terminalColourText("(" + defaultResult + ")",["blue","underline"]);
}
prompt += exports.terminalColourText(": ");
rl.question(prompt,function(input) {
// Use default value for the empty string
if(input === "" && defaultResult) {
input = defaultResult;
}
callback(null,input); // Pass the input to the callback
rl.close();
});
};
/*
Wrap a string in colour codes. Colour can be an array
*/
exports.terminalColourText = function(text,colour) {
if(!$tw.utils.isArray(colour)) {
colour = [colour];
}
$tw.utils.each(colour,function(code) {
text = exports.terminalColour(code) + text + exports.terminalColour(code,true);
});
return text;
};
/*
Returns a terminal colour string. Set "closed" to true to return the closing code
*/
exports.terminalColour = function(colour,closed) {
if(!$tw.browser && $tw.node && process.stdout.isTTY) {
if(colour) {
var code = exports.terminalColourLookup[colour];
if(code) {
return "\x1b[" + code + "m";
return "\x1b[" + code[closed ? 1 : 0] + "m";
}
} else {
return "\x1b[0m"; // Cancel colour
}
return "\x1b[0m"; // Reset
}
return "";
};
exports.terminalColourLookup = {
"black": "0;30",
"red": "0;31",
"green": "0;32",
"brown/orange": "0;33",
"blue": "0;34",
"purple": "0;35",
"cyan": "0;36",
"light gray": "0;37"
// Modifiers
"reset": [0,0],
"bold": [1,22],
"dim": [2,22],
"italic": [3,23],
"underline": [4,24],
"overline": [53,55],
"inverse": [7,27],
"hidden": [8,28],
"strikethrough": [9,29],
// Colours
"black": [30,39],
"red": [31,39],
"green": [32,39],
"yellow": [33,39],
"brown/orange": [33,39], // Backwards compatbility
"blue": [34,39],
"magenta": [35,39], // Backwards compatbility
"purple": [35,39],
"cyan": [36,39],
"white": [37,39],
"gray": [90,39],
"light red": [91,39],
"light green": [92,39],
"light yellow": [93,39],
"light blue": [94,39],
"light magenta": [95,39],
"light purple": [95,39], // Backwards compatbility
"light cyan": [96,39],
"light gray": [97,39],
// Background colours
"black background": [40,49],
"red background": [41,49],
"green background": [42,49],
"yellow background": [43,49],
"brown/orange background": [43,49], // Backwards compatbility
"blue background": [44,49],
"magenta background": [45,49], // Backwards compatbility
"purple background": [45,49],
"cyan background": [46,49],
"white background": [47,49],
"gray background": [100,49],
"light red background": [101,49],
"light green background": [102,49],
"light yellow background": [103,49],
"light blue background": [104,49],
"light magenta background": [105,49],
"light purple background": [105,49], // Backwards compatbility
"light cyan background": [106,49],
"light gray background": [107,49]
};
/*

View File

@@ -0,0 +1,8 @@
caption: echo
created: 20241019150907690
modified: 20241019150907690
tags: Commands
title: EchoCommand
type: text/vnd.tiddlywiki
{{$:/language/Help/echo}}

View File

@@ -6,22 +6,6 @@ tags: Welcome
title: HelloThere
type: text/vnd.tiddlywiki
<$let json=`{"parseTree":{"input":[1,2,3]}}`>
<$log
message="HelloThere"
text1={{{[<json>jsonextract[parseTree]then[...]else[XXXX]]}}}
text2={{{[<json>jsonextract[parsetree]then[...]else[XXXX]]}}}
text3={{{[<json>jsonextract[parseTree]then[...]else[XXXX]]}}}
text4={{{[<json>jsonextract[parsetree]then[...]else[XXXX]]}}}
text5={{{[<json>jsonextract[parseTree]then[...]else[XXXX]]}}}
text6={{{[<json>jsonextract[parsetree]then[...]else[XXXX]]}}}
text7={{{[<json>jsonextract[parseTree]then[...]else[XXXX]]}}}
text8={{{[<json>jsonextract[parsetree]then[...]else[XXXX]]}}}
/>
</$let>
!!.tc-hero-heading ''Welcome to TiddlyWiki, a unique [[non-linear|Philosophy of Tiddlers]] notebook for [[capturing|Creating and editing tiddlers]], [[organising|Structuring TiddlyWiki]] and [[sharing|Sharing your tiddlers with others]] complex information''
Use it to keep your [[to-do list|TaskManagementExample]], to plan an [[essay or novel|"TiddlyWiki for Scholars" by Alberto Molina]], or to organise your wedding. Record every thought that crosses your brain, or build a flexible and responsive website.

View File

@@ -6,12 +6,12 @@ type: text/vnd.tiddlywiki
[[TiddlyWikiFolders]] are configured with a single `tiddlywiki.info` file in the root of the wiki folder. It should contain a JSON object comprising the following properties:
* ''plugins'' - an array of plugin names to be included in the wiki
* ''themes'' - an array of theme names to be included in the wiki
* ''languages'' - an array of language names to be included in the wiki
* ''includeWikis'' - an array of references to external wiki folders to be included in the wiki
* ''build'' - a hashmap of named build targets, each defined by an array of command tokens (see BuildCommand)
* ''config'' - an optional hashmap of configuration options (see below)
* ''plugins'' - optional array of plugin names to be included in the wiki
* ''themes'' - optional array of theme names to be included in the wiki
* ''languages'' - optional array of language names to be included in the wiki
* ''includeWikis'' - optional array of references to external wiki folders to be included in the wiki
* ''build'' - optional hashmap of named build targets, each defined by an array of command tokens (see BuildCommand)
* ''config'' - optional hashmap of configuration options (see below)
!!! ''includeWikis''
@@ -22,8 +22,45 @@ The entries in the ''includeWikis'' array can be either a string specifying the
!!! ''build''
The ''build'' property contains a hashmap of named build targets. Each of the build targets is defined as an array of command tokens.
Note that the build targets of included wikis are merged if a target of that name isn't defined in the current `tiddlywiki.info` file.
Command tokens can be a simple string or <<.from-version "5.3.6">> a command token object with a ''type'' property. The following types are defined to allow the command token to be dynamically defined:
* ''filter'': the value of the first result of a filter expression specified in the ''text'' property of the command token object
* ''wikify'': the result of wikifying a text string specified in the ''text'' property of the command token object
* ''prompt'': the result of prompting the user for a string. The ''prompt'' property of the command token object specifies the textual prompt to be used. The optional ''default'' property specifies a string value to be used if the user presses enter in response to the prompt. The optional ''transformFilter'' property specifies a filter to be applied to the user input to transform it into a command token. The string input by the user is available in the variable `user-input`
The EchoCommand is useful for debugging complex dynamic command tokens.
For example:
```
"build": {
"dynamic": [
"--echo","testing",
"the following argument is wikified",
{
"type": "wikify",
"text": "<<version>>-prod.html"
},
"the following argument is a filter result",
{
"type": "filter",
"text": "[<version>!match[5.3.6-prerelease]then[text/html]else[text/plain]]"
},
"the following argument was provided by the user",
{
"type": "prompt",
"prompt": "Please enter some text and type enter",
"default": "Nothing"
}
],
...
```
!!! ''config''
Configuration options include:

View File

@@ -23,6 +23,26 @@
"languages": [
],
"build": {
"dynamic-example": [
"--echo","testing",
"the following argument is wikified",
{
"type": "wikify",
"text": "<<version>>-prod.html"
},
"the following argument is a filter result",
{
"type": "filter",
"text": "[<version>!match[5.3.6-prerelease]then[text/html]else[text/plain]]"
},
"the following argument was provided by the user",
{
"type": "prompt",
"prompt": "Please enter some text and type enter",
"default": "Nothing",
"transformFilter": "[addprefix[testing ]]"
}
],
"index": [
"--savetiddlers","[tag[external-image]]","images",
"--render","[tag[external-text]]","[encodeuricomponent[]addprefix[text/]addsuffix[.tid]]","text/plain","$:/core/templates/tid-tiddler",