diff --git a/core/language/en-GB/Buttons.multids b/core/language/en-GB/Buttons.multids
index 83598f410..7cf1c9955 100644
--- a/core/language/en-GB/Buttons.multids
+++ b/core/language/en-GB/Buttons.multids
@@ -18,6 +18,8 @@ CopyToClipboard/Caption: copy to clipboard
CopyToClipboard/Hint: Copy this text to the clipboard
Delete/Caption: delete
Delete/Hint: Delete this tiddler
+DeleteTiddlers/Caption: delete tiddlers
+DeleteTiddlers/Hint: Delete tiddlers
Edit/Caption: edit
Edit/Hint: Edit this tiddler
Encryption/Caption: encryption
diff --git a/core/language/en-GB/Misc.multids b/core/language/en-GB/Misc.multids
index b677494e6..00cb3c99c 100644
--- a/core/language/en-GB/Misc.multids
+++ b/core/language/en-GB/Misc.multids
@@ -8,6 +8,7 @@ CloseAll/Button: close all
ColourPicker/Recent: Recent:
ConfirmCancelTiddler: Do you wish to discard changes to the tiddler "<$text text=<
>/>"?
ConfirmDeleteTiddler: Do you wish to delete the tiddler "<$text text=<>/>"?
+ConfirmDeleteTiddlers: Are you sure you wish to delete <> tiddler(s)?
ConfirmOverwriteTiddler: Do you wish to overwrite the tiddler "<$text text=<>/>"?
ConfirmEditShadowTiddler: You are about to edit a ShadowTiddler. Any changes will override the default system making future upgrades non-trivial. Are you sure you want to edit "<$text text=<>/>"?
ConfirmAction: Do you wish to proceed?
diff --git a/core/ui/AdvancedSearch/FilterButtons/delete.tid b/core/ui/AdvancedSearch/FilterButtons/delete.tid
index 85d9b224e..8d3069b57 100644
--- a/core/ui/AdvancedSearch/FilterButtons/delete.tid
+++ b/core/ui/AdvancedSearch/FilterButtons/delete.tid
@@ -3,7 +3,7 @@ tags: $:/tags/AdvancedSearch/FilterButton
\whitespace trim
<$reveal state="$:/temp/advancedsearch" type="nomatch" text="">
-<$button popup=<> class="tc-btn-invisible">
+<$button tooltip={{$:/language/Buttons/DeleteTiddlers/Hint}} popup=<> class="tc-btn-invisible">
{{$:/core/images/delete-button}}
$button>
$reveal>
@@ -13,13 +13,13 @@ tags: $:/tags/AdvancedSearch/FilterButton
<$set name="resultCount" value="""<$count filter={{$:/temp/advancedsearch}}/>""">
-Are you sure you wish to delete <> tiddler(s)?
+{{$:/language/ConfirmDeleteTiddlers}}
$set>
<$button class="tc-btn">
<$action-deletetiddler $filter={{$:/temp/advancedsearch}}/>
-Delete these tiddlers
+{{$:/language/Buttons/DeleteTiddlers/Hint}}
$button>
diff --git a/editions/test/tiddlers/tests/test-json-filters.js b/editions/test/tiddlers/tests/test-json-filters.js
index 16fdbf652..c892c2419 100644
--- a/editions/test/tiddlers/tests/test-json-filters.js
+++ b/editions/test/tiddlers/tests/test-json-filters.js
@@ -84,23 +84,7 @@ describe("json filter tests", function() {
expect(wiki.filterTiddlers("[{First}jsontype[d],[f],[4]]")).toEqual(["null"]);
});
- it("should support the jsontypecheck operator", function() {
- expect(wiki.filterTiddlers("[{First}jsontypecheck[string]]")).toEqual(['{"a":"one","b":"","c":1.618,"d": {"e": "four","f": ["five","six",true,false,null]}}']);
- expect(wiki.filterTiddlers("[{First}jsonget[]jsontypecheck[object]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}']);
- expect(wiki.filterTiddlers("[{First}jsonget[a]jsontypecheck[string]]")).toEqual(["one"]);
- expect(wiki.filterTiddlers("[{First}jsonget[b]jsontypecheck[string]]")).toEqual([""]);
- expect(wiki.filterTiddlers("[{First}jsonget[c]jsontypecheck[number]]")).toEqual(["1.618"]);
- expect(wiki.filterTiddlers("[{First}jsonget[d]jsontypecheck[object]]")).toEqual(['{"e":"four","f":["five","six",true,false,null]}']);
- expect(wiki.filterTiddlers("[{First}jsonget[d],[e]jsontypecheck[string]]")).toEqual(["four"]);
- expect(wiki.filterTiddlers("[{First}jsonget[d],[f]jsontypecheck[array]]")).toEqual(['["five","six",true,false,null]']);
- expect(wiki.filterTiddlers("[{First}jsonget[d],[f],[0]jsontypecheck[string]]")).toEqual(["five"]);
- expect(wiki.filterTiddlers("[{First}jsonget[d],[f],[1]jsontypecheck[string]]")).toEqual(["six"]);
- expect(wiki.filterTiddlers("[{First}jsonget[d],[f],[2]jsontypecheck[boolean]]")).toEqual(["true"]);
- expect(wiki.filterTiddlers("[{First}jsonget[d],[f],[3]jsontypecheck[boolean]]")).toEqual(["false"]);
- expect(wiki.filterTiddlers("[{First}jsonget[d],[f],[4]jsontypecheck[null]]")).toEqual(["null"]);
- });
-
- it("should support the jsontype operator", function() {
+ it("should support the format:json operator", function() {
expect(wiki.filterTiddlers("[{First}format:json[]]")).toEqual(["{\"a\":\"one\",\"b\":\"\",\"c\":1.618,\"d\":{\"e\":\"four\",\"f\":[\"five\",\"six\",true,false,null]}}"]);
expect(wiki.filterTiddlers("[{First}format:json[4]]")).toEqual(["{\n \"a\": \"one\",\n \"b\": \"\",\n \"c\": 1.618,\n \"d\": {\n \"e\": \"four\",\n \"f\": [\n \"five\",\n \"six\",\n true,\n false,\n null\n ]\n }\n}"]);
expect(wiki.filterTiddlers("[{First}format:json[ ]]")).toEqual(["{\n \"a\": \"one\",\n \"b\": \"\",\n \"c\": 1.618,\n \"d\": {\n \"e\": \"four\",\n \"f\": [\n \"five\",\n \"six\",\n true,\n false,\n null\n ]\n }\n}"]);
diff --git a/editions/tw5.com/tiddlers/filters/examples/format Operator (Examples).tid b/editions/tw5.com/tiddlers/filters/examples/format Operator (Examples).tid
index d7bbc1271..c1d79ea8c 100644
--- a/editions/tw5.com/tiddlers/filters/examples/format Operator (Examples).tid
+++ b/editions/tw5.com/tiddlers/filters/examples/format Operator (Examples).tid
@@ -1,5 +1,5 @@
created: 20201020102735123
-modified: 20210524044020645
+modified: 20220611104737314
tags: [[Operator Examples]] [[format Operator]]
title: format Operator (Examples)
type: text/vnd.tiddlywiki
@@ -18,9 +18,12 @@ Modified date shown as a relative date:
A tiddler title with spaces formatted as a title list:
<<.operator-example 4 """[[Hello There]format:titlelist[]]""">>
-All tiddler titles tagged with <> formatted as a title list :
+All tiddler titles tagged with <> formatted as a title list:
<<.operator-example 5 """[tag[TableOfContents]format:titlelist[]]""">>
+A JSON string formatted as JSON – note how the JSON string is normalised to remove the duplicated properties:
+<<.operator-example 6 """[[{"one":"first","one":"another","two":"second"}]format:json[]]""">>
+
<<.tip "To create a string to save a [[title list|Title List]] into a list field, use `format:titlelist[]` with the [[join operator|join Operator]]">>
<<.operator-example 6 """[tag[TableOfContents]format:titlelist[]join[ ]]""">>
For example, to save titles tagged `TableOfContents` to the titles field of the tiddler [[format titlelist test]]:
diff --git a/editions/tw5.com/tiddlers/filters/format.tid b/editions/tw5.com/tiddlers/filters/format.tid
index 9cc846139..e5b12b134 100644
--- a/editions/tw5.com/tiddlers/filters/format.tid
+++ b/editions/tw5.com/tiddlers/filters/format.tid
@@ -1,6 +1,6 @@
caption: format
created: 20201020100834443
-modified: 20220523075550449
+modified: 20220611104737314
op-input: a [[selection of titles|Title Selection]]
op-output: input strings formatted according to the specified suffix <<.place B>>
op-parameter: optional format string for the formats
@@ -17,9 +17,10 @@ type: text/vnd.tiddlywiki
The suffix <<.place B>> is one of the following supported string formats:
|!Format |!Description |
-|^`date` |The input string is interpreted as a UTC date and displayed according to the DateFormat specified in the optional operator parameter. (Defaults to "YYYY MM DD 0hh:0mm") |
-|^`relativedate` |The input string is interpreted as a UTC date and displayed as the interval from the present instant. Any operator parameters are ignored. |
-|^`titlelist` |<<.from-version "5.2.0">>The input string wrapped in double square brackets if it contains a space. Appropriate for use in a [[title list|Title List]]. |
+|^`date` |The input string is interpreted as a UTC date and displayed according to the DateFormat specified in the optional operator operand. (Defaults to "YYYY MM DD 0hh:0mm") |
+|^`json` |<<.from-version "5.2.4">> The input string is interpreted as JSON and displayed with standard formatting. The optional operator operand specifies the number of spaces to use for indenting, or a string to use for indenting. Nothing is returned if the input string is not valid JSON |
+|^`relativedate` |The input string is interpreted as a UTC date and displayed as the interval from the present instant. Any operator parameters are ignored |
+|^`titlelist` |<<.from-version "5.2.0">> The input string wrapped in double square brackets if it contains a space. Appropriate for use in a [[title list|Title List]]. |
<<.warning """The [[Title List]] format cannot reliably represent items that contain certain specific character sequences such as `]] `. Thus it should not be used where there is a possibility of such sequences occurring.""">>
diff --git a/editions/tw5.com/tiddlers/filters/jsonget.tid b/editions/tw5.com/tiddlers/filters/jsonget.tid
new file mode 100644
index 000000000..dbc247d7b
--- /dev/null
+++ b/editions/tw5.com/tiddlers/filters/jsonget.tid
@@ -0,0 +1,86 @@
+created: 20220611104737314
+modified: 20220611104737314
+tags: [[Filter Operators]] [[JSON Operators]]
+title: jsonget Operator
+caption: jsonget
+op-purpose: retrieve the value of a property from JSON strings
+op-input: a selection of JSON strings
+op-parameter: one or more indexes of the property to retrieve
+op-output: the values of each of the retrieved properties
+
+<<.from-version "5.2.4">> See [[JSON in TiddlyWiki]] for background.
+
+The <<.op jsonget>> operator is used to retrieve values from JSON data. See also the following related operators:
+
+* <<.olink jsontype>> to retrieve the type of a JSON value
+* <<.olink jsonindexes>> to retrieve the names of the fields of a JSON object, or the indexes of a JSON array
+
+Properties within a JSON object are identified by a sequence of indexes. In the following example, the value at `[a]` is `one`, and the value at `[d][f][0]` is `five`.
+
+```
+{
+ "a": "one",
+ "b": "",
+ "c": "three",
+ "d": {
+ "e": "four",
+ "f": [
+ "five",
+ "six",
+ true,
+ false,
+ null
+ ],
+ "g": {
+ "x": "max",
+ "y": "may",
+ "z": "maize"
+ }
+ }
+}
+```
+
+The following examples assume that this JSON data is contained in a variable called `jsondata`.
+
+The <<.op jsonget>> operator uses multiple operands to specify the indexes of the property to retrieve:
+
+```
+[jsonget[a]] --> "one"
+[jsonget[d],[e]] --> "four"
+[jsonget[d],[f],[0]] --> "five"
+```
+
+Indexes can be dynamically composed from variables and transclusions:
+
+```
+[jsonget,{!!field},[0]]
+```
+
+Boolean values and null are returned as normal strings. The <<.olink jsontype>> operator can be used to retrieve a string identifying the original type. Thus:
+
+```
+[jsontype[a]] --> "string"
+[jsontype[d]] --> "object"
+[jsontype[d],[f]] --> "array"
+[jsontype[d],[f],[2]] --> "boolean"
+```
+
+Using the <<.op jsonget>> operator to retrieve an object or an array returns a JSON string of the values. For example:
+
+```
+[jsonget[d],[f]] --> `["five","six",true,false,null]`
+[jsonget[d],[g]] --> `{"x": "max","y": "may","z": "maize"}`
+```
+
+The <<.olink jsonindexes>> operator retrieves the corresponding indexes:
+
+```
+[jsonindexes[d],[f]] --> "0", "1", "2", "3", "4"
+[jsonindexes[d],[g]] --> "x", "y", "z"
+```
+
+A subtlety is that the special case of a single blank operand is used to identify the root object. Thus:
+
+```
+[jsonindexes[]] --> "a", "b", "c", "d"
+```
diff --git a/editions/tw5.com/tiddlers/filters/jsonindexes.tid b/editions/tw5.com/tiddlers/filters/jsonindexes.tid
new file mode 100644
index 000000000..933f0f101
--- /dev/null
+++ b/editions/tw5.com/tiddlers/filters/jsonindexes.tid
@@ -0,0 +1,64 @@
+created: 20220611104737314
+modified: 20220611104737314
+tags: [[Filter Operators]] [[JSON Operators]]
+title: jsonindexes Operator
+caption: jsonindexes
+op-purpose: retrieve the value of a property from JSON strings
+op-input: a selection of JSON strings
+op-parameter: one or more indexes of the property to retrieve
+op-output: the values of each of the retrieved properties
+
+<<.from-version "5.2.4">> See [[JSON in TiddlyWiki]] for background.
+
+The <<.op jsonindexes>> operator is used to retrieve the property names of JSON objects or the index names of JSON arrays. See also the following related operators:
+
+* <<.olink jsonget>> to retrieve the values of a property in JSON data
+* <<.olink jsontype>> to retrieve the type of a JSON value
+
+Properties within a JSON object are identified by a sequence of indexes. In the following example, the value at `[a]` is `one`, and the value at `[d][f][0]` is `five`.
+
+```
+{
+ "a": "one",
+ "b": "",
+ "c": "three",
+ "d": {
+ "e": "four",
+ "f": [
+ "five",
+ "six",
+ true,
+ false,
+ null
+ ],
+ "g": {
+ "x": "max",
+ "y": "may",
+ "z": "maize"
+ }
+ }
+}
+```
+
+The following examples assume that this JSON data is contained in a variable called `jsondata`.
+
+The <<.op jsonindexes>> operator uses multiple operands to specify the indexes of the property to retrieve:
+
+```
+[jsonindexes[d],[f]] --> "0", "1", "2", "3", "4"
+[jsonindexes[d],[g]] --> "x", "y", "z"
+```
+
+Indexes can be dynamically composed from variables and transclusions:
+
+```
+[jsonindexes,{!!field}]
+```
+
+Retrieving the indexes of JSON properties that are not objects or arrays will return nothing.
+
+A subtlety is that the special case of a single blank operand is used to identify the root object. Thus:
+
+```
+[jsonindexes[]] --> "a", "b", "c", "d"
+```
diff --git a/editions/tw5.com/tiddlers/filters/jsontype.tid b/editions/tw5.com/tiddlers/filters/jsontype.tid
new file mode 100644
index 000000000..766757af0
--- /dev/null
+++ b/editions/tw5.com/tiddlers/filters/jsontype.tid
@@ -0,0 +1,73 @@
+created: 20220611104737314
+modified: 20220611104737314
+tags: [[Filter Operators]] [[JSON Operators]]
+title: jsontype Operator
+caption: jsontype
+op-purpose: retrieve the type of a property from JSON strings
+op-input: a selection of JSON strings
+op-parameter: one or more indexes of the property whose type is to be retrieved
+op-output: the types of each of the retrieved properties
+
+<<.from-version "5.2.4">> See [[JSON in TiddlyWiki]] for background.
+
+The <<.op jsontype>> operator is used to retrieve the type of a property in JSON data. See also the following related operators:
+
+* <<.olink jsonget>> to retrieve the values of a property in JSON data
+* <<.olink jsonindexes>> to retrieve the names of the fields of a JSON object, or the indexes of a JSON array
+
+JSON supports the following data types:
+
+* ''string'' - a Unicode string
+* ''number'' - a floating point number
+* ''boolean'' - Boolean value (true or false)
+* ''array'' - an array of values
+* ''object'' - an object of name/value pairs
+* ''null'' - a special type representing a missing value
+
+Properties within a JSON object are identified by a sequence of indexes. In the following example, the value at `[a]` is `one`, and the value at `[d][f][0]` is `five`.
+
+```
+{
+ "a": "one",
+ "b": "",
+ "c": "three",
+ "d": {
+ "e": "four",
+ "f": [
+ "five",
+ "six",
+ true,
+ false,
+ null
+ ],
+ "g": {
+ "x": "max",
+ "y": "may",
+ "z": "maize"
+ }
+ }
+}
+```
+
+The following examples assume that this JSON data is contained in a variable called `jsondata`.
+
+The <<.op jsontype>> operator uses multiple operands to specify the indexes of the property whose type is to be retrieved:
+
+```
+[jsontype[a]] --> "string"
+[jsontype[d]] --> "object"
+[jsontype[d],[f]] --> "array"
+[jsontype[d],[f],[2]] --> "boolean"
+```
+
+Indexes can be dynamically composed from variables and transclusions:
+
+```
+[jsontype,{!!field},[0]]
+```
+
+A subtlety is that the special case of a single blank operand is used to identify the root object. Thus:
+
+```
+[jsontype[]] --> "object"
+```
diff --git a/languages/fr-FR/Buttons.multids b/languages/fr-FR/Buttons.multids
index 058cf8041..426a4c1fb 100644
--- a/languages/fr-FR/Buttons.multids
+++ b/languages/fr-FR/Buttons.multids
@@ -18,6 +18,8 @@ CopyToClipboard/Caption: copier dans le presse-papier
CopyToClipboard/Hint: Copie ce texte dans le presse-papier
Delete/Caption: supprimer
Delete/Hint: Supprime ce tiddler
+DeleteTiddlers/Caption: supprimer les tiddlers
+DeleteTiddlers/Hint: Supprime ces tiddlers
Edit/Caption: éditer
Edit/Hint: Édite ce tiddler
Encryption/Caption: chiffrement
diff --git a/languages/fr-FR/Misc.multids b/languages/fr-FR/Misc.multids
index 2d9c66655..1c0be807f 100644
--- a/languages/fr-FR/Misc.multids
+++ b/languages/fr-FR/Misc.multids
@@ -7,8 +7,9 @@ ClassicWarning/Upgrade/Caption: mettre à jour
CloseAll/Button: tout fermer
ColourPicker/Recent: Récent :
ConfirmCancelTiddler: Souhaitez-vous annuler les modifications apportées au tiddler « <$text text=<>/> » ?
-ConfirmDeleteTiddler: Souhaitez-vous supprimer le tiddler « <$text text=<>/> » ?
-ConfirmOverwriteTiddler: Souhaitez-vous supplanter le tiddler « <$text text=<>/> » ?
+ConfirmDeleteTiddler: Souhaitez-vous supprimer le tiddler « <$text text=<>/> » ?
+ConfirmDeleteTiddlers: Êtes-vous sûr•e de vouloir supprimer <> tiddler(s) ?
+ConfirmOverwriteTiddler: Souhaitez-vous supplanter le tiddler « <$text text=<>/> » ?
ConfirmEditShadowTiddler: Vous êtes sur le point d'éditer un ShadowTiddler. Toute modification supplantera la version par défaut du système, rendant les prochaines mises à jour non-triviales. Êtes-vous sûr(e) de vouloir éditer "<$text text=<>/>"?
ConfirmAction: Souhaitez-vous poursuivre ?
Count: total
diff --git a/languages/zh-Hans/Buttons.multids b/languages/zh-Hans/Buttons.multids
index 380bba3eb..6feef4e92 100644
--- a/languages/zh-Hans/Buttons.multids
+++ b/languages/zh-Hans/Buttons.multids
@@ -18,6 +18,8 @@ CopyToClipboard/Caption: 复制到剪贴板
CopyToClipboard/Hint: 将此文本复制到剪贴板
Delete/Caption: 删除
Delete/Hint: 删除此条目
+DeleteTiddlers/Caption: 删除条目
+DeleteTiddlers/Hint: 删除条目
Edit/Caption: 编辑
Edit/Hint: 编辑此条目
Encryption/Caption: 加密
diff --git a/languages/zh-Hans/Misc.multids b/languages/zh-Hans/Misc.multids
index 7b8edb4b8..567ddfaee 100644
--- a/languages/zh-Hans/Misc.multids
+++ b/languages/zh-Hans/Misc.multids
@@ -8,6 +8,7 @@ CloseAll/Button: 全部关闭
ColourPicker/Recent: 最近︰
ConfirmCancelTiddler: 您确定要放弃对条目 "<$text text=<>/>" 的更改?
ConfirmDeleteTiddler: 您确定要删除条目 "<$text text=<>/>"?
+ConfirmDeleteTiddlers: 您确定要删除 <> 个条目?
ConfirmOverwriteTiddler: 您确定要复写条目 "<$text text=<>/>"?
ConfirmEditShadowTiddler: 您即将要编辑默认条目,任何更改将会复盖默认的系统,使未来的升级不寻常。您确定要编辑 "<$text text=<>/>"?
ConfirmAction: 是否要继续?
diff --git a/languages/zh-Hant/Buttons.multids b/languages/zh-Hant/Buttons.multids
index aa94f7013..2a170ed8e 100644
--- a/languages/zh-Hant/Buttons.multids
+++ b/languages/zh-Hant/Buttons.multids
@@ -18,6 +18,8 @@ CopyToClipboard/Caption: 複製到剪貼簿
CopyToClipboard/Hint: 將此文字複製到剪貼簿
Delete/Caption: 刪除
Delete/Hint: 刪除此條目
+DeleteTiddlers/Caption: 刪除條目
+DeleteTiddlers/Hint: 刪除條目
Edit/Caption: 編輯
Edit/Hint: 編輯此條目
Encryption/Caption: 加密
diff --git a/languages/zh-Hant/Misc.multids b/languages/zh-Hant/Misc.multids
index 3b67b65ba..0269aa7dd 100644
--- a/languages/zh-Hant/Misc.multids
+++ b/languages/zh-Hant/Misc.multids
@@ -8,6 +8,7 @@ CloseAll/Button: 全部關閉
ColourPicker/Recent: 最近︰
ConfirmCancelTiddler: 您確定要放棄對條目 "<$text text=<>/>" 的更改?
ConfirmDeleteTiddler: 您確定要刪除條目 "<$text text=<>/>"?
+ConfirmDeleteTiddlers: 您確定要刪除 <> 個條目?
ConfirmOverwriteTiddler: 您確定要覆寫條目 "<$text text=<>/>"?
ConfirmEditShadowTiddler: 您即將要編輯預設條目,任何更改將會覆蓋預設的系統,使未來的升級不尋常。您確定要編輯 "<$text text=<>/>"?
ConfirmAction: 是否要繼續?
diff --git a/plugins/tiddlywiki/internals/editpreviews/parse-tree.tid b/plugins/tiddlywiki/internals/editpreviews/parse-tree.tid
index aafa30ecf..205ef7cae 100644
--- a/plugins/tiddlywiki/internals/editpreviews/parse-tree.tid
+++ b/plugins/tiddlywiki/internals/editpreviews/parse-tree.tid
@@ -3,13 +3,121 @@ tags: $:/tags/EditPreview
list-after: $:/core/ui/EditTemplate/body/preview/output
caption: parse tree
-\define preview(mode)
-<$wikify name="preview-text" text={{!!text}} type={{!!type}} mode="$mode$" output="parsetree">
-
-
-<$text text=<>/>
-
-
+\whitespace trim
+
+\procedure preview-node-properties(node)
+<$let excludeProperties="text type tag children attributes orderedAttributes">
+<$list filter="[jsonindexes[]] -[subfilter] +[limit[1]]" variable="ignore">
+
+
+<$list filter="[jsonindexes[]] -[subfilter] +[sort[]]" variable="index">
+
+
+<$text text=<>/>
+ |
+
+<$text text={{{ [jsonget] }}}/>
+ |
+
+$list>
+
+
+$list>
+$let>
+\end
+
+\procedure preview-node-attribute-string(attribute)
+<$text text={{{ [jsonget[value]] }}}/>
+\end
+
+\procedure preview-node-attribute-indirect(attribute)
+{{<$text text={{{ [jsonget[textReference]] }}}/>}}
+\end
+
+\procedure preview-node-attribute-macro(attribute)
+<<
+<$text text={{{ [jsonget[value],[name]] }}}/>
+<$list filter="[jsonindexes[value],[params]]" variable="index">
+
+<$list filter="[jsonget[value],[params],,[name]]" variable="ignore">
+<$text text={{{ [jsonget[value],[params],,[name]] }}}/>
+:
+$list>
+<$text text={{{ [jsonget[value],[params],,[value]] }}}/>
+$list>
+>>
+\end
+
+\procedure preview-node-attributes(node)
+<$list filter="[jsonindexes[attributes]limit[1]]" variable="ignore">
+
+
+<$list filter="[jsonindexes[attributes]sort[]]" variable="index">
+
+
+<$text text=<>/>
+ |
+
+<$let type={{{ [jsonget[attributes],,[type]] }}}>
+<$transclude $variable={{{ [match[string]then[preview-node-attribute-string]] :else[match[indirect]then[preview-node-attribute-indirect]] :else[match[macro]then[preview-node-attribute-macro]] }}} attribute={{{ [jsonget[attributes],] }}}/>
+$let>
+ |
+
+$list>
+
+
+$list>
+\end
+
+\procedure preview-node-children(node)
+
+<$transclude $variable="preview-node-properties" node=<>/>
+<$transclude $variable="preview-node-attributes" node=<>/>
+<$transclude $variable="preview-node-list" nodeList={{{ [jsonget[children]] }}}/>
+
+\end
+
+\procedure preview-node-title-widget(node)
+
+
+<$<$text text={{{ [jsonget[type]] }}}/>>
+
+<$transclude $variable="preview-node-children" node=<
>/>
+
+\end
+
+\procedure preview-node-title-element(node)
+
+
+<<$text text={{{ [jsonget[tag]] }}}/>>
+
+<$transclude $variable="preview-node-children" node=<
>/>
+
+\end
+
+\procedure preview-node-title-text(node)
+
+
+"<$text text={{{ [jsonget[text]] }}}/>"
+
+
+\end
+
+\procedure preview-node(node)
+<$let type={{{ [jsonget[type]] }}}>
+<$transclude $variable={{{ [match[element]then[preview-node-title-element]] :else[match[text]then[preview-node-title-text]] :else[[preview-node-title-widget]] }}} node=<>/>
+$let>
+\end
+
+\procedure preview-node-list(nodeList)
+<$list filter="[jsonindexes[]]" variable="index">
+<$transclude $variable="preview-node" node={{{ [jsonget] }}}/>
+$list>
+\end
+
+\procedure preview(mode)
+<$wikify name="preview-json" text={{!!text}} type={{!!type}} mode=<> output="parsetree">
+<$transclude $variable="preview-node-list" nodeList=<>/>
$wikify>
\end
diff --git a/themes/tiddlywiki/vanilla/base.tid b/themes/tiddlywiki/vanilla/base.tid
index cbb8b1784..9c4a342ff 100644
--- a/themes/tiddlywiki/vanilla/base.tid
+++ b/themes/tiddlywiki/vanilla/base.tid
@@ -397,6 +397,7 @@ a.tc-tiddlylink {
font-weight: 500;
color: <>;
-webkit-user-select: inherit; /* Otherwise the draggable attribute makes links impossible to select */
+ -webkit-touch-callout: none; /* Prevents long presses from bringing up a link preview */
}
.tc-sidebar-lists a.tc-tiddlylink {