mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2026-08-20 17:58:54 +00:00
Compare commits
31
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9d5ee0ad7 | ||
|
|
68630c939d | ||
|
|
7efbd01801 | ||
|
|
5143138c16 | ||
|
|
ceb0817176 | ||
|
|
c0b3403f87 | ||
|
|
2ececf21c7 | ||
|
|
9a0bbf831b | ||
|
|
a30854a4c7 | ||
|
|
856057be81 | ||
|
|
748f04c9e3 | ||
|
|
d391595836 | ||
|
|
967140a148 | ||
|
|
95e68b0437 | ||
|
|
8ddede1611 | ||
|
|
ceb200b08c | ||
|
|
00e5f48a59 | ||
|
|
ea0e9105bc | ||
|
|
51459815ba | ||
|
|
8c62935a01 | ||
|
|
839fa2417d | ||
|
|
1b8610e4d8 | ||
|
|
ca063cbe90 | ||
|
|
7d4328b2fb | ||
|
|
1ae224ab74 | ||
|
|
c308fc44c4 | ||
|
|
aa3fb85919 | ||
|
|
bd052a33f8 | ||
|
|
bb766c36c3 | ||
|
|
9c1f69e9c1 | ||
|
|
14b11575d0 |
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
|
||||
title: TiddlyWiki Team
|
||||
tags: Community/Team
|
||||
modified: 20260708095630754
|
||||
created: 20260708095630754
|
||||
leader: @BurningTreeC
|
||||
team:
|
||||
|
||||
The TiddlyDesktop development repository is at https://github.com/TiddlyWiki/TiddlyDesktop
|
||||
@@ -31,13 +31,16 @@ exports.parse = function() {
|
||||
reEnd.lastIndex = this.parser.pos;
|
||||
var match = reEnd.exec(this.parser.source),
|
||||
text,
|
||||
start = this.parser.pos;
|
||||
start = this.parser.pos,
|
||||
textEnd;
|
||||
// Process the text
|
||||
if(match) {
|
||||
text = this.parser.source.substring(this.parser.pos,match.index);
|
||||
textEnd = match.index;
|
||||
this.parser.pos = match.index + match[0].length;
|
||||
} else {
|
||||
text = this.parser.source.substr(this.parser.pos);
|
||||
textEnd = this.parser.sourceLength;
|
||||
this.parser.pos = this.parser.sourceLength;
|
||||
}
|
||||
return [{
|
||||
@@ -47,7 +50,7 @@ exports.parse = function() {
|
||||
type: "text",
|
||||
text: text,
|
||||
start: start,
|
||||
end: this.parser.pos
|
||||
end: textEnd
|
||||
}]
|
||||
}];
|
||||
};
|
||||
|
||||
@@ -32,7 +32,8 @@ exports.parse = function() {
|
||||
this.parser.pos = this.matchRegExp.lastIndex;
|
||||
// Create the link unless it is suppressed
|
||||
if(this.match[0].substr(0,1) === "~") {
|
||||
return [{type: "text", text: this.match[0].substr(1), start: start, end: this.parser.pos}];
|
||||
// Start after the suppressing "~" so the span matches the plain text
|
||||
return [{type: "text", text: this.match[0].substr(1), start: start + 1, end: this.parser.pos}];
|
||||
} else {
|
||||
return [{
|
||||
type: "element",
|
||||
|
||||
@@ -34,7 +34,7 @@ exports.parse = function() {
|
||||
// Parse the filter terminated by a line break
|
||||
var reMatch = /(.*)(?:$|\r?\n)/mg;
|
||||
reMatch.lastIndex = this.parser.pos;
|
||||
var filterStart = this.parser.source;
|
||||
var filterStart = this.parser.pos;
|
||||
var match = reMatch.exec(this.parser.source);
|
||||
this.parser.pos = reMatch.lastIndex;
|
||||
// Parse tree nodes to return
|
||||
|
||||
@@ -27,9 +27,11 @@ Parse the most recent match
|
||||
*/
|
||||
exports.parse = function() {
|
||||
// Get the details of the match
|
||||
var linkText = this.match[0];
|
||||
var linkText = this.match[0],
|
||||
// Start after the suppressing "~" so the span matches the plain text
|
||||
start = this.parser.pos + 1;
|
||||
// Move past the wikilink
|
||||
this.parser.pos = this.matchRegExp.lastIndex;
|
||||
// Return the link without unwikilink character as plain text
|
||||
return [{type: "text", text: linkText.substr(1)}];
|
||||
return [{type: "text", text: linkText.substr(1), start: start, end: this.parser.pos}];
|
||||
};
|
||||
|
||||
@@ -243,8 +243,28 @@ exports.slowInSlowOut = function(t) {
|
||||
};
|
||||
|
||||
exports.copyObjectPropertiesSafe = function(object) {
|
||||
const seen = new Set(),
|
||||
isDOMElement = (value) => value instanceof Node || value instanceof Window;
|
||||
const seen = new Set();
|
||||
|
||||
function isDOMElement(value) {
|
||||
if(!value || typeof value !== "object") {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cross-realm DOM nodes
|
||||
if(typeof value.nodeType === "number" &&
|
||||
typeof value.nodeName === "string") {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Cross-realm Window objects
|
||||
if(value.window === value &&
|
||||
value.document &&
|
||||
value.location) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function safeCopy(obj) {
|
||||
// skip circular references
|
||||
@@ -255,10 +275,6 @@ exports.copyObjectPropertiesSafe = function(object) {
|
||||
if(typeof obj !== "object" || obj === null) {
|
||||
return obj;
|
||||
}
|
||||
// skip DOM elements
|
||||
if(isDOMElement(obj)) {
|
||||
return undefined;
|
||||
}
|
||||
// copy arrays, preserving positions
|
||||
if(Array.isArray(obj)) {
|
||||
return obj.map((item) => {
|
||||
@@ -266,7 +282,11 @@ exports.copyObjectPropertiesSafe = function(object) {
|
||||
return value === undefined ? null : value;
|
||||
});
|
||||
}
|
||||
|
||||
// skip DOM elements
|
||||
if(isDOMElement(obj)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
seen.add(obj);
|
||||
const copy = {};
|
||||
let key,
|
||||
|
||||
@@ -8,9 +8,8 @@ type: text/vnd.tiddlywiki
|
||||
# Ensure the new release banner image is up to date
|
||||
# Update ''master'' with changes from ''tiddlywiki-com''
|
||||
# Verify the version numbers in [[$:/config/OfficialPluginLibrary]] in `core/wiki/config/OfficialPluginLibrary.tid`
|
||||
# Move the latest release note from the prerelease edition into the tw5.com edition
|
||||
# Adjust the release date and the ''released'' field of the latest release tiddler (eg, [[Release 5.1.3]])
|
||||
# Also adjust the github.com comparison link to point to the tag for the new release
|
||||
# Adjust the release date and the ''released'' field of the release tiddler (eg, [[Release 5.1.3]])
|
||||
# Update the ''release-introduction'' definition with the new release text, and if necessary the ''description'' field of the release tiddler
|
||||
# Adjust the tiddler [[TiddlyWiki Archive]] to include the new version number
|
||||
# Ensure [[TiddlyWiki Releases]] has the new version as the default tab
|
||||
# Adjust the modified time of HelloThere
|
||||
@@ -23,7 +22,6 @@ type: text/vnd.tiddlywiki
|
||||
# Run `./bin/readme-bld.sh` to build the readme files
|
||||
# Commit the new readme files to ''master''
|
||||
# Restore `package.json` to the previous version number
|
||||
# Adjust the link for "GitHub for detailed change history of this release" in the release note
|
||||
# Add the credits for the new release banner to the release note, including a link to the GitHub instance of the image from the commit history
|
||||
|
||||
!! Make New Release
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
title: $:/editions/dev/github-fork-ribbon
|
||||
tags: $:/tags/PageTemplate
|
||||
caption: ~GitHub ribbon
|
||||
description: ~GitHub ribbon for tw5.com/dev
|
||||
|
||||
<div class="github-fork-ribbon-wrapper right" style><div class="github-fork-ribbon" style="background-color:#DF4848;"><a href="https://github.com/TiddlyWiki/TiddlyWiki5" target="_blank" rel="noopener noreferrer">Find me on ~GitHub</a></div></div>
|
||||
@@ -2,7 +2,6 @@
|
||||
"description": "Developer documentation from https://tiddlywiki.com/dev/",
|
||||
"plugins": [
|
||||
"tiddlywiki/highlight",
|
||||
"tiddlywiki/nodewebkitsaver",
|
||||
"tiddlywiki/github-fork-ribbon",
|
||||
"tiddlywiki/menubar",
|
||||
"tiddlywiki/internals",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"description": "A step by step introduction to TiddlyWiki",
|
||||
"plugins": [
|
||||
"tiddlywiki/cecily",
|
||||
"tiddlywiki/codemirror",
|
||||
"tiddlywiki/highlight",
|
||||
"tiddlywiki/katex"
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
Fast test runner that boots the test edition and runs selected test specs.
|
||||
Skips the expensive --rendertiddler step from --build index.
|
||||
|
||||
Usage:
|
||||
node test-parser-quick.js [test-files...]
|
||||
|
||||
Examples:
|
||||
node test-parser-quick.js # Run ALL specs
|
||||
node test-parser-quick.js test-wikitext-parser # Run one spec file
|
||||
node test-parser-quick.js test-wikitext-parser test-filters # Run multiple spec files
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var $tw = require("../../boot/boot.js").TiddlyWiki();
|
||||
|
||||
$tw.boot.argv = ["editions/test"];
|
||||
|
||||
// Disable startup modules that aren't needed for tests
|
||||
$tw.boot.disabledStartupModules = [
|
||||
"favicon", "password", "browser-messaging", "info",
|
||||
"render", "rootwidget", "story", "windows"
|
||||
];
|
||||
|
||||
$tw.boot.boot(function() {
|
||||
var args = process.argv.slice(2);
|
||||
var allTests = $tw.wiki.filterTiddlers("[all[tiddlers+shadows]type[application/javascript]tag[$:/tags/test-spec]]");
|
||||
|
||||
// Filter test tiddlers if arguments provided
|
||||
var testsToRun;
|
||||
if(args.length > 0) {
|
||||
testsToRun = allTests.filter(function(title) {
|
||||
return args.some(function(arg) {
|
||||
return title.toLowerCase().indexOf(arg.toLowerCase()) !== -1;
|
||||
});
|
||||
});
|
||||
if(testsToRun.length === 0) {
|
||||
console.error("No test files matched: " + args.join(", "));
|
||||
console.error("Available test files:");
|
||||
allTests.forEach(function(t) { console.error(" " + t); });
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
testsToRun = allTests;
|
||||
}
|
||||
|
||||
// Override the test filter to only include our selected tests
|
||||
var titlesSet = Object.create(null);
|
||||
testsToRun.forEach(function(t) { titlesSet[t] = true; });
|
||||
|
||||
var origFilterTiddlers = $tw.wiki.filterTiddlers.bind($tw.wiki);
|
||||
$tw.wiki.filterTiddlers = function(filterString) {
|
||||
var result = origFilterTiddlers.apply(null, arguments);
|
||||
if(filterString.indexOf("$:/tags/test-spec") !== -1) {
|
||||
return result.filter(function(t) { return titlesSet[t]; });
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
console.log("Running " + testsToRun.length + " of " + allTests.length + " test files");
|
||||
|
||||
// Use the jasmine plugin's own runTests function
|
||||
var jasmine = $tw.modules.execute("$:/plugins/tiddlywiki/jasmine/jasmine-plugin.js");
|
||||
jasmine.runTests(function(err) {
|
||||
if(err) {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,60 @@
|
||||
/*\
|
||||
title: test-parsetree-positions.js
|
||||
type: application/javascript
|
||||
tags: [[$:/tags/test-spec]]
|
||||
|
||||
Regression tests for #9882: wikitext parser rules must emit accurate
|
||||
`start`/`end` source positions on their parse tree nodes. Tooling that maps
|
||||
rendered output back to the source text relies on these offsets.
|
||||
|
||||
\*/
|
||||
|
||||
"use strict";
|
||||
|
||||
describe("Parse tree source position tests (#9882)", function() {
|
||||
|
||||
// Create a wiki
|
||||
var wiki = $tw.test.wiki();
|
||||
|
||||
// Define a parsing shortcut
|
||||
var parse = function(text) {
|
||||
return wiki.parseText("text/vnd.tiddlywiki",text).tree;
|
||||
};
|
||||
|
||||
it("should give inline code runs a text node that spans only the code, not the backticks", function() {
|
||||
// codeinline.js: `code` gives a text node "code" spanning offsets 1 to 5. The closing backtick at 5 is excluded.
|
||||
// Bug: `end` was set to `this.parser.pos`, which sits past the closing backtick, so end was 6 and the span swallowed the backtick.
|
||||
expect(parse("`code`")).toEqual(
|
||||
[ { type: "element", tag: "p", rule: "parseblock", start: 0, end: 6, children: [ { type: "element", tag: "code", rule: "codeinline", start: 0, end: 6, children: [ { type: "text", text: "code", start: 1, end: 5 } ] } ] } ]
|
||||
);
|
||||
// ``a`b`` gives text "a`b" spanning 2 to 5. `end` must be the offset of the closing marker whatever its length.
|
||||
// Bug: `end` was `this.parser.pos` (7), two characters past the code, so it also swallowed the closing ``.
|
||||
expect(parse("``a`b``")).toEqual(
|
||||
[ { type: "element", tag: "p", rule: "parseblock", start: 0, end: 7, children: [ { type: "element", tag: "code", rule: "codeinline", start: 0, end: 7, children: [ { type: "text", text: "a`b", start: 2, end: 5 } ] } ] } ]
|
||||
);
|
||||
});
|
||||
|
||||
it("should start the text node of a suppressed external link after the ~", function() {
|
||||
// extlink.js: ~https://example.com/ emits the plain text "https://example.com/", which spans offsets 1 to 21.
|
||||
// Bug: `start` was the offset of the ~ (0), so the span was one character too wide and began on the ~ that the text omits.
|
||||
expect(parse("~https://example.com/")).toEqual(
|
||||
[ { type: "element", tag: "p", rule: "parseblock", start: 0, end: 21, children: [ { type: "text", text: "https://example.com/", start: 1, end: 21, rule: "extlink" } ] } ]
|
||||
);
|
||||
});
|
||||
|
||||
it("should give a suppressed wikilink's text node source positions", function() {
|
||||
// wikilinkprefix.js: ~SuppressedLink emits the plain text "SuppressedLink", spanning offsets 1 to 15.
|
||||
// Bug: the text node carried no `start`/`end` at all. The parser framework then defaulted `start` to the ~ offset (0).
|
||||
expect(parse("~SuppressedLink")).toEqual(
|
||||
[ { type: "element", tag: "p", rule: "parseblock", start: 0, end: 15, children: [ { type: "text", text: "SuppressedLink", start: 1, end: 15, rule: "wikilinkprefix" } ] } ]
|
||||
);
|
||||
});
|
||||
|
||||
it("should record the filter's start offset for an \\import pragma", function() {
|
||||
// import.js: \import [tag[x]] records the filter value "[tag[x]]" starting at offset 8, right after "\import ".
|
||||
// Bug: `filterStart` was assigned `this.parser.source` (the whole source string) instead of `this.parser.pos`, so `start` was a string, not an offset.
|
||||
expect(parse("\\import [tag[x]]\n")).toEqual(
|
||||
[ { type: "importvariables", rule: "import", start: 0, end: 16, attributes: { filter: { type: "string", value: "[tag[x]]", start: 8, end: 16 } }, children: [] } ]
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,240 @@
|
||||
/*\
|
||||
title: test-utils-copyObjectPropertiesSafe.js
|
||||
type: application/javascript
|
||||
tags: [[$:/tags/test-spec]]
|
||||
|
||||
Tests $tw.utils.copyObjectPropertiesSafe, the root cause of #9869.
|
||||
|
||||
$eventcatcher serialises DOM events via JSON.stringify(copyObjectPropertiesSafe(event)).
|
||||
|
||||
The original bug was caused by instanceof Node/Window being realm-specific.
|
||||
When an event originated from a different browser window, DOM objects from that
|
||||
window were not detected and JSON.stringify() could throw "Illegal invocation".
|
||||
|
||||
The implementation must:
|
||||
- skip DOM nodes and Window objects from other realms
|
||||
- preserve normal event data
|
||||
- preserve CustomEvent.detail payloads
|
||||
- continue copying enumerable properties from non-DOM objects
|
||||
- preserve arrays and break circular references
|
||||
|
||||
The tests use substitutes for foreign DOM objects because headless Node does not
|
||||
provide a second browser realm. These objects model the important characteristics:
|
||||
DOM nodes have nodeType/nodeName, and Window objects have window/self/document.
|
||||
\*/
|
||||
|
||||
"use strict";
|
||||
|
||||
describe("copyObjectPropertiesSafe (#9869)", function() {
|
||||
|
||||
var cops = $tw.utils.copyObjectPropertiesSafe;
|
||||
|
||||
// Simulates a DOM node from another realm.
|
||||
// The important characteristics are:
|
||||
// - nodeType/nodeName identify it as a DOM node
|
||||
// - circular parentNode references resemble real DOM trees
|
||||
function fakeElement(tagName,extra) {
|
||||
var node = $tw.utils.extend({
|
||||
nodeType: 1,
|
||||
nodeName: tagName,
|
||||
tagName: tagName
|
||||
},extra || {});
|
||||
|
||||
node.parentNode = node;
|
||||
return node;
|
||||
}
|
||||
|
||||
// Simulates a Window object from another realm.
|
||||
function fakeWindow() {
|
||||
var win = {
|
||||
document: {},
|
||||
location: {}
|
||||
};
|
||||
|
||||
win.window = win;
|
||||
win.self = win;
|
||||
|
||||
return win;
|
||||
}
|
||||
|
||||
// An event carrying foreign DOM nodes and a Window.
|
||||
function fakeEvent(type,target,extra) {
|
||||
return $tw.utils.extend({
|
||||
type: type,
|
||||
target: target,
|
||||
currentTarget: target,
|
||||
view: fakeWindow(),
|
||||
detail: 0,
|
||||
isTrusted: true
|
||||
},extra || {});
|
||||
}
|
||||
|
||||
|
||||
it("does not throw serialising any event type from a secondary window", function() {
|
||||
var events = [
|
||||
fakeEvent("focusin",fakeElement("INPUT")),
|
||||
fakeEvent("change",fakeElement("SELECT")),
|
||||
fakeEvent("click",fakeElement("BUTTON"),{
|
||||
button: 0,
|
||||
clientX: 5,
|
||||
clientY: 9,
|
||||
relatedTarget: null
|
||||
}),
|
||||
fakeEvent("mouseover",fakeElement("DIV"),{
|
||||
relatedTarget: fakeElement("SPAN")
|
||||
})
|
||||
];
|
||||
|
||||
events.forEach(function(event) {
|
||||
expect(function() {
|
||||
JSON.stringify(cops(event));
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it("drops DOM nodes and Window but keeps serialisable event data", function() {
|
||||
var event = fakeEvent("click",fakeElement("BUTTON"),{
|
||||
button: 0,
|
||||
clientX: 5,
|
||||
clientY: 9
|
||||
});
|
||||
|
||||
var result = JSON.parse(JSON.stringify(cops(event)));
|
||||
|
||||
expect(result.target).toBeUndefined();
|
||||
expect(result.currentTarget).toBeUndefined();
|
||||
expect(result.view).toBeUndefined();
|
||||
|
||||
expect(result.type).toBe("click");
|
||||
expect(result.detail).toBe(0);
|
||||
expect(result.isTrusted).toBe(true);
|
||||
expect(result.button).toBe(0);
|
||||
expect(result.clientX).toBe(5);
|
||||
expect(result.clientY).toBe(9);
|
||||
});
|
||||
|
||||
|
||||
it("preserves nested objects, arrays and circular reference handling", function() {
|
||||
var event = fakeEvent("custom",fakeElement("DIV"),{
|
||||
detail: {
|
||||
nested: {
|
||||
a: 1,
|
||||
b: [2,3]
|
||||
}
|
||||
},
|
||||
path: [
|
||||
fakeElement("DIV"),
|
||||
fakeWindow(),
|
||||
42
|
||||
]
|
||||
});
|
||||
|
||||
event.self = event;
|
||||
|
||||
var result = JSON.parse(JSON.stringify(cops(event)));
|
||||
|
||||
expect(result.detail).toEqual({
|
||||
nested: {
|
||||
a: 1,
|
||||
b: [2,3]
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.path).toEqual([
|
||||
null,
|
||||
null,
|
||||
42
|
||||
]);
|
||||
|
||||
expect(result.self).toBeUndefined();
|
||||
});
|
||||
|
||||
|
||||
it("preserves CustomEvent.detail objects including custom object instances", function() {
|
||||
function DetailObject() {
|
||||
this.name = "example";
|
||||
this.values = [1,2,3];
|
||||
}
|
||||
|
||||
var event = fakeEvent("custom",fakeElement("DIV"),{
|
||||
detail: new DetailObject()
|
||||
});
|
||||
|
||||
var result = JSON.parse(JSON.stringify(cops(event)));
|
||||
|
||||
expect(result.detail).toEqual({
|
||||
name: "example",
|
||||
values: [1,2,3]
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it("preserves CustomEvent.detail objects while dropping DOM objects inside them", function() {
|
||||
var event = fakeEvent("custom",fakeElement("DIV"),{
|
||||
detail: {
|
||||
value: 123,
|
||||
nested: {
|
||||
target: fakeElement("SPAN"),
|
||||
kept: "yes"
|
||||
},
|
||||
items: [
|
||||
fakeElement("BUTTON"),
|
||||
42
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
var result = JSON.parse(JSON.stringify(cops(event)));
|
||||
|
||||
expect(result.detail.value).toBe(123);
|
||||
|
||||
expect(result.detail.nested.target).toBeUndefined();
|
||||
expect(result.detail.nested.kept).toBe("yes");
|
||||
|
||||
expect(result.detail.items).toEqual([
|
||||
null,
|
||||
42
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
it("accepts primitive, array and object arguments", function() {
|
||||
var nullProto = Object.create(null);
|
||||
nullProto.kept = true;
|
||||
|
||||
expect(cops(42)).toBe(42);
|
||||
expect(cops(null)).toBe(null);
|
||||
|
||||
expect(cops([
|
||||
1,
|
||||
"two",
|
||||
{three: 3}
|
||||
])).toEqual([
|
||||
1,
|
||||
"two",
|
||||
{three: 3}
|
||||
]);
|
||||
|
||||
expect(cops({
|
||||
a: 1,
|
||||
b: {
|
||||
c: 2
|
||||
}
|
||||
})).toEqual({
|
||||
a: 1,
|
||||
b: {
|
||||
c: 2
|
||||
}
|
||||
});
|
||||
|
||||
expect(JSON.parse(JSON.stringify(cops({
|
||||
nullProto: nullProto
|
||||
})))).toEqual({
|
||||
nullProto: {
|
||||
kept: true
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,7 +1,8 @@
|
||||
created: 20231005205623086
|
||||
modified: 20250807100434131
|
||||
modified: 20260710090951727
|
||||
tags: About
|
||||
title: TiddlyWiki Archive
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
\procedure versions()
|
||||
5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.1.8 5.1.9
|
||||
@@ -9,7 +10,7 @@ title: TiddlyWiki Archive
|
||||
5.1.20 5.1.21 5.1.22 5.1.23
|
||||
5.2.0 5.2.1 5.2.2 5.2.3 5.2.4 5.2.5 5.2.6 5.2.7
|
||||
5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.3.8
|
||||
5.4.0
|
||||
5.4.0 5.4.1
|
||||
\end
|
||||
|
||||
Older versions of TiddlyWiki are available in the [[archive|https://github.com/TiddlyWiki/tiddlywiki.com-gh-pages/tree/master/archive]]:
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
title: Community Survey 2025
|
||||
created: 20250708130030654
|
||||
modified: 20250826162904085
|
||||
title: Community Survey 2025
|
||||
|
||||
<div style.float="right" style.padding-left="1em">
|
||||
<$image source="Community Survey 2025" alt="Shaping the future of TiddlyWiki with the Community Survey 2025" width="280"/>
|
||||
<$image source="Community Survey 2025 Image" alt="Shaping the future of TiddlyWiki with the Community Survey 2025" width="280"/>
|
||||
</div>
|
||||
|
||||
The core developers work hard year by year to continuously improve ~TiddlyWiki. Part of the satisfaction is that we are not just building software for ourselves, we’re serving the needs of a wider community of users.
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
created: 20150106180000000
|
||||
modified: 20241204085601176
|
||||
list: GroupedLists/Example/TypesTab GroupedLists/Example/ByField GroupedLists/Example/WithSearch GroupedLists/Example/WithTabs GroupedLists/Example/TabsWithSearch GroupedLists/Example/SearchAnotherField GroupedLists/Example/RecentTab
|
||||
modified: 20260729194941115
|
||||
tags: ListWidget Lists
|
||||
title: GroupedLists
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
The following sidebar tabs give examples of grouped lists created by nesting.
|
||||
A grouped list is made by nesting one list inside another. The outer list collects each distinct value of a field, and the inner list finds the tiddlers holding that value.
|
||||
|
||||
!! [[Types Tab|$:/core/ui/MoreSideBar/Types]]
|
||||
TiddlyWiki uses the pattern in two of its sidebar tabs. The [[Types Tab|$:/core/ui/MoreSideBar/Types]] groups tiddlers by their `type` field, and sits under ''More'' in the sidebar. The [[Recent Tab|$:/core/ui/SideBar/Recent]] groups them by day, through the <<.mlink timeline>> macro. Both are reproduced as examples below.
|
||||
|
||||
For the "Types Tab", the outer list filter as shown below selects each discrete value found in the `type` field. The inner list filter selects all the (non-system) tiddlers with that type.
|
||||
!! Interactive Examples
|
||||
|
||||
<<tw-code "$:/core/ui/MoreSideBar/Types">>
|
||||
Each example is a runnable test case carrying its own sample data. Where a field is grouped on, it is named once in <<.def groupField>>, so the grouping can be changed without touching anything else.
|
||||
|
||||
!! [[Recent Tab|$:/core/ui/SideBar/Recent]]
|
||||
<<<
|
||||
<$list filter="[tag[GroupedLists]]">
|
||||
|
||||
The list in the "Recent Tab" is generated using the <<.mlink timeline>> macro. Here, the outer list filter selects each discrete day found in the `modified` field, while the inner list filter selects all the tiddlers dated the same day in the `modified` field.
|
||||
|
||||
<<tw-code "$:/core/macros/timeline">>
|
||||
; <$link/>
|
||||
: {{!!description}}
|
||||
</$list>
|
||||
<<<
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
title: ButtonWidget/Example/Accessibility
|
||||
description: Add accessibility and metadata attributes such as aria-label, role, tabindex and data
|
||||
modified: 20260724235815000
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
|
||||
title: Narrative
|
||||
|
||||
A button can carry accessibility and metadata attributes that pass straight through to the HTML element. They have no visible effect, so this example shows the markup rather than a result:
|
||||
|
||||
* `aria-label` gives an accessible name when the label is only an icon
|
||||
* `role` overrides the implied ARIA role
|
||||
* `tabindex` sets the keyboard focus order
|
||||
* `data-*` attaches custom data attributes for scripts
|
||||
* `selectedAria` chooses which ARIA state `selectedClass` toggles
|
||||
|
||||
Inspect the button in your browser to see the attributes on the element.
|
||||
+
|
||||
title: Output
|
||||
|
||||
<$wikify name="button-accessibility" mode="inline" output="html"
|
||||
text="""<$button aria-label="Close"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
data-action="close"
|
||||
>
|
||||
×
|
||||
</$button>
|
||||
""">
|
||||
<$text text=<<button-accessibility>>/>
|
||||
</$wikify>
|
||||
@@ -0,0 +1,33 @@
|
||||
title: ButtonWidget/Example/Actions
|
||||
description: Run one or more ActionWidgets when the button is clicked, using the preferred actions attribute
|
||||
modified: 20260725015330000
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
|
||||
title: Narrative
|
||||
|
||||
The `actions` attribute holds one or more ActionWidgets that run when the button is clicked. This is the preferred way to give a button its behaviour, because the whole behaviour sits in one place instead of being spread over several attributes.
|
||||
|
||||
A button runs `actions` last, after `to`, `message`, `popup` and `set`. ButtonWidget lists the full order. A single `<$button to="HelloThere">Click Me</$button>` still needs no action string.
|
||||
|
||||
The procedure below runs //two// actions in a single click:
|
||||
|
||||
* one sets a message
|
||||
* the other bumps a counter
|
||||
|
||||
Click ''Greet'' a few times to watch both update.
|
||||
+
|
||||
title: Output
|
||||
|
||||
\procedure greetActions()
|
||||
<$action-setfield $tiddler="$:/state/greeting" text="Hello!"/>
|
||||
<$action-setfield $tiddler="$:/state/greeting-count"
|
||||
text={{{ [[$:/state/greeting-count]get[text]] :else[[0]] +[add[1]] }}}
|
||||
/>
|
||||
\end
|
||||
|
||||
<$button actions=<<greetActions>>>
|
||||
Greet
|
||||
</$button>
|
||||
|
||||
Message: <$text text={{$:/state/greeting}}/> (clicked <$text text={{{ [[$:/state/greeting-count]get[text]] :else[[0]] }}}/> times)
|
||||
@@ -0,0 +1,34 @@
|
||||
title: ButtonWidget/Example/DragAndDrop
|
||||
description: Make a button draggable with dragTiddler or dragFilter
|
||||
modified: 20260724235815000
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
|
||||
title: Narrative
|
||||
|
||||
`dragTiddler` makes the button draggable and sets the single tiddler it carries. `dragFilter` carries a list of tiddlers produced by a filter instead.
|
||||
|
||||
Choose one of the two:
|
||||
|
||||
* `dragTiddler` is a single tiddler title to drag
|
||||
* `dragFilter` is a filter whose results are dragged as a list
|
||||
|
||||
Drag the button onto the drop area to see the title it carries. Drag and drop needs a desktop browser.
|
||||
+
|
||||
title: Output
|
||||
|
||||
\procedure recordDrop()
|
||||
<$action-setfield $tiddler="$:/state/dropped" text=<<actionTiddler>>/>
|
||||
\end
|
||||
|
||||
<$button dragTiddler="HelloThere" class="tc-btn-invisible tc-tiddlylink">
|
||||
Drag me
|
||||
</$button>
|
||||
|
||||
<$droppable actions=<<recordDrop>>>
|
||||
<div style="border: 1px dashed #999; padding: 0.5em; margin-top: 0.5em;">
|
||||
Drop the button here
|
||||
</div>
|
||||
</$droppable>
|
||||
|
||||
Dropped title: <$text text={{$:/state/dropped}}/>
|
||||
@@ -0,0 +1,54 @@
|
||||
title: ButtonWidget/Example/DragFilter
|
||||
description: Drag a list of tiddlers with dragFilter and receive it with listActions
|
||||
modified: 20260725001110000
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
|
||||
title: Narrative
|
||||
|
||||
`dragFilter` makes the button draggable and carries every tiddler the filter returns, where `dragTiddler` carries a single title.
|
||||
|
||||
The drop target chooses how it receives that payload:
|
||||
|
||||
* `actions` runs once per dragged tiddler, with the title in `<<actionTiddler>>`
|
||||
* `listActions` runs once for the whole payload, with all titles in `<<actionTiddlerList>>`
|
||||
|
||||
This test case carries four payload tiddlers tagged `Concepts`. The button drags all four, so the drop area shows the whole list at once. Drag and drop needs a desktop browser.
|
||||
+
|
||||
title: Cascades
|
||||
tags: Concepts
|
||||
|
||||
A cascade is a filter list that is evaluated in turn until one returns a result.
|
||||
+
|
||||
title: ColourPalettes
|
||||
tags: Concepts
|
||||
|
||||
A colour palette is a tiddler holding the named colours used by the user interface.
|
||||
+
|
||||
title: Commands
|
||||
tags: Concepts
|
||||
|
||||
Commands are the operations run by the Node.js server from the command line.
|
||||
+
|
||||
title: Filters
|
||||
tags: Concepts
|
||||
|
||||
A filter is an expression that selects a list of tiddler titles from the wiki.
|
||||
+
|
||||
title: Output
|
||||
|
||||
\procedure recordDropList()
|
||||
<$action-setfield $tiddler="$:/state/dropped-list" text=<<actionTiddlerList>>/>
|
||||
\end
|
||||
|
||||
<$button dragFilter="[tag[Concepts]]" class="tc-btn-invisible tc-tiddlylink">
|
||||
Drag four tiddlers
|
||||
</$button>
|
||||
|
||||
<$droppable listActions=<<recordDropList>>>
|
||||
<div style="border: 1px dashed #999; padding: 0.5em; margin-top: 0.5em;">
|
||||
Drop the button here
|
||||
</div>
|
||||
</$droppable>
|
||||
|
||||
Dropped titles: <$text text={{$:/state/dropped-list}}/>
|
||||
@@ -0,0 +1,30 @@
|
||||
title: ButtonWidget/Example/Message
|
||||
description: Send a widget message with the message and param attributes
|
||||
modified: 20260724235815000
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
|
||||
title: Narrative
|
||||
|
||||
The `message` attribute sends a widget message when the button is clicked. The `param` attribute passes a single value with it.
|
||||
|
||||
The two attributes work together:
|
||||
|
||||
* `message` is the message type to send
|
||||
* `param` is the value delivered with the message
|
||||
|
||||
A `messagecatcher` receives the message here and shows the parameter. Click the button to send it.
|
||||
+
|
||||
title: Output
|
||||
|
||||
\procedure receiveMessage()
|
||||
<$action-setfield $tiddler="$:/state/received" text=<<event-param>>/>
|
||||
\end
|
||||
|
||||
<$messagecatcher $tm-sample-message=<<receiveMessage>>>
|
||||
<$button message="tm-sample-message" param="Hello from param">
|
||||
Send message
|
||||
</$button>
|
||||
</$messagecatcher>
|
||||
|
||||
Received param: <$text text={{$:/state/received}}/>
|
||||
@@ -0,0 +1,42 @@
|
||||
title: ButtonWidget/Example/NewTiddler
|
||||
description: Create a tiddler from a template with the tm-new-tiddler message
|
||||
modified: 20260725002340000
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
|
||||
title: Narrative
|
||||
|
||||
`tm-new-tiddler` is a message the core already handles, so the button needs no catcher of its own.
|
||||
|
||||
* `message` is set to `tm-new-tiddler`
|
||||
* `param` names a template tiddler, and the new tiddler starts out with that template's fields
|
||||
|
||||
This test case carries a `TaskTemplate` payload tiddler holding `tags`, `status` and `priority`. Open the ''TaskTemplate'' tab above to see it.
|
||||
|
||||
The message creates a draft and normally opens it for editing. A test case has no story river, so the table lists the drafts instead. Each click adds one row, and the new tiddler takes the next free title because the previous draft still exists.
|
||||
+
|
||||
title: TaskTemplate
|
||||
tags: Task
|
||||
status: open
|
||||
priority: normal
|
||||
|
||||
Describe the task here.
|
||||
+
|
||||
title: Output
|
||||
|
||||
<$button message="tm-new-tiddler" param="TaskTemplate">
|
||||
New task
|
||||
</$button>
|
||||
|
||||
<table>
|
||||
<tr><th>Click</th><th>New tiddler</th><th>tags</th><th>status</th><th>priority</th></tr>
|
||||
<$list filter="[has[draft.title]sort[draft.title]]" counter="clickNumber">
|
||||
<tr>
|
||||
<td><<clickNumber>></td>
|
||||
<td><$text text={{!!draft.title}}/></td>
|
||||
<td><$text text={{!!tags}}/></td>
|
||||
<td><$text text={{!!status}}/></td>
|
||||
<td><$text text={{!!priority}}/></td>
|
||||
</tr>
|
||||
</$list>
|
||||
</table>
|
||||
@@ -0,0 +1,38 @@
|
||||
created: 20260724134149482
|
||||
description: Toggle a popup with the popup attribute and a companion reveal widget
|
||||
modified: 20260725003054000
|
||||
tags: $:/tags/wiki-test-spec
|
||||
title: ButtonWidget/Example/Popup
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
|
||||
title: Narrative
|
||||
|
||||
The `popup` attribute names a state tiddler that stores the popup coordinates. A companion `reveal` widget reads that state to show or hide the content.
|
||||
|
||||
The button uses these attributes:
|
||||
|
||||
* `popup` is the state tiddler for the popup
|
||||
* `selectedClass` highlights the button while the popup is open
|
||||
* `popupAbsCoords` writes absolute coordinates when set to `yes`, rather than relative ones
|
||||
|
||||
Use `popupTitle` in place of `popup` when the state title should not be read as a text reference. Click the button to toggle the popup.
|
||||
|
||||
The `reveal` widget always creates a DOM element of its own: a `div` in block mode, a `span` in inline mode. The `tag` attribute sets that element explicitly. Wrapping the popup content in a `<div>` would add a second element, so this example sets `tag="div"` and puts the `tc-drop-down` class on the widget itself. The popup is then one element instead of two.
|
||||
+
|
||||
title: Output
|
||||
|
||||
<$button popup="$:/state/popup/demo" selectedClass="tc-selected">
|
||||
Toggle popup
|
||||
</$button>
|
||||
|
||||
<$reveal type="popup"
|
||||
tag="div"
|
||||
state="$:/state/popup/demo"
|
||||
position="belowleft"
|
||||
animate="yes"
|
||||
class="tc-drop-down"
|
||||
>
|
||||
|
||||
This is the popup content.
|
||||
|
||||
</$reveal>
|
||||
@@ -0,0 +1,41 @@
|
||||
title: ButtonWidget/Example/Presentation
|
||||
description: Change the rendered element and appearance with tag, class, style and disabled
|
||||
modified: 20260725003716000
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
|
||||
title: Narrative
|
||||
|
||||
Several attributes control how the button looks and which element it renders:
|
||||
|
||||
* `tag` renders a different HTML element in place of `button`
|
||||
* `class` adds one or more CSS classes
|
||||
* `style` sets a full CSS style string
|
||||
* `disabled` set to `yes` disables the button
|
||||
|
||||
Reach for `class` first and keep the rules in a stylesheet. One class restyles every button that uses it, follows the colour palette and stays under the user's control. A hardcoded `style` overrides the theme and has to be repeated at every button, so use it only when no class can do the job.
|
||||
|
||||
`style` sets the whole style attribute and replaces anything a `style.*` attribute wrote before it, so do not mix the two.
|
||||
+
|
||||
title: Output
|
||||
|
||||
<$button tag="a" class="tc-tiddlylink">
|
||||
tag="a" renders a link element
|
||||
</$button>
|
||||
|
||||
<$button class="tc-btn-big-green">
|
||||
class uses a rule from the stylesheet
|
||||
</$button>
|
||||
|
||||
<$button disabled="yes">
|
||||
disabled="yes"
|
||||
</$button>
|
||||
|
||||
<$button style.color="green">
|
||||
style is the last resort
|
||||
</$button>
|
||||
|
||||
<$button style="font-weight: bold;">
|
||||
style is the last resort
|
||||
</$button>
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
title: ButtonWidget/Example/SelectedState
|
||||
description: Highlight the active button with selectedClass, set, setTo and default used together
|
||||
modified: 20260724235815000
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
|
||||
title: Narrative
|
||||
|
||||
This example combines four attributes so a row of buttons can show which colour is active:
|
||||
|
||||
* `set` and `setTo` write the chosen colour to a state tiddler
|
||||
* `selectedClass` marks the button whose `setTo` matches the current value of `set`
|
||||
* `default` gives the value to compare against while the `set` tiddler does not yet exist
|
||||
|
||||
Because `default` is `red`, the ''Red'' button starts out selected. Click another colour to move the selection.
|
||||
+
|
||||
title: Output
|
||||
|
||||
<!-- The inline <style> below is only for this self-contained test/development example.
|
||||
For production, define styles in a tiddler tagged $:/tags/Stylesheet instead. -->
|
||||
<style>
|
||||
.demo-swatch {
|
||||
padding: 0.2em 0.7em;
|
||||
}
|
||||
.demo-selected {
|
||||
outline: 2px solid #1a73e8;
|
||||
}
|
||||
</style>
|
||||
|
||||
<$button set="$:/state/colour" setTo="red"
|
||||
default="red"
|
||||
class="demo-swatch tc-small-gap-right"
|
||||
selectedClass="demo-selected"
|
||||
>
|
||||
Red
|
||||
</$button>
|
||||
<$button set="$:/state/colour" setTo="green"
|
||||
default="red"
|
||||
class="demo-swatch tc-small-gap-right"
|
||||
selectedClass="demo-selected"
|
||||
>
|
||||
Green
|
||||
</$button>
|
||||
<$button set="$:/state/colour" setTo="blue"
|
||||
default="red"
|
||||
class="demo-swatch tc-small-gap-right"
|
||||
selectedClass="demo-selected"
|
||||
>
|
||||
Blue
|
||||
</$button>
|
||||
|
||||
Selected colour: <$text text={{{ [[$:/state/colour]get[text]] :else[[red]] }}}/>
|
||||
@@ -0,0 +1,24 @@
|
||||
title: ButtonWidget/Example/SetAndSetTo
|
||||
description: Assign a value to a tiddler with the set and setTo attributes used together
|
||||
modified: 20260724235815000
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
|
||||
title: Narrative
|
||||
|
||||
`set` and `setTo` work as a pair to assign a value when the button is clicked:
|
||||
|
||||
* `set` names the storage location, a TextReference
|
||||
* `setTo` is the value written to it
|
||||
|
||||
`set` points at the location //itself//, so it takes no curly brackets, unlike an ordinary transclusion.
|
||||
|
||||
Click a colour to write it to the state tiddler shown below.
|
||||
+
|
||||
title: Output
|
||||
|
||||
<$button set="$:/state/colour" setTo="red">Red</$button>
|
||||
<$button set="$:/state/colour" setTo="green">Green</$button>
|
||||
<$button set="$:/state/colour" setTo="blue">Blue</$button>
|
||||
|
||||
Selected colour: <$text text={{$:/state/colour}}/>
|
||||
@@ -0,0 +1,37 @@
|
||||
title: ButtonWidget/Example/SetField
|
||||
description: Assign a field or index directly with setTitle, setField and setIndex
|
||||
modified: 20260724235815000
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
|
||||
title: Narrative
|
||||
|
||||
The `set` attribute treats its value as a TextReference, which is ambiguous when a title contains `!!` or `##`. The `setTitle` group names the target directly instead.
|
||||
|
||||
The attributes work together:
|
||||
|
||||
* `setTitle` is the tiddler to change, with no TextReference parsing
|
||||
* `setField` is the field to write, defaulting to `text`
|
||||
* `setIndex` writes a data index instead of a field
|
||||
* `setTo` is the value to assign
|
||||
|
||||
Click a button to set the `caption` field, then watch it update below.
|
||||
+
|
||||
title: Output
|
||||
|
||||
<$button setTitle="$:/state/demo"
|
||||
setField="caption"
|
||||
setTo="First"
|
||||
class="tc-small-gap-right"
|
||||
>
|
||||
Set caption to First
|
||||
</$button>
|
||||
<$button setTitle="$:/state/demo"
|
||||
setField="caption"
|
||||
setTo="Second"
|
||||
class="tc-small-gap-right"
|
||||
>
|
||||
Set caption to Second
|
||||
</$button>
|
||||
|
||||
Caption is now: <$text text={{$:/state/demo!!caption}}/>
|
||||
@@ -0,0 +1,173 @@
|
||||
code-body: yes
|
||||
created: 20260728220000000
|
||||
description: Bibliography sample data shared by the GroupedLists examples
|
||||
modified: 20260729180306417
|
||||
title: GroupedLists/Bibliography
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
|
||||
title: 3DWiki2011
|
||||
bibtex-entry-type: InProceedings
|
||||
bibtex-author: 3D Wiki Collective
|
||||
bibtex-title: Spatial Notes in Three Dimensions
|
||||
bibtex-date: 2011
|
||||
|
||||
+
|
||||
title: 3rdWave2014
|
||||
bibtex-entry-type: Article
|
||||
bibtex-author: 3rd Wave Research Group
|
||||
bibtex-title: Note Taking After the Desktop
|
||||
bibtex-date: 2014
|
||||
|
||||
+
|
||||
title: Aronsson2002
|
||||
bibtex-entry-type: InProceedings
|
||||
bibtex-author: Aronsson, Lars
|
||||
bibtex-title: Operation of a Large Scale, General Purpose Wiki Website
|
||||
bibtex-date: 2002
|
||||
|
||||
+
|
||||
title: Atkinson2016
|
||||
bibtex-entry-type: Article
|
||||
bibtex-author: Atkinson, Paul
|
||||
bibtex-title: Digital ethnographies
|
||||
bibtex-date: 2016
|
||||
|
||||
+
|
||||
title: Bakshi2016
|
||||
bibtex-entry-type: Article
|
||||
bibtex-author: Bakshi, Divya
|
||||
bibtex-title: Hypertext and Feminisms: Voicing the Silence
|
||||
bibtex-date: 2016
|
||||
|
||||
+
|
||||
title: Barker2008
|
||||
bibtex-entry-type: InProceedings
|
||||
bibtex-author: Barker, Philip
|
||||
bibtex-title: Using wikis and weblogs to enhance human performance
|
||||
bibtex-date: 2008
|
||||
|
||||
+
|
||||
title: Barker2008a
|
||||
bibtex-entry-type: InProceedings
|
||||
bibtex-author: Barker, Philip
|
||||
bibtex-title: Using Wikis for Knowledge Management
|
||||
bibtex-date: 2008
|
||||
|
||||
+
|
||||
title: Bernstein2016
|
||||
bibtex-entry-type: Book
|
||||
bibtex-author: Bernstein, Mark
|
||||
bibtex-title: Getting Started With Hypertext Narrative
|
||||
bibtex-date: 2016
|
||||
|
||||
+
|
||||
title: Dalgaard2001
|
||||
bibtex-entry-type: InProceedings
|
||||
bibtex-author: Dalgaard, Rune
|
||||
bibtex-title: Hypertext and the Scholarly Archive: Intertexts, Paratexts and Metatexts at Work
|
||||
bibtex-date: 2001
|
||||
|
||||
+
|
||||
title: Dickinson2008
|
||||
bibtex-entry-type: InProceedings
|
||||
bibtex-author: Dickinson, Anne
|
||||
bibtex-title: Is the e-Learning Object Create Interactive Accessible e-Learning Accessible?
|
||||
bibtex-date: 2008
|
||||
|
||||
+
|
||||
title: Finnemann2016
|
||||
bibtex-entry-type: Article
|
||||
bibtex-author: Finnemann, Niels Ole
|
||||
bibtex-title: Hypertext configurations: Genres in networked digital media
|
||||
bibtex-date: 2016
|
||||
|
||||
+
|
||||
title: Frumkin2005
|
||||
bibtex-entry-type: Article
|
||||
bibtex-author: Frumkin, Jeremy
|
||||
bibtex-title: The wiki and the digital library
|
||||
bibtex-date: 2005
|
||||
|
||||
+
|
||||
title: Maier2016
|
||||
bibtex-entry-type: InCollection
|
||||
bibtex-author: Maier, Carmen Daniela
|
||||
bibtex-title: Hypertext and Hypermedia
|
||||
bibtex-date: 2016
|
||||
|
||||
+
|
||||
title: Morris2007
|
||||
bibtex-entry-type: InProceedings
|
||||
bibtex-author: Morris, Joseph C.
|
||||
bibtex-title: DistriWiki: a distributed peer-to-peer wiki network
|
||||
bibtex-date: 2007
|
||||
|
||||
+
|
||||
title: Ruston2004
|
||||
bibtex-entry-type: Book
|
||||
bibtex-author: Ruston, Jeremy
|
||||
bibtex-title: TiddlyWiki
|
||||
bibtex-date: 2004
|
||||
|
||||
+
|
||||
title: Rutherford2009
|
||||
bibtex-entry-type: Thesis
|
||||
bibtex-author: Rutherford, Jayne
|
||||
bibtex-title: Graphical Input for TiddlyWiki
|
||||
bibtex-date: 2009
|
||||
|
||||
+
|
||||
title: Schaffert2006
|
||||
bibtex-entry-type: InProceedings
|
||||
bibtex-author: Schaffert, Sebastian
|
||||
bibtex-title: IkeWiki: A semantic wiki for collaborative knowledge management
|
||||
bibtex-date: 2006
|
||||
|
||||
+
|
||||
title: Shang2016
|
||||
bibtex-entry-type: Article
|
||||
bibtex-author: Shang, Hui-Fang
|
||||
bibtex-title: Online metacognitive strategies, hypermedia annotations, and motivation on hypertext comprehension
|
||||
bibtex-date: 2016
|
||||
|
||||
+
|
||||
title: Shang2016a
|
||||
bibtex-entry-type: Article
|
||||
bibtex-author: Shang, Hui-Fang
|
||||
bibtex-title: Exploring demographic and motivational factors associated with hypertext reading by English as a foreign language students
|
||||
bibtex-date: 2016
|
||||
|
||||
+
|
||||
title: Skiba2005
|
||||
bibtex-entry-type: Article
|
||||
bibtex-author: Skiba, Diane J.
|
||||
bibtex-title: Do your students wiki?
|
||||
bibtex-date: 2005
|
||||
|
||||
+
|
||||
title: Trentin2009
|
||||
bibtex-entry-type: Article
|
||||
bibtex-author: Trentin, Guglielmo
|
||||
bibtex-title: Using a wiki to evaluate individual contribution to a collaborative learning project
|
||||
bibtex-date: 2009
|
||||
|
||||
+
|
||||
title: Truman2016
|
||||
bibtex-entry-type: Article
|
||||
bibtex-author: Truman, Gail
|
||||
bibtex-title: Web Archiving Environmental Scan
|
||||
bibtex-date: 2016
|
||||
|
||||
+
|
||||
title: Wagner2004
|
||||
bibtex-entry-type: Article
|
||||
bibtex-author: Wagner, Christian
|
||||
bibtex-title: Wiki: A technology for conversational knowledge management and group collaboration
|
||||
bibtex-date: 2004
|
||||
|
||||
+
|
||||
title: Wilson2007
|
||||
bibtex-entry-type: Article
|
||||
bibtex-author: Wilson, Tom D.
|
||||
bibtex-title: Review of TiddlyWiki 2.1.3
|
||||
bibtex-date: 2007
|
||||
@@ -0,0 +1,45 @@
|
||||
created: 20260728220100000
|
||||
description: Group tiddlers by the value of a field chosen in one place
|
||||
import-compound: [[GroupedLists/Bibliography]]
|
||||
modified: 20260729185240642
|
||||
tags: $:/tags/wiki-test-spec GroupedLists
|
||||
title: GroupedLists/Example/ByField
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
|
||||
title: Narrative
|
||||
|
||||
`groupField` names the field to group by. It is the only line to change when you want a different grouping.
|
||||
|
||||
Two nested lists do the work:
|
||||
|
||||
* `f.valuesAll` collects each distinct value of that field, sorted
|
||||
* `f.tiddlersFor` returns the tiddlers holding one of those values
|
||||
|
||||
This test case imports 24 bibliography tiddlers from the `GroupedLists/Bibliography` payload. They came from a ~BibTeX import, so the field is `bibtex-author` rather than `author`.
|
||||
|
||||
Barker and Shang each published twice, so their groups list two titles. Set `groupField` to `bibtex-entry-type` to regroup the same data by publication type.
|
||||
+
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
|
||||
<!-- bibtex-author<value> would make the field name the operator, which cannot be a variable, so f.tiddlersFor compares via get<groupField> -->
|
||||
|
||||
<!-- No trailing whitespace after the field name: it becomes part of the name and the list silently renders nothing -->
|
||||
\procedure groupField() bibtex-author
|
||||
|
||||
\function f.valuesAll() [all[tiddlers]!is[system]has<groupField>each<groupField>get<groupField>sort[]]
|
||||
|
||||
\function f.tiddlersFor(value) [!is[system]has<groupField>] :filter[get<groupField>match<value>] +[sort[title]]
|
||||
|
||||
<$list filter="[f.valuesAll[]]" variable="_value">
|
||||
<div class="tc-menu-list-item">
|
||||
<<_value>>
|
||||
|
||||
<$list filter="[f.tiddlersFor<_value>]">
|
||||
<div class="tc-menu-list-subitem">
|
||||
<$link><$text text={{{ [<currentTiddler>get[bibtex-title]] :else[<currentTiddler>] }}}/></$link>
|
||||
</div>
|
||||
</$list>
|
||||
</div>
|
||||
</$list>
|
||||
@@ -0,0 +1,51 @@
|
||||
title: GroupedLists/Example/RecentTab
|
||||
created: 20260729190100000
|
||||
modified: 20260729190100000
|
||||
description: Group tiddlers by day with the timeline macro, as the sidebar Recent tab does
|
||||
tags: [[$:/tags/wiki-test-spec]] GroupedLists
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
|
||||
title: Narrative
|
||||
|
||||
The `timeline` macro groups tiddlers by the day found in a date field. The ''Recent'' tab in the sidebar is a single call to it, which is all the Output below is.
|
||||
|
||||
The nesting happens inside the macro:
|
||||
|
||||
* the outer filter uses `eachday` to yield one tiddler per distinct day
|
||||
* the inner filter uses `sameday` to collect every tiddler falling on that day
|
||||
* `dateField` chooses which field to read, so the same macro groups by `created` just as well as by `modified`
|
||||
|
||||
The five dummy payload tiddlers below carry hand set `modified` dates. Nothing else here has a date field, so they are exactly what the timeline finds.
|
||||
|
||||
Pass `format` to change how each day is headed, and `limit` to cap how many tiddlers are considered.
|
||||
+
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
|
||||
<$transclude $variable="timeline" format="DDth MMM YYYY"/>
|
||||
+
|
||||
title: Alpha
|
||||
modified: 20260103120000000
|
||||
|
||||
Modified on the third, later in the day.
|
||||
+
|
||||
title: Beta
|
||||
modified: 20260103100000000
|
||||
|
||||
Modified on the third, earlier in the day.
|
||||
+
|
||||
title: Gamma
|
||||
modified: 20260102120000000
|
||||
|
||||
Modified on the second, later in the day.
|
||||
+
|
||||
title: Delta
|
||||
modified: 20260102100000000
|
||||
|
||||
Modified on the second, earlier in the day.
|
||||
+
|
||||
title: Epsilon
|
||||
modified: 20260101120000000
|
||||
|
||||
Modified on the first.
|
||||
@@ -0,0 +1,116 @@
|
||||
created: 20260728224000000
|
||||
description: Group by one field while the search box filters on another
|
||||
import-compound: [[GroupedLists/Bibliography]]
|
||||
modified: 20260729190424885
|
||||
tags: $:/tags/wiki-test-spec GroupedLists
|
||||
title: GroupedLists/Example/SearchAnotherField
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
|
||||
title: Narrative
|
||||
|
||||
`searchField` names the field the search box looks at, which no longer has to be the field the list groups by.
|
||||
|
||||
Three definitions carry the split:
|
||||
|
||||
* `groupField` is `bibtex-entry-type`, so the groups and the tabs are publication types
|
||||
* `searchField` is `bibtex-author`, so typing still matches people
|
||||
* `f.tiddlersMatching` narrows the tiddlers first, and the groups are derived from whatever survives
|
||||
|
||||
Because the groups are computed after the search, a whole group disappears once none of its entries match. The tab strip is still built from the unfiltered data, so the tabs themselves stay put.
|
||||
|
||||
This test case carries the same `_TabButton` and `_TabContent` payload tiddlers as the previous example.
|
||||
|
||||
Type `bar` to leave only ''InProceedings'', holding Barker's two papers. The other tabs drop to zero and show the `emptyMessage` of the list.
|
||||
+
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
|
||||
<!-- Filter operator suffixes cannot be variables, so search:<searchField> is impossible.
|
||||
f.tiddlersMatching searches the extracted value as a title, which works whichever field it came from. -->
|
||||
|
||||
<!-- No trailing whitespace after the field name: it becomes part of the name and the list silently renders nothing -->
|
||||
\procedure groupField() bibtex-entry-type
|
||||
|
||||
\procedure searchField() bibtex-author
|
||||
|
||||
\procedure searchLabel() Author
|
||||
|
||||
\function f.valuesAll() [all[tiddlers]!is[system]has<groupField>each<groupField>get<groupField>sort[]]
|
||||
|
||||
\function f.tiddlersMatching() [all[tiddlers]!is[system]has<groupField>] :filter[get<searchField>search:title{$:/temp/GroupedLists/search-other}]
|
||||
|
||||
\function f.valuesFound() [f.tiddlersMatching[]each<groupField>get<groupField>sort[]]
|
||||
|
||||
\function f.letters() [f.valuesAll[]] :map[uppercase[]split[]first[]] +[unique[]sort[]]
|
||||
|
||||
\function f.valuesFor(letter) [f.valuesFound[]] :filter[uppercase[]split[]first[]match<letter>]
|
||||
|
||||
\function f.valuesShown(letter) [f.valuesFound[]] :filter[<letter>match[All]] :else[f.valuesFor<letter>]
|
||||
|
||||
\function f.tiddlersFor(value) [f.tiddlersMatching[]] :filter[get<groupField>match<value>] +[sort[title]]
|
||||
|
||||
<!-- The inline style below is only for this self-contained example. For production, define styles in a tiddler tagged $:/tags/Stylesheet instead. -->
|
||||
<style>.demo-group-filter { margin-bottom: 1em; }</style>
|
||||
|
||||
<$macrocall
|
||||
$name="tabs"
|
||||
tabsList="[[All]] [f.letters[]]"
|
||||
default="All"
|
||||
state="$:/state/GroupedLists/tab-other"
|
||||
buttonTemplate="_TabButton"
|
||||
template="_TabContent"
|
||||
/>
|
||||
+
|
||||
title: _TabButton
|
||||
code-body: yes
|
||||
|
||||
\whitespace trim
|
||||
|
||||
<$text text=<<currentTab>>/><span class="tc-tiny-gap-left">(<$count filter="[f.valuesShown<currentTab>]"/>)</span>
|
||||
+
|
||||
title: _TabContent
|
||||
code-body: yes
|
||||
|
||||
\whitespace trim
|
||||
|
||||
<!-- tm-focus-selector takes the first match in the document, so .tc-search input would hit the sidebar search. -->
|
||||
|
||||
<div class="tc-search demo-group-filter">
|
||||
<label>
|
||||
<<searchLabel>>:<$edit-text
|
||||
tiddler="$:/temp/GroupedLists/search-other"
|
||||
tag="input"
|
||||
type="search"
|
||||
default=""
|
||||
placeholder={{{ [[Filter by ]addsuffix<searchLabel>] }}}
|
||||
class="tc-tiny-gap"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<%if [[$:/temp/GroupedLists/search-other]get[text]minlength[1]] %>
|
||||
<$button
|
||||
class="tc-btn-invisible"
|
||||
tooltip="Clear the filter"
|
||||
aria-label="Clear the filter"
|
||||
>
|
||||
<$action-deletetiddler $tiddler="$:/temp/GroupedLists/search-other"/>
|
||||
<$action-sendmessage $message="tm-focus-selector" $param=".demo-group-filter input"/>
|
||||
{{$:/core/images/close-button}}
|
||||
</$button>
|
||||
<%endif%>
|
||||
</div>
|
||||
<$list filter="[f.valuesShown<currentTab>]" variable="_value">
|
||||
<$list-empty>
|
||||
//No matches//
|
||||
</$list-empty>
|
||||
<div class="tc-menu-list-item">
|
||||
<<_value>>
|
||||
|
||||
<$list filter="[f.tiddlersFor<_value>]">
|
||||
<div class="tc-menu-list-subitem">
|
||||
<$link><$text text={{{ [<currentTiddler>get[bibtex-title]] :else[<currentTiddler>] }}}/></$link>
|
||||
</div>
|
||||
</$list>
|
||||
</div>
|
||||
</$list>
|
||||
@@ -0,0 +1,111 @@
|
||||
created: 20260728220400000
|
||||
description: Combine the tab strip with a search box inside the tab panel
|
||||
import-compound: [[GroupedLists/Bibliography]]
|
||||
modified: 20260729190424885
|
||||
tags: $:/tags/wiki-test-spec GroupedLists
|
||||
title: GroupedLists/Example/TabsWithSearch
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
|
||||
title: Narrative
|
||||
|
||||
`_TabContent` holds the search box as well as the list, so the search sits inside the panel and filters within the selected tab.
|
||||
|
||||
Which values each definition reads is the whole design:
|
||||
|
||||
* `f.letters` reads the //unfiltered// values, so tabs never disappear while you type and the selected tab cannot be stranded on an empty panel
|
||||
* `f.valuesShown` reads the //filtered// values, so the counts follow the search and a tab showing zero tells you not to look there
|
||||
|
||||
This test case carries the same `_TabButton` and `_TabContent` payload tiddlers as the previous example, with the search box added to the panel.
|
||||
|
||||
The input is a sibling of the list rather than a child of anything that recomputes, which is what keeps the cursor in the box while you type.
|
||||
|
||||
Type `ba` and watch the counts change. Any tab whose count falls to zero shows the `emptyMessage` of the list instead of nothing at all.
|
||||
+
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
|
||||
<!-- Filter operator suffixes cannot be variables, so search:<groupField> and bibtex-author<value> are impossible.
|
||||
f.valuesFound searches the collected values as titles, f.tiddlersFor compares via get<groupField>. -->
|
||||
|
||||
<!-- No trailing whitespace after the field name: it becomes part of the name and the list silently renders nothing -->
|
||||
\procedure groupField() bibtex-author
|
||||
|
||||
\procedure groupLabel() Author
|
||||
|
||||
\function f.valuesAll() [all[tiddlers]!is[system]has<groupField>each<groupField>get<groupField>sort[]]
|
||||
|
||||
\function f.valuesFound() [f.valuesAll[]] :filter[search:title{$:/temp/GroupedLists/search}]
|
||||
|
||||
\function f.letters() [f.valuesAll[]] :map[uppercase[]split[]first[]] +[unique[]sort[]]
|
||||
|
||||
\function f.valuesFor(letter) [f.valuesFound[]] :filter[uppercase[]split[]first[]match<letter>]
|
||||
|
||||
\function f.valuesShown(letter) [f.valuesFound[]] :filter[<letter>match[All]] :else[f.valuesFor<letter>]
|
||||
|
||||
\function f.tiddlersFor(value) [!is[system]has<groupField>] :filter[get<groupField>match<value>] +[sort[title]]
|
||||
|
||||
<!-- The inline style below is only for this self-contained example. For production, define styles in a tiddler tagged $:/tags/Stylesheet instead. -->
|
||||
<style>.demo-group-filter { margin-bottom: 1em; }</style>
|
||||
|
||||
<$macrocall
|
||||
$name="tabs"
|
||||
tabsList="[[All]] [f.letters[]]"
|
||||
default="All"
|
||||
state="$:/state/GroupedLists/tab-search"
|
||||
buttonTemplate="_TabButton"
|
||||
template="_TabContent"
|
||||
/>
|
||||
+
|
||||
title: _TabButton
|
||||
code-body: yes
|
||||
|
||||
\whitespace trim
|
||||
|
||||
<$text text=<<currentTab>>/><span class="tc-tiny-gap-left">(<$count filter="[f.valuesShown<currentTab>]"/>)</span>
|
||||
+
|
||||
title: _TabContent
|
||||
code-body: yes
|
||||
|
||||
\whitespace trim
|
||||
|
||||
<!-- tm-focus-selector takes the first match in the document, so .tc-search input would hit the sidebar search. -->
|
||||
|
||||
<div class="tc-search demo-group-filter">
|
||||
<label>
|
||||
<<groupLabel>>:<$edit-text
|
||||
tiddler="$:/temp/GroupedLists/search"
|
||||
tag="input"
|
||||
type="search"
|
||||
default=""
|
||||
placeholder={{{ [[Filter by ]addsuffix<groupLabel>] }}}
|
||||
class="tc-tiny-gap"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<%if [[$:/temp/GroupedLists/search]get[text]minlength[1]] %>
|
||||
<$button
|
||||
class="tc-btn-invisible"
|
||||
tooltip="Clear the filter"
|
||||
aria-label="Clear the filter"
|
||||
>
|
||||
<$action-deletetiddler $tiddler="$:/temp/GroupedLists/search"/>
|
||||
<$action-sendmessage $message="tm-focus-selector" $param=".demo-group-filter input"/>
|
||||
{{$:/core/images/close-button}}
|
||||
</$button>
|
||||
<%endif%>
|
||||
</div>
|
||||
<$list filter="[f.valuesShown<currentTab>]" variable="_value">
|
||||
<$list-empty>
|
||||
//No matches//
|
||||
</$list-empty>
|
||||
<div class="tc-menu-list-item">
|
||||
<<_value>>
|
||||
|
||||
<$list filter="[f.tiddlersFor<_value>]">
|
||||
<div class="tc-menu-list-subitem">
|
||||
<$link><$text text={{{ [<currentTiddler>get[bibtex-title]] :else[<currentTiddler>] }}}/></$link>
|
||||
</div>
|
||||
</$list>
|
||||
</div>
|
||||
</$list>
|
||||
@@ -0,0 +1,60 @@
|
||||
title: GroupedLists/Example/TypesTab
|
||||
created: 20260729190000000
|
||||
modified: 20260729190000000
|
||||
description: Group tiddlers by their type, as the sidebar Types tab does
|
||||
tags: [[$:/tags/wiki-test-spec]] GroupedLists
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
|
||||
title: Narrative
|
||||
|
||||
`$:/core/Filters/TypedTiddlers` collects one tiddler per distinct type, and the inner list then finds every tiddler sharing that type.
|
||||
|
||||
The two lists do different jobs:
|
||||
|
||||
* the outer filter ends in `each[type]`, so it yields one representative tiddler per type rather than every tiddler
|
||||
* the inner filter reads `{!!type}` from that representative, so `currentTiddler` is what carries the type from the outer list to the inner one
|
||||
|
||||
This is the code behind the ''Types'' tab in the sidebar, under ''More''. Open that tab to see it running over a real wiki.
|
||||
|
||||
The five dummy payload tiddlers below are the only ones here with a `type` field, so they are exactly what the list finds.
|
||||
+
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
|
||||
<$list filter={{$:/core/Filters/TypedTiddlers!!filter}}>
|
||||
<div class="tc-menu-list-item">
|
||||
<$view field="type"/>
|
||||
|
||||
<$list filter="[type{!!type}!is[system]sort[title]]">
|
||||
<div class="tc-menu-list-subitem">
|
||||
<$link to={{!!title}}><$view field="title"/></$link>
|
||||
</div>
|
||||
</$list>
|
||||
</div>
|
||||
</$list>
|
||||
+
|
||||
title: Alpha
|
||||
type: image/svg+xml
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><circle cx="8" cy="8" r="7"/></svg>
|
||||
+
|
||||
title: Beta
|
||||
type: image/svg+xml
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><rect x="1" y="1" width="14" height="14"/></svg>
|
||||
+
|
||||
title: Gamma
|
||||
type: text/plain
|
||||
|
||||
A plain text sample.
|
||||
+
|
||||
title: Delta
|
||||
type: text/plain
|
||||
|
||||
Another plain text sample.
|
||||
+
|
||||
title: Epsilon
|
||||
type: application/json
|
||||
|
||||
{"sample": true}
|
||||
@@ -0,0 +1,82 @@
|
||||
created: 20260728220200000
|
||||
description: Narrow a grouped list with a search box
|
||||
import-compound: [[GroupedLists/Bibliography]]
|
||||
modified: 20260729185240642
|
||||
tags: $:/tags/wiki-test-spec GroupedLists
|
||||
title: GroupedLists/Example/WithSearch
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
|
||||
title: Narrative
|
||||
|
||||
`search:title` filters the values collected by `f.valuesAll`, so the grouped list narrows while you type without the tiddlers themselves being searched.
|
||||
|
||||
Two definitions are added to the plain grouped list:
|
||||
|
||||
* `groupLabel` is the human readable name shown beside the input
|
||||
* `f.valuesFound` applies the search to the collected values
|
||||
|
||||
An empty box is a no operation, so the whole list comes back. The clear button appears only once there is something to clear, and puts the cursor back in the input.
|
||||
|
||||
Type `ann` to match Dickinson and Finnemann, `wi` to match several at once, or `zzz` to fall through to the `emptyMessage` of the outer list.
|
||||
+
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
|
||||
<!-- Filter operator suffixes cannot be variables, so search:<groupField> and bibtex-author<value> are impossible.
|
||||
f.valuesFound searches the collected values as titles, f.tiddlersFor compares via get<groupField>. -->
|
||||
|
||||
<!-- tm-focus-selector takes the first match in the document, so .tc-search input would hit the sidebar search. -->
|
||||
|
||||
<!-- No trailing whitespace after the field name: it becomes part of the name and the list silently renders nothing -->
|
||||
\procedure groupField() bibtex-author
|
||||
|
||||
\procedure groupLabel() Author
|
||||
|
||||
\function f.valuesAll() [all[tiddlers]!is[system]has<groupField>each<groupField>get<groupField>sort[]]
|
||||
|
||||
\function f.valuesFound() [f.valuesAll[]] :filter[search:title{$:/temp/GroupedLists/search}]
|
||||
|
||||
\function f.tiddlersFor(value) [!is[system]has<groupField>] :filter[get<groupField>match<value>] +[sort[title]]
|
||||
|
||||
<!-- The inline style below is only for this self-contained example. For production, define styles in a tiddler tagged $:/tags/Stylesheet instead. -->
|
||||
<style>.demo-group-filter { margin-bottom: 1em; }</style>
|
||||
|
||||
<div class="tc-search demo-group-filter">
|
||||
<label>
|
||||
<<groupLabel>>:<$edit-text
|
||||
tiddler="$:/temp/GroupedLists/search"
|
||||
tag="input"
|
||||
type="search"
|
||||
default=""
|
||||
placeholder={{{ [[Filter by ]addsuffix<groupLabel>] }}}
|
||||
class="tc-tiny-gap"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<%if [[$:/temp/GroupedLists/search]get[text]minlength[1]] %>
|
||||
<$button
|
||||
class="tc-btn-invisible"
|
||||
tooltip="Clear the filter"
|
||||
aria-label="Clear the filter"
|
||||
>
|
||||
<$action-deletetiddler $tiddler="$:/temp/GroupedLists/search"/>
|
||||
<$action-sendmessage $message="tm-focus-selector" $param=".demo-group-filter input"/>
|
||||
{{$:/core/images/close-button}}
|
||||
</$button>
|
||||
<%endif%>
|
||||
</div>
|
||||
<$list filter="[f.valuesFound[]]" variable="_value">
|
||||
<$list-empty>
|
||||
//No matches//
|
||||
</$list-empty>
|
||||
<div class="tc-menu-list-item">
|
||||
<<_value>>
|
||||
|
||||
<$list filter="[f.tiddlersFor<_value>]">
|
||||
<div class="tc-menu-list-subitem">
|
||||
<$link><$text text={{{ [<currentTiddler>get[bibtex-title]] :else[<currentTiddler>] }}}/></$link>
|
||||
</div>
|
||||
</$list>
|
||||
</div>
|
||||
</$list>
|
||||
@@ -0,0 +1,76 @@
|
||||
created: 20260728220300000
|
||||
description: Bucket the groups into tabs by first character, with a count per tab
|
||||
import-compound: [[GroupedLists/Bibliography]]
|
||||
modified: 20260729190424885
|
||||
tags: $:/tags/wiki-test-spec GroupedLists
|
||||
title: GroupedLists/Example/WithTabs
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
|
||||
title: Narrative
|
||||
|
||||
The `tabs` macro takes its tab list from a filter, so here the tabs come out of the data: one per distinct first character, plus an ''All'' tab.
|
||||
|
||||
Three definitions feed it:
|
||||
|
||||
* `f.letters` collects the distinct first characters
|
||||
* `f.valuesFor` selects the groups filed under one character
|
||||
* `f.valuesShown` returns everything for ''All'', one character otherwise
|
||||
|
||||
This test case carries two payload tiddlers, `_TabButton` and `_TabContent`, because the `tabs` macro transcludes its caption and its panel by tiddler title and cannot take inline wikitext.
|
||||
|
||||
Matching is case insensitive, so `de Vries` and `De Vries` would share a tab. A character that is not a letter gets its own tab, which is why the sample data produces a `3` tab. Each caption carries the number of groups behind it, so no tab is a dead end.
|
||||
+
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
|
||||
<!-- bibtex-author<value> would make the field name the operator, which cannot be a variable, so f.tiddlersFor compares via get<groupField> -->
|
||||
|
||||
<!-- No trailing whitespace after the field name: it becomes part of the name and the list silently renders nothing -->
|
||||
\procedure groupField() bibtex-author
|
||||
|
||||
\function f.valuesAll() [all[tiddlers]!is[system]has<groupField>each<groupField>get<groupField>sort[]]
|
||||
|
||||
\function f.letters() [f.valuesAll[]] :map[uppercase[]split[]first[]] +[unique[]sort[]]
|
||||
|
||||
\function f.valuesFor(letter) [f.valuesAll[]] :filter[uppercase[]split[]first[]match<letter>]
|
||||
|
||||
\function f.valuesShown(letter) [f.valuesAll[]] :filter[<letter>match[All]] :else[f.valuesFor<letter>]
|
||||
|
||||
\function f.tiddlersFor(value) [!is[system]has<groupField>] :filter[get<groupField>match<value>] +[sort[title]]
|
||||
|
||||
<$macrocall
|
||||
$name="tabs"
|
||||
tabsList="[[All]] [f.letters[]]"
|
||||
default="All"
|
||||
state="$:/state/GroupedLists/tab"
|
||||
buttonTemplate="_TabButton"
|
||||
template="_TabContent"
|
||||
/>
|
||||
+
|
||||
title: _TabButton
|
||||
code-body: yes
|
||||
|
||||
\whitespace trim
|
||||
|
||||
<$text text=<<currentTab>>/><span class="tc-tiny-gap-left">(<$count filter="[f.valuesShown<currentTab>]"/>)</span>
|
||||
+
|
||||
title: _TabContent
|
||||
code-body: yes
|
||||
|
||||
\whitespace trim
|
||||
|
||||
<$list filter="[f.valuesShown<currentTab>]" variable="_value">
|
||||
<$list-empty>
|
||||
//No matches//
|
||||
</$list-empty>
|
||||
<div class="tc-menu-list-item">
|
||||
<<_value>>
|
||||
|
||||
<$list filter="[f.tiddlersFor<_value>]">
|
||||
<div class="tc-menu-list-subitem">
|
||||
<$link><$text text={{{ [<currentTiddler>get[bibtex-title]] :else[<currentTiddler>] }}}/></$link>
|
||||
</div>
|
||||
</$list>
|
||||
</div>
|
||||
</$list>
|
||||
@@ -1,7 +1,7 @@
|
||||
created: 20130822170200000
|
||||
icon: $:/core/icon
|
||||
list: [[A Gentle Guide to TiddlyWiki]] [[Discover TiddlyWiki]] [[Some of the things you can do with TiddlyWiki]] [[Ten reasons to switch to TiddlyWiki]] Examples [[What happened to the original TiddlyWiki?]]
|
||||
modified: 20260420192600833
|
||||
modified: 20260710091122803
|
||||
tags: Welcome
|
||||
title: HelloThere
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 47 KiB |
@@ -4,7 +4,7 @@ created: 20131219100608529
|
||||
delivery: DIY
|
||||
description: Flexible hosting on your own machine or in the cloud
|
||||
method: sync
|
||||
modified: 20221115230831173
|
||||
modified: 20260712162730421
|
||||
tags: Saving [[TiddlyWiki on Node.js]] Windows Mac Linux
|
||||
title: Installing TiddlyWiki on Node.js
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -39,4 +39,39 @@ The `-g` flag causes TiddlyWiki to be installed globally. Without it, TiddlyWiki
|
||||
|
||||
<<.warning "If you are using Debian or Debian-based Linux and you are receiving a `node: command not found` error though node.js package is installed, you may need to create a symbolic link between `nodejs` and `node`. Consult your distro's manual and `whereis` to correctly create a link. See github [[issue 1434|http://github.com/TiddlyWiki/TiddlyWiki5/issues/1434]]. <br><br>Example Debian v8.0: `sudo ln -s /usr/bin/nodejs /usr/bin/node`">>
|
||||
<br>
|
||||
<<.tip "You can also install prior versions like this: <br><code> npm install -g tiddlywiki@5.1.13</code>">>
|
||||
<<.tip "You can also install prior versions like this: <br><code> npm install -g tiddlywiki@5.1.13</code><br>Note: this will overwrite the installed version rather than installing it alongside.">>
|
||||
|
||||
!! Installing multiple Node.js verions alongside
|
||||
|
||||
There are multiple options:
|
||||
|
||||
!!! Local install
|
||||
|
||||
```
|
||||
mkdir -p /home/user/opt/tiddlywiki-5.1.13 && cd "$_" # works in Bash
|
||||
npm install tiddlywiki@5.1.13
|
||||
```
|
||||
|
||||
This installs the given version in the current directory, at the expense of taking some extra disk space. The binary is in `node_modules/.bin/tiddlywiki` (relative to the current directory). For convenience, it is possible to create a symlink (in Linux) containing explicit version number in the name, like this:
|
||||
|
||||
```
|
||||
ln -s /home/user/opt/tiddlywiki-5.1.13/node_modules/.bin/tiddlywiki /home/user/bin/tiddlywiki-5.1.13
|
||||
```
|
||||
|
||||
Having `~/bin` in `PATH` environment variable (usually at the beginning of it), makes it possible to start this version of TiddlyWiki as simple as `tiddlywiki-5.1.13`.
|
||||
|
||||
!!! Use `npx`
|
||||
|
||||
To run another version without overwriting the currently installed version, use npx:
|
||||
|
||||
```
|
||||
npx tiddlywiki@5.3.8 --version
|
||||
```
|
||||
|
||||
<<.warning "This comes with a security risk, since it downloads and executes a package from the internet. See https://talk.tiddlywiki.org/t/tiddlywiki-docs-gettingstarted-node-js/15423/4">>
|
||||
|
||||
!!! Run directly from the source code repository
|
||||
|
||||
# clone https://github.com/TiddlyWiki/TiddlyWiki5/ locally
|
||||
# checkout any version (using `git checkout` or `git switch` for modern versions of Git)
|
||||
# set up a link or a shell script for it
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
title: $:/changenotes/5.4.0/#8972/impacts/parenthesised-field-names
|
||||
changenote: $:/changenotes/5.4.0/#8972
|
||||
created: 20260609193636000
|
||||
modified: 20260609193636000
|
||||
tags: $:/tags/ImpactNote
|
||||
impact-type: compatibility-break
|
||||
description: A field name containing round brackets can no longer be used as a filter operator suffix, because `(` now starts a multi value variable operand.
|
||||
|
||||
Two filter operators take a field name in the suffix position: `regexp` and `search`. Up to 5.3.8 such a field name could contain round brackets, for example:
|
||||
|
||||
```
|
||||
[regexp:_cd-work(s)[(?i)suite]]
|
||||
[search:_cd-work(s)[suite]]
|
||||
```
|
||||
|
||||
In 5.4.0 the new `(varname)` operand syntax for [[Multi-Valued Variables]] makes `(` start an operand. The parser now splits such a field name at the `(`, so the filter no longer matches.
|
||||
|
||||
This will not be fixed. The two readings of `field(x)`, a field named `field(x)` versus the field `field` with the operand `(x)`, are genuinely ambiguous, so `(` and `)` are reserved in filter operator names and suffixes from 5.4.0 onwards.
|
||||
|
||||
Workaround: do not use `(` or `)` in field names that are referenced as a filter operator suffix. Rename the field, for example `_cd-works`.
|
||||
+3
-4
@@ -1,11 +1,10 @@
|
||||
title: $:/changenotes/5.4.0/#9107
|
||||
description: Update configuration defaults
|
||||
description: Update default sidebar layout
|
||||
release: 5.4.0
|
||||
tags: $:/tags/ChangeNote
|
||||
change-type: enhancement
|
||||
change-category: usability
|
||||
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9107
|
||||
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9107 https://github.com/TiddlyWiki/TiddlyWiki5/issues/9757
|
||||
github-contributors: Jermolene
|
||||
|
||||
* Changed default sidebar layout from ''fixed-fluid'' to ''fluid-fixed''
|
||||
* Changed ''Wrap long lines in code blocks'' default to ''No''
|
||||
Changed default sidebar layout from ''fixed-fluid'' to ''fluid-fixed''
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
change-category: internal
|
||||
change-type: bugfix
|
||||
created: 20260710092852681
|
||||
description: Fixes an issue with the minheight of textareas.
|
||||
github-contributors: pmario
|
||||
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9828
|
||||
modified: 20260710093223971
|
||||
release: 5.4.1
|
||||
tags: $:/tags/ChangeNote
|
||||
title: $:/changenotes/5.4.1/#9828
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
Fixes an issue with the minheight of textareas.
|
||||
@@ -0,0 +1,9 @@
|
||||
title: $:/changenotes/5.4.0/#9829
|
||||
description: Update Polish translation
|
||||
release: 5.4.1
|
||||
tags: $:/tags/ChangeNote
|
||||
change-type: enhancement
|
||||
change-category: translation
|
||||
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9829
|
||||
github-contributors: EvidentlyCube
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
title: $:/changenotes/5.4.1/#9836
|
||||
description: Fix mid-text link cutoff, fix disabling freelinks toggle blanks view, and add configurable MaxLinks cap
|
||||
tags: $:/tags/ChangeNote
|
||||
release: 5.4.1
|
||||
change-type: bugfix
|
||||
change-category: plugin
|
||||
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9836
|
||||
change-type: bugfix
|
||||
created: 20260710092644515
|
||||
description: Freelinks: Fix mid-text link cutoff, fix disabling freelinks toggle blanks view, and add configurable ~MaxLinks cap
|
||||
github-contributors: s793016
|
||||
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9836
|
||||
modified: 20260710092700225
|
||||
release: 5.4.1
|
||||
tags: $:/tags/ChangeNote
|
||||
title: $:/changenotes/5.4.1/#9836
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
Fixes correctness and reliability issues in the freelinks plugin.
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
change-category: widget
|
||||
change-type: bugfix
|
||||
created: 20260710093059614
|
||||
description: Fixes a regression in the SelectWidget when selecting multiple values.
|
||||
github-contributors: pmario
|
||||
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9841
|
||||
modified: 20260710093210171
|
||||
release: 5.4.1
|
||||
tags: $:/tags/ChangeNote
|
||||
title: $:/changenotes/5.4.1/#9841
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
Fixes a regression in the SelectWidget when selecting multiple values.
|
||||
@@ -0,0 +1,9 @@
|
||||
title: $:/changenotes/5.4.0/#9870
|
||||
description: Update Japanese translation
|
||||
release: 5.4.1
|
||||
tags: $:/tags/ChangeNote
|
||||
change-type: enhancement
|
||||
change-category: translation
|
||||
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9870
|
||||
github-contributors: IchijikuIchigo
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
change-category: plugin
|
||||
change-type: bugfix
|
||||
created: 20260710093318232
|
||||
description: Katex: restores a missing font
|
||||
github-contributors: Leilei332
|
||||
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9873
|
||||
modified: 20260710093432151
|
||||
release: 5.4.1
|
||||
tags: $:/tags/ChangeNote
|
||||
title: $:/changenotes/5.4.1/#9873
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
Restores the missing KaTeX_Caligraphic-Regular.woff2
|
||||
font in the Katex plugin
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
title: $:/changenotes/5.4.1/#9882
|
||||
created: 20260701142414000
|
||||
modified: 20260701142414000
|
||||
description: Fix invalid source positions in parse tree nodes
|
||||
release: 5.4.1
|
||||
tags: $:/tags/ChangeNote
|
||||
change-type: bugfix
|
||||
change-category: internal
|
||||
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9882
|
||||
github-contributors: pmario
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
Several wikitext parser rules emitted parse tree nodes with missing or incorrect `start` and `end` source positions. Accurate positions are needed by tooling that maps rendered output back to the source text.
|
||||
|
||||
* ''Inline code'' (`` `code` ``): the text node `end` no longer runs past the closing backtick, so the span covers only the code text.
|
||||
|
||||
* ''Suppressed external links'' (`~https://example.com`): the text node now starts after the suppressing `~`, matching the plain text that is emitted.
|
||||
|
||||
* ''Suppressed wikilinks'' (`~SuppressedLink`): the text node now carries `start` and `end` positions. Previously it had none.
|
||||
|
||||
* ''Import filter'' (`\import`): the filter start position was set to the whole source string instead of the current parse position. It now uses the correct offset.
|
||||
@@ -0,0 +1,13 @@
|
||||
title: $:/changenotes/5.4.1/#9905
|
||||
created: 20260709142414000
|
||||
modified: 20260709142414000
|
||||
description: Fix errors in eventcatcher while serializing event properties
|
||||
release: 5.4.1
|
||||
tags: $:/tags/ChangeNote
|
||||
change-type: bugfix
|
||||
change-category: internal
|
||||
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9905
|
||||
github-contributors: saqimtiaz
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
Fixes an issue in utils.copyObjectPropertiesSafe which did not correctly handle DOM objects from other windows resulting in an error.
|
||||
@@ -1,13 +1,19 @@
|
||||
caption: 5.4.1
|
||||
created: 20260508181012812
|
||||
modified: 20260508181012812
|
||||
description: Release v5.4.1 addresses regressions and bugs introduced in the v5.4.0 release.
|
||||
modified: 20260710091945414
|
||||
released: 20260710090509444
|
||||
tags: ReleaseNotes
|
||||
title: Release 5.4.1
|
||||
type: text/vnd.tiddlywiki
|
||||
description: Under development
|
||||
|
||||
\procedure release-introduction()
|
||||
Release v5.4.1 is under development.
|
||||
Release v5.4.1 addresses regressions and bugs introduced in the v5.4.0 release.
|
||||
\end release-introduction
|
||||
|
||||
\define banner-credit-user-name() Peter
|
||||
\define banner-credit-user-link() https://talk.tiddlywiki.org/u/peter
|
||||
\define banner-credit-discussion-link() https://talk.tiddlywiki.org/t/vote-for-the-banner-tiddlywiki-v5-4-0/15016
|
||||
\define banner-credit-permalink() https://raw.githubusercontent.com/TiddlyWiki/TiddlyWiki5/92caa7312ebc51c59cd345cc81b4a326661a0650/editions/tw5.com/tiddlers/images/New%20Release%20Banner.webp
|
||||
|
||||
<<releasenote 5.4.1>>
|
||||
|
||||
@@ -5,25 +5,27 @@ created: 20131129101027725
|
||||
delivery: App
|
||||
description: iPad/iPhone app for working with TiddlyWiki
|
||||
method: save
|
||||
modified: 20201007205336209
|
||||
modified: 20260421210013537
|
||||
tags: Saving iOS [[Standalone App]]
|
||||
title: Saving on iPad/iPhone
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
The iPad/iPhone app ''Quine 2'' makes it possible to view, edit and then save changes to TiddlyWiki5 on iOS. [[Download it here|https://apps.apple.com/us/app/quine-2/id1450128957]].
|
||||
The iPad/iPhone app ''Quine'' makes it possible to view, edit and then save changes to TiddlyWiki5 on iOS.
|
||||
|
||||
Currently in its third major version, rewritten for iOS and iPadOS 26.0.
|
||||
|
||||
[[Download it here|https://apps.apple.com/us/app/TiddlyWiki51664603]].
|
||||
|
||||
Instructions for use:
|
||||
|
||||
# Open Quine 2
|
||||
# Tap the + toolbar button to create and open a new TiddlyWiki
|
||||
# Open Quine
|
||||
# From the file list tap the + toolbar button, or the "New Wiki" button, to create and open a new TiddlyWiki
|
||||
# From the file list tap an existing TiddlyWiki file to open it
|
||||
# Edit the TiddlyWiki as normal, and save as normal using either Autosave or the TiddlyWiki save button <<.icon $:/core/images/save-button-dynamic>>
|
||||
# Tap the left hand "Documents" toolbar button to close an open TiddlyWiki
|
||||
# Tap the left hand "<" toolbar button to close an open TiddlyWiki, returning to the file list
|
||||
|
||||
*Quine 2 works natively in iOS with the local file system and the iCloud file system
|
||||
*Quine 2 also allows you to open, edit and save TiddlyWiki files stored with cloud file providers
|
||||
*Quine 2 allows you to follow embedded WikiText links and canonical links to external files for cloud-like file providers which support "folder level sharing".
|
||||
**This includes the apps "Secure Shellfish" and "Working Copy". Most providers, though, do not allow apps like Quine 2 to access linked files this way.
|
||||
** If you wish to enable such links for "well behaved" file providers, toggle "on" the "Enable folder selection for out-of-sandbox links" setting in iOS Settings for Quine 2
|
||||
*Quine works natively in iOS with the local file system and the iCloud file system
|
||||
*Quine also allows you to open, edit and save TiddlyWiki files stored with other cloud file providers
|
||||
*Quine allows you to follow embedded WikiText links and canonical links to external files on cloud-like file providers which support "folder level sharing".
|
||||
|
||||
//Note that Quine is published independently of TiddlyWiki//
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
caption: button
|
||||
created: 20131024141900000
|
||||
modified: 20251101091926820
|
||||
modified: 20260725015330000
|
||||
tags: Widgets TriggeringWidgets
|
||||
title: ButtonWidget
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -66,4 +66,40 @@ Press me!
|
||||
</$button>
|
||||
```
|
||||
|
||||
''Tip:'' Set ''class'' to `tc-btn-invisible tc-tiddlylink` to have a button look like an internal link.
|
||||
! Order of execution
|
||||
|
||||
The order of the attributes written in wikitext does ''not'' matter.
|
||||
|
||||
A button can carry several of these attributes at once. They are not alternatives: every one that is present is used, and ''always in the order shown below''.
|
||||
|
||||
# ActionWidgets in the button body
|
||||
# <<.attr to>>
|
||||
# <<.attr message>>, with <<.attr param>>
|
||||
# <<.attr popup>> or <<.attr popupTitle>>
|
||||
# <<.attr set>> or <<.attr setTitle>>, with <<.attr setTo>>, <<.attr setField>> and <<.attr setIndex>>
|
||||
# <<.attr actions>>
|
||||
|
||||
<<.tip """When a button does more than one thing, put the whole behaviour in the `actions` attribute. It keeps the behaviour in one place and runs it last, after every other attribute above. A single `<$button to="HelloThere">Click Me</$button>` needs no action string.""">>
|
||||
|
||||
Action widgets are refreshed before each one runs, so they see the results of the actions before them. Ordinary widgets wrapped around them are not. See [[ActionWidget Execution Modes]] when a value looks out of date.
|
||||
|
||||
! Examples
|
||||
|
||||
This button uses the <<.attr to>> attribute to navigate to the [[HelloThere]] tiddler, opening it in the story river. The optional <<.attr tooltip>> attribute sets the text shown on hover.
|
||||
|
||||
<$macrocall $name='wikitext-example-without-html'
|
||||
src='<$button to="HelloThere" tooltip="Navigate to the HelloThere tiddler">
|
||||
Open ~HelloThere
|
||||
</$button>'/>
|
||||
|
||||
<<.tip """Set ''class'' to `tc-btn-invisible tc-tiddlylink` to have a button look like an internal link.""">>
|
||||
|
||||
! Interactive Examples
|
||||
|
||||
<<<
|
||||
<$list filter="[prefix[ButtonWidget/Example/]]">
|
||||
|
||||
; <$link/>
|
||||
: {{!!description}}
|
||||
</$list>
|
||||
<<<
|
||||
@@ -1,5 +1,5 @@
|
||||
created: 20221007144237585
|
||||
modified: 20240422084734129
|
||||
modified: 20260807190532419
|
||||
tags: Concepts
|
||||
title: Custom Widgets
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -79,3 +79,34 @@ Python
|
||||
<$let test="Tiger">
|
||||
<$codeblock code=<<test>>/>
|
||||
</$let>""">>
|
||||
|
||||
In this example, we override the <<.wlink "LinkWidget">> widget to automatically display an icon next to the link if the target tiddler has an `icon` field. We use the <<.wlink "ParametersWidget">> widget to capture all attributes passed to the original link and forward them to the underlying widget via <<.wlink "GenesisWidget">>:
|
||||
|
||||
<<wikitext-example-without-html """\widget $link()
|
||||
\function link.icon()
|
||||
[<params-var>jsonget[to]else<currentTiddler>get[icon]]
|
||||
\end link.icon
|
||||
\function link.text()
|
||||
[<params-var>jsonget[to]else<currentTiddler>]
|
||||
\end link.text
|
||||
\whitespace trim
|
||||
<$parameters $params="params-var">
|
||||
<$genesis
|
||||
$type="$link"
|
||||
$remappable="no"
|
||||
$names="[<params-var>jsonindexes[]]"
|
||||
$values="[<params-var>jsonindexes[]] :map[<params-var>jsonget<currentTiddler>]"
|
||||
>
|
||||
<%if [<link.icon>]%>
|
||||
<span class="tc-tiddler-title-icon tc-titlebar" style="fill:currentColor;font-size:11pt!important">
|
||||
<$transclude $tiddler=<<link.icon>> size="11pt"/>
|
||||
</span>
|
||||
<%endif%>
|
||||
<$slot $name="ts-raw">
|
||||
<$text text=<<link.text>> />
|
||||
</$slot>
|
||||
</$genesis>
|
||||
</$parameters>
|
||||
\end
|
||||
|
||||
<<list-links "[has[icon]]">>""">>
|
||||
|
||||
@@ -7,7 +7,7 @@ type: text/vnd.tiddlywiki
|
||||
|
||||
!! Basic syntax
|
||||
|
||||
HTML description lists (<abbr title="also known as">AKA</abbr> definition lists) are created with this syntax:
|
||||
HTML description lists (<abbr title="also known as">AKA</abbr> definition lists) are created with this syntax:
|
||||
|
||||
<<wikitext-example src:"; Term being described
|
||||
: Description / Definition of that term
|
||||
@@ -17,7 +17,7 @@ HTML description lists (<abbr title="also known as">AKA</abbr> definition lists)
|
||||
|
||||
!! Multiple terms and descriptions
|
||||
|
||||
You can create multiple descriptions for a term, or multiple terms for a single description:
|
||||
You can create multiple descriptions for a term, or multiple terms with a single description:
|
||||
|
||||
<<wikitext-example src:"; Mouse
|
||||
: A rodent with a small body and a long tail
|
||||
@@ -41,4 +41,4 @@ Description lists may also be nested to create lists within lists:
|
||||
::: A coffee made with espresso and steamed milk
|
||||
; Tea
|
||||
: A beverage typically made from tea leaves
|
||||
">>
|
||||
">>
|
||||
@@ -10,7 +10,6 @@ Advanced/ShadowInfo/Shadow/Hint: Tiddler <$link to=<<infoTiddler>>><$text text=<
|
||||
Advanced/ShadowInfo/Shadow/Source: Zdefiniiowany we wtyczce <$link to=<<pluginTiddler>>><$text text=<<pluginTiddler>>/></$link>
|
||||
Advanced/ShadowInfo/OverriddenShadow/Hint: Nadpisany przez zwykłego tiddlera
|
||||
Advanced/CascadeInfo/Heading: Szczegóły Kaskady
|
||||
Advanced/CascadeInfo/Hint: These are the view template segments that are resolved for each of the system view template cascades
|
||||
Advanced/CascadeInfo/Hint: Lista elementów widoku, których kaskady określają szablony użyte do wyświetlenia tiddlera
|
||||
Advanced/CascadeInfo/Detail/View: Widok
|
||||
Advanced/CascadeInfo/Detail/ActiveCascadeFilter: Aktywny filtr kaskady
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
TiddlyWiki created by Jeremy Ruston, (jeremy [at] jermolene [dot] com)
|
||||
|
||||
Copyright (c) 2004-2007, Jeremy Ruston
|
||||
Copyright (c) 2007-2026, UnaMesa Association
|
||||
Copyright (c) 2007-2025, UnaMesa Association
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
||||
@@ -650,4 +650,5 @@ Rishu kumar, @rishu-7549, 2025/10/25
|
||||
|
||||
Himmel, @NotHimmel, 2026/03/19
|
||||
|
||||
@sean-clayton, 2026/05/16
|
||||
|
||||
@vuktw, 2026/07/12
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "tiddlywiki",
|
||||
"version": "5.4.0",
|
||||
"version": "5.4.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "tiddlywiki",
|
||||
"version": "5.4.0",
|
||||
"version": "5.4.1",
|
||||
"license": "BSD",
|
||||
"bin": {
|
||||
"tiddlywiki": "tiddlywiki.js"
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "tiddlywiki",
|
||||
"preferGlobal": true,
|
||||
"version": "5.4.1-prerelease",
|
||||
"version": "5.4.1",
|
||||
"author": "Jeremy Ruston <jeremy@jermolene.com>",
|
||||
"description": "a non-linear personal web notebook",
|
||||
"contributors": [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<h1 class="">Welcome</h1><p>Welcome to <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki.html">TiddlyWiki</a>, a non-linear personal web notebook that anyone can use and keep forever, independently of any corporation.</p><p>TiddlyWiki is a complete interactive wiki in JavaScript. It can be used as a single HTML file in the browser or as a powerful Node.js application. It is highly customisable: the entire user interface is itself implemented in hackable <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/WikiText.html">WikiText</a>.</p><h2 class="">Demo</h2><p>Learn more and see it in action at <a class="tc-tiddlylink-external" href="https://tiddlywiki.com/" rel="noopener noreferrer" target="_blank">https://tiddlywiki.com/</a></p><h2 class="">Developer Documentation</h2><p>Developer documentation is in progress at <a class="tc-tiddlylink-external" href="https://tiddlywiki.com/dev/" rel="noopener noreferrer" target="_blank">https://tiddlywiki.com/dev/</a></p><h2 class="">Pull Request Previews</h2><p>Pull request previews courtesy of <a class="tc-tiddlylink-external" href="https://netlify.com" rel="noopener noreferrer" target="_blank">Netlify</a></p><p><a href="https://www.netlify.com" rel="noopener noreferrer" target="_blank"><img alt="Deploys by Netlify" src="https://www.netlify.com/v3/img/components/netlify-light.svg"></a></p><h1 class="">Join the Community</h1><p>
|
||||
<h2 class="">User forums</h2><h3 class="">Talk TiddlyWiki</h3><p>As the official TiddlyWiki forum, Talk TiddlyWiki is a place to talk about TiddlyWiki: requests for help, <a class="tc-tiddlylink-external" href="https://talk.tiddlywiki.org/c/announcements/20" rel="noopener noreferrer" target="_blank">announcements</a> of new releases and plugins, debating new features, or just sharing experiences. You can participate via the associated website, or subscribe via email.</p><p><a class="tc-tiddlylink-external" href="https://talk.tiddlywiki.org/" rel="noopener noreferrer" target="_blank">https://talk.tiddlywiki.org/</a></p><h3 class="">Google Groups</h3><p>For the convenience of existing users, we also continue to operate the original TiddlyWiki group (hosted on Google Groups since 2005): <a class="tc-tiddlylink-external" href="https://groups.google.com/group/TiddlyWiki" rel="noopener noreferrer" target="_blank">https://groups.google.com/group/TiddlyWiki</a></p><h2 class="">Developer forums</h2><ul><li><a class="tc-tiddlylink-external" href="https://tiddlywiki.com/dev" rel="noopener noreferrer" target="_blank">tiddlywiki.com/dev</a> is the official developer documentation</li><li>Get involved in the <a class="tc-tiddlylink-external" href="https://github.com/TiddlyWiki/TiddlyWiki5" rel="noopener noreferrer" target="_blank">development on GitHub</a></li><li><a class="tc-tiddlylink-external" href="https://github.com/TiddlyWiki/TiddlyWiki5/discussions" rel="noopener noreferrer" target="_blank">GitHub Discussions</a> are for Q&A and open-ended discussion</li><li><a class="tc-tiddlylink-external" href="https://github.com/TiddlyWiki/TiddlyWiki5/issues" rel="noopener noreferrer" target="_blank">GitHub Issues</a> are for raising bug reports and proposing specific, actionable new ideas</li><li>See <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/Contributing.html">Contributing</a> for guidelines on how to contribute to the project.</li></ul><h2 class="">Other forums</h2><ul><li><a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki.html">TiddlyWiki</a> Subreddit: <a class="tc-tiddlylink-external" href="https://www.reddit.com/r/TiddlyWiki5/" rel="noopener noreferrer" target="_blank">/r/TiddlyWiki5</a></li><li>Chat on Discord at <a class="tc-tiddlylink-external" href="https://discord.gg/HFFZVQ8" rel="noopener noreferrer" target="_blank">https://discord.gg/HFFZVQ8</a></li></ul>
|
||||
</p><hr><h1 class="">Installing TiddlyWiki on Node.js</h1><p>TiddlyWiki is a <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/SingleFileApplication.html">SingleFileApplication</a>, which is easy to use. For advanced users and developers there is a possibility to use a Node.js client / server configuration. This configuration is also used to build the TiddlyWiki <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/SinglePageApplication.html">SinglePageApplication</a></p><ol><li>Install <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/Node.js.html">Node.js</a><ul><li>Linux: <blockquote><div><em>Debian/Ubuntu</em>:<br><code>apt install nodejs</code><br>May need to be followed up by:<br><code>apt install npm</code></div><div><em>Arch Linux</em><br><code>yay -S tiddlywiki</code> <br>(installs node and tiddlywiki)</div></blockquote></li><li>Mac<blockquote><div><code>brew install node</code></div></blockquote></li><li>Android<blockquote><div><a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/Serving%2520TW5%2520from%2520Android.html">Termux for Android</a></div></blockquote></li><li>Other <blockquote><div>See <a class="tc-tiddlylink-external" href="http://nodejs.org" rel="noopener noreferrer" target="_blank">http://nodejs.org</a></div></blockquote></li></ul></li><li>Open a command line terminal and type:<blockquote><div><code>npm install -g tiddlywiki</code></div><div>If it fails with an error you may need to re-run the command as an administrator:</div><div><code>sudo npm install -g tiddlywiki</code> (Mac/Linux)</div></blockquote></li><li>Ensure TiddlyWiki is installed by typing:<blockquote><div><code>tiddlywiki --version</code></div></blockquote><ul><li>In response, you should see <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki.html">TiddlyWiki</a> report its current version (eg "5.4.0". You may also see other debugging information reported.)</li></ul></li><li>Try it out:<ol><li><code>tiddlywiki mynewwiki --init server</code> to create a folder for a new wiki that includes server-related components</li><li><code>tiddlywiki mynewwiki --listen</code> to start <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki.html">TiddlyWiki</a></li><li>Visit <a class="tc-tiddlylink-external" href="http://127.0.0.1:8080/" rel="noopener noreferrer" target="_blank">http://127.0.0.1:8080/</a> in your browser</li><li>Try editing and creating tiddlers</li></ol></li><li>Optionally, make an offline copy:<ul><li>click the <span class="doc-icon"><svg class="tc-image-save-button-dynamic tc-image-button" height="22pt" viewBox="0 0 128 128" width="22pt">
|
||||
</p><hr><h1 class="">Installing TiddlyWiki on Node.js</h1><p>TiddlyWiki is a <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/SingleFileApplication.html">SingleFileApplication</a>, which is easy to use. For advanced users and developers there is a possibility to use a Node.js client / server configuration. This configuration is also used to build the TiddlyWiki <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/SinglePageApplication.html">SinglePageApplication</a></p><ol><li>Install <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/Node.js.html">Node.js</a><ul><li>Linux: <blockquote><div><em>Debian/Ubuntu</em>:<br><code>apt install nodejs</code><br>May need to be followed up by:<br><code>apt install npm</code></div><div><em>Arch Linux</em><br><code>yay -S tiddlywiki</code> <br>(installs node and tiddlywiki)</div></blockquote></li><li>Mac<blockquote><div><code>brew install node</code></div></blockquote></li><li>Android<blockquote><div><a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/Serving%2520TW5%2520from%2520Android.html">Termux for Android</a></div></blockquote></li><li>Other <blockquote><div>See <a class="tc-tiddlylink-external" href="http://nodejs.org" rel="noopener noreferrer" target="_blank">http://nodejs.org</a></div></blockquote></li></ul></li><li>Open a command line terminal and type:<blockquote><div><code>npm install -g tiddlywiki</code></div><div>If it fails with an error you may need to re-run the command as an administrator:</div><div><code>sudo npm install -g tiddlywiki</code> (Mac/Linux)</div></blockquote></li><li>Ensure TiddlyWiki is installed by typing:<blockquote><div><code>tiddlywiki --version</code></div></blockquote><ul><li>In response, you should see <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki.html">TiddlyWiki</a> report its current version (eg "5.4.1". You may also see other debugging information reported.)</li></ul></li><li>Try it out:<ol><li><code>tiddlywiki mynewwiki --init server</code> to create a folder for a new wiki that includes server-related components</li><li><code>tiddlywiki mynewwiki --listen</code> to start <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki.html">TiddlyWiki</a></li><li>Visit <a class="tc-tiddlylink-external" href="http://127.0.0.1:8080/" rel="noopener noreferrer" target="_blank">http://127.0.0.1:8080/</a> in your browser</li><li>Try editing and creating tiddlers</li></ol></li><li>Optionally, make an offline copy:<ul><li>click the <span class="doc-icon"><svg class="tc-image-save-button-dynamic tc-image-button" height="22pt" viewBox="0 0 128 128" width="22pt">
|
||||
<g class="tc-image-save-button-dynamic-clean">
|
||||
<path d="M120.783 34.33c4.641 8.862 7.266 18.948 7.266 29.646 0 35.347-28.653 64-64 64-35.346 0-64-28.653-64-64 0-35.346 28.654-64 64-64 18.808 0 35.72 8.113 47.43 21.03l2.68-2.68c3.13-3.13 8.197-3.132 11.321-.008 3.118 3.118 3.121 8.193-.007 11.32l-4.69 4.691zm-12.058 12.058a47.876 47.876 0 013.324 17.588c0 26.51-21.49 48-48 48s-48-21.49-48-48 21.49-48 48-48c14.39 0 27.3 6.332 36.098 16.362L58.941 73.544 41.976 56.578c-3.127-3.127-8.201-3.123-11.32-.005-3.123 3.124-3.119 8.194.006 11.319l22.617 22.617a7.992 7.992 0 005.659 2.347c2.05 0 4.101-.783 5.667-2.349l44.12-44.12z" fill-rule="evenodd"></path>
|
||||
</g>
|
||||
|
||||
Reference in New Issue
Block a user