mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2026-07-26 13:38:54 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ec36293df |
@@ -0,0 +1,69 @@
|
||||
# Repository Contribution Guidelines & Automation
|
||||
|
||||
This document establishes the single source of truth for repository contribution guidelines, optimized for AI context consumption, alongside a TiddlyWiki-based automation strategy to prevent source-of-truth drift.
|
||||
|
||||
---
|
||||
|
||||
## 📄 The High-Density Ruleset
|
||||
*Copy the content below into a single master tiddler titled `ProjectContributionRules` with the type `text/x-markdown`.*
|
||||
|
||||
```markdown
|
||||
# CONTRIBUTING
|
||||
|
||||
## AI & Comment Rules
|
||||
* Banned: Conversational filler, verbose explanations of standard APIs, or teaching basic programming logic.
|
||||
* Required: Prune all AI-generated comment blocks down to a single sentence maximum.
|
||||
* Focus: Comments must only state the "why" behind non-obvious engineering decisions, never the "how".
|
||||
* PR Descriptions: Write concise, bulleted summaries of what changed. Do not use AI-generated marketing/polite filler text.
|
||||
|
||||
### Code Comment Examples
|
||||
* BAD: // Use behavior:"instant" so that this animation's per-frame position updates are applied immediately and are NOT re-animated by a CSS `scroll-behavior: smooth` on the scrolling element (which would make the page lag behind and stutter).
|
||||
* GOOD: // Use 'instant' to prevent conflicts with CSS scroll-behavior: smooth.
|
||||
|
||||
## Workflow Rules
|
||||
* Branching: Base all feature branches on `main`. Use `feat/` or `fix/` prefixes.
|
||||
* Scope: Keep pull requests limited to a single logical change. Massive, multi-feature PRs will be rejected.
|
||||
* Testing: Run local tests and formatting checks before pushing.
|
||||
* Merging: All commits are squashed on merge.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ TiddlyWiki Build Integration
|
||||
To ensure this file is distributed automatically to all required locations without duplication, use TiddlyWiki's native command-line rendering tool.
|
||||
|
||||
### 1. Update `tiddlywiki.info`
|
||||
Add a dedicated `repo-configs` target to your project's `tiddlywiki.info` JSON file. This instructs TiddlyWiki to compile the master tiddler out to both human-facing and AI-facing configuration endpoints:
|
||||
|
||||
```json
|
||||
{
|
||||
"description": "Project build targets",
|
||||
"plugins": [
|
||||
"tiddlywiki/markdown"
|
||||
],
|
||||
"build": {
|
||||
"repo-configs": [
|
||||
"--rendertiddler", "ProjectContributionRules", "./CONTRIBUTING.md", "text/plain",
|
||||
"--rendertiddler", "ProjectContributionRules", "./.claudecode", "text/plain"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Update `package.json`
|
||||
Integrate the rendering command into your existing Node.js project lifecycle scripts so it triggers automatically during development builds or pre-commit checks:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"build:configs": "tiddlywiki --build repo-configs"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Why This Works
|
||||
* **Zero Duplication:** Edits are made exclusively within `ProjectContributionRules` inside the wiki workspace.
|
||||
* **Human Visibility:** Browsing GitHub automatically surfaces `CONTRIBUTING.md` to human contributors.
|
||||
* **Claude Code Ready:** When **Claude Code** sweeps the project root, it instantly reads the identical, strict engineering boundaries outputted to `.claudecode`.
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
# Default to the current version number for building the plugin library
|
||||
|
||||
if [ -z "$TW5_BUILD_VERSION" ]; then
|
||||
TW5_BUILD_VERSION=v5.5.0.
|
||||
TW5_BUILD_VERSION=v5.4.1.
|
||||
fi
|
||||
|
||||
echo "Using TW5_BUILD_VERSION as [$TW5_BUILD_VERSION]"
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,8 +0,0 @@
|
||||
title: TiddlyWiki Team
|
||||
tags: Community/Team
|
||||
modified: 20260708095630754
|
||||
created: 20260708095630754
|
||||
leader: @BurningTreeC
|
||||
team:
|
||||
|
||||
The TiddlyDesktop development repository is at https://github.com/TiddlyWiki/TiddlyDesktop
|
||||
@@ -9,14 +9,15 @@ Serve tiddlers over http
|
||||
|
||||
"use strict";
|
||||
|
||||
let fs, path, crypto, zlib, URL;
|
||||
let fs, url, path, querystring, crypto, zlib;
|
||||
|
||||
if($tw.node) {
|
||||
fs = require("fs"),
|
||||
url = require("url"),
|
||||
path = require("path"),
|
||||
querystring = require("querystring"),
|
||||
crypto = require("crypto"),
|
||||
zlib = require("zlib");
|
||||
URL = require("url").URL;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -259,8 +260,8 @@ Server.prototype.requestHandler = function(request,response,options) {
|
||||
state.wiki = options.wiki || self.wiki;
|
||||
state.boot = options.boot || self.boot;
|
||||
state.server = self;
|
||||
state.urlInfo = new URL(request.url, "http://localhost");
|
||||
state.queryParameters = Object.fromEntries(state.urlInfo.searchParams);
|
||||
state.urlInfo = url.parse(request.url);
|
||||
state.queryParameters = querystring.parse(state.urlInfo.query);
|
||||
state.pathPrefix = options.pathPrefix || this.get("path-prefix") || "";
|
||||
// Enable CORS
|
||||
if(this.corsEnable) {
|
||||
|
||||
@@ -255,5 +255,4 @@ ViewTemplateTags/Caption: View Template Tags
|
||||
ViewTemplateTags/Hint: This rule cascade is used by the default view template to dynamically choose the template for displaying the tags area of a tiddler.
|
||||
WikiInformation/Caption: Wiki Information
|
||||
WikiInformation/Hint: This page summarises high level information about the configuration of this ~TiddlyWiki. It is designed to enable users to quickly share relevant aspects of the configuration of their ~TiddlyWiki with others, for example when seeking help in one of the forums. No private or personal information is included, and nothing is shared without being explicitly copied and pasted elsewhere
|
||||
WikiInformation/Drag/Caption: Drag this link to copy this tool to another wiki
|
||||
WikiInformation/Generate/Caption: Click to generate wiki information report
|
||||
WikiInformation/Drag/Caption: Drag this link to copy this tool to another wiki
|
||||
@@ -9,56 +9,69 @@ Filter operators for manipulating the current selection list
|
||||
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Fetch titles from the current list
|
||||
*/
|
||||
const prepare_results = (source) => {
|
||||
const results = [];
|
||||
source((tiddler,title) => results.push(title));
|
||||
return results;
|
||||
};
|
||||
|
||||
/*
|
||||
Order a list
|
||||
*/
|
||||
exports.order = function(source,operator,options) {
|
||||
const results = prepare_results(source);
|
||||
return operator.operand.toLowerCase() === "reverse" ?
|
||||
results.reverse() :
|
||||
results;
|
||||
var results = [];
|
||||
if(operator.operand.toLowerCase() === "reverse") {
|
||||
source(function(tiddler,title) {
|
||||
results.unshift(title);
|
||||
});
|
||||
} else {
|
||||
source(function(tiddler,title) {
|
||||
results.push(title);
|
||||
});
|
||||
}
|
||||
return results;
|
||||
};
|
||||
|
||||
/*
|
||||
Reverse list
|
||||
*/
|
||||
exports.reverse = function(source,operator,options) {
|
||||
return prepare_results(source).reverse();
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
results.unshift(title);
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
/*
|
||||
First entry/entries in list
|
||||
*/
|
||||
exports.first = function(source,operator,options) {
|
||||
const count = $tw.utils.getInt(operator.operand,1);
|
||||
return prepare_results(source).slice(0,count);
|
||||
var count = $tw.utils.getInt(operator.operand,1),
|
||||
results = [];
|
||||
source(function(tiddler,title) {
|
||||
results.push(title);
|
||||
});
|
||||
return results.slice(0,count);
|
||||
};
|
||||
|
||||
/*
|
||||
Last entry/entries in list
|
||||
*/
|
||||
exports.last = function(source,operator,options) {
|
||||
const count = $tw.utils.getInt(operator.operand,1);
|
||||
return count === 0 ?
|
||||
[] :
|
||||
prepare_results(source).slice(-count);
|
||||
var count = $tw.utils.getInt(operator.operand,1),
|
||||
results = [];
|
||||
if(count === 0) return results;
|
||||
source(function(tiddler,title) {
|
||||
results.push(title);
|
||||
});
|
||||
return results.slice(-count);
|
||||
};
|
||||
|
||||
/*
|
||||
All but the first entry/entries of the list
|
||||
*/
|
||||
exports.rest = function(source,operator,options) {
|
||||
const count = $tw.utils.getInt(operator.operand,1);
|
||||
return prepare_results(source).slice(count);
|
||||
var count = $tw.utils.getInt(operator.operand,1),
|
||||
results = [];
|
||||
source(function(tiddler,title) {
|
||||
results.push(title);
|
||||
});
|
||||
return results.slice(count);
|
||||
};
|
||||
exports.butfirst = exports.rest;
|
||||
exports.bf = exports.rest;
|
||||
@@ -67,9 +80,12 @@ exports.bf = exports.rest;
|
||||
All but the last entry/entries of the list
|
||||
*/
|
||||
exports.butlast = function(source,operator,options) {
|
||||
const count = $tw.utils.getInt(operator.operand,1),
|
||||
results = prepare_results(source),
|
||||
index = count === 0 ? results.length : -count;
|
||||
var count = $tw.utils.getInt(operator.operand,1),
|
||||
results = [];
|
||||
source(function(tiddler,title) {
|
||||
results.push(title);
|
||||
});
|
||||
var index = count === 0 ? results.length : -count;
|
||||
return results.slice(0,index);
|
||||
};
|
||||
exports.bl = exports.butlast;
|
||||
@@ -78,14 +94,22 @@ exports.bl = exports.butlast;
|
||||
The nth member of the list
|
||||
*/
|
||||
exports.nth = function(source,operator,options) {
|
||||
const count = $tw.utils.getInt(operator.operand,1);
|
||||
return prepare_results(source).slice(count - 1,count);
|
||||
var count = $tw.utils.getInt(operator.operand,1),
|
||||
results = [];
|
||||
source(function(tiddler,title) {
|
||||
results.push(title);
|
||||
});
|
||||
return results.slice(count - 1,count);
|
||||
};
|
||||
|
||||
/*
|
||||
The zero based nth member of the list
|
||||
*/
|
||||
exports.zth = function(source,operator,options) {
|
||||
const count = $tw.utils.getInt(operator.operand,0);
|
||||
return prepare_results(source).slice(count,count + 1);
|
||||
};
|
||||
var count = $tw.utils.getInt(operator.operand,0),
|
||||
results = [];
|
||||
source(function(tiddler,title) {
|
||||
results.push(title);
|
||||
});
|
||||
return results.slice(count,count + 1);
|
||||
};
|
||||
|
||||
@@ -13,23 +13,37 @@ Filter operator for checking if a title starts with a prefix
|
||||
Export our filter function
|
||||
*/
|
||||
exports.prefix = function(source,operator,options) {
|
||||
const results = [],
|
||||
suffixes = (operator.suffixes || [])[0] || [],
|
||||
caseInsensitive = suffixes.indexOf("caseinsensitive") !== -1,
|
||||
negate = operator.prefix === "!";
|
||||
|
||||
const operand = caseInsensitive ?
|
||||
operator.operand.toLowerCase() :
|
||||
operator.operand;
|
||||
|
||||
source((tiddler,title) => {
|
||||
const value = caseInsensitive ? title.toLowerCase() : title;
|
||||
const matches = value.startsWith(operand);
|
||||
|
||||
if(negate ? !matches : matches) {
|
||||
results.push(title);
|
||||
var results = [],
|
||||
suffixes = (operator.suffixes || [])[0] || [];
|
||||
if(suffixes.indexOf("caseinsensitive") !== -1) {
|
||||
var operand = operator.operand.toLowerCase();
|
||||
if(operator.prefix === "!") {
|
||||
source(function(tiddler,title) {
|
||||
if(title.toLowerCase().substr(0,operand.length) !== operand) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
source(function(tiddler,title) {
|
||||
if(title.toLowerCase().substr(0,operand.length) === operand) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
if(operator.prefix === "!") {
|
||||
source(function(tiddler,title) {
|
||||
if(title.substr(0,operator.operand.length) !== operator.operand) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
source(function(tiddler,title) {
|
||||
if(title.substr(0,operator.operand.length) === operator.operand) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return results;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -22,11 +22,12 @@ exports.tag = function(source,operator,options) {
|
||||
});
|
||||
} else {
|
||||
// Old semantics:
|
||||
var tiddlers;
|
||||
if(operator.prefix === "!") {
|
||||
// Returns a copy of the input if operator.operand is missing
|
||||
const excludeTagSet = new Set(options.wiki.getTiddlersWithTag(operator.operand));
|
||||
tiddlers = options.wiki.getTiddlersWithTag(operator.operand);
|
||||
source(function(tiddler,title) {
|
||||
if(!excludeTagSet.has(title)) {
|
||||
if(tiddlers.indexOf(title) === -1) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
@@ -38,9 +39,9 @@ exports.tag = function(source,operator,options) {
|
||||
return indexedResults;
|
||||
}
|
||||
} else {
|
||||
const includeTagSet = new Set(options.wiki.getTiddlersWithTag(operator.operand));
|
||||
tiddlers = options.wiki.getTiddlersWithTag(operator.operand);
|
||||
source(function(tiddler,title) {
|
||||
if(includeTagSet.has(title)) {
|
||||
if(tiddlers.indexOf(title) !== -1) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -11,18 +11,20 @@ Extended filter operators to manipulate the current list.
|
||||
|
||||
/*
|
||||
Fetch titles from the current list
|
||||
*/
|
||||
const prepare_results = (source) => {
|
||||
const results = [];
|
||||
source((tiddler,title) => results.push(title));
|
||||
*/
|
||||
var prepare_results = function (source) {
|
||||
var results = [];
|
||||
source(function (tiddler, title) {
|
||||
results.push(title);
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
/*
|
||||
Moves a number of items from the tail of the current list before the item named in the operand
|
||||
*/
|
||||
exports.putbefore = function(source,operator) {
|
||||
const results = prepare_results(source),
|
||||
*/
|
||||
exports.putbefore = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
index = results.indexOf(operator.operand),
|
||||
count = $tw.utils.getInt(operator.suffix,1);
|
||||
return (index === -1) ?
|
||||
@@ -32,9 +34,9 @@ exports.putbefore = function(source,operator) {
|
||||
|
||||
/*
|
||||
Moves a number of items from the tail of the current list after the item named in the operand
|
||||
*/
|
||||
exports.putafter = function(source,operator) {
|
||||
const results = prepare_results(source),
|
||||
*/
|
||||
exports.putafter = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
index = results.indexOf(operator.operand),
|
||||
count = $tw.utils.getInt(operator.suffix,1);
|
||||
return (index === -1) ?
|
||||
@@ -44,9 +46,9 @@ exports.putafter = function(source,operator) {
|
||||
|
||||
/*
|
||||
Replaces the item named in the operand with a number of items from the tail of the current list
|
||||
*/
|
||||
exports.replace = function(source,operator) {
|
||||
const results = prepare_results(source),
|
||||
*/
|
||||
exports.replace = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
index = results.indexOf(operator.operand),
|
||||
count = $tw.utils.getInt(operator.suffix,1);
|
||||
return (index === -1) ?
|
||||
@@ -56,39 +58,39 @@ exports.replace = function(source,operator) {
|
||||
|
||||
/*
|
||||
Moves a number of items from the tail of the current list to the head of the list
|
||||
*/
|
||||
exports.putfirst = function(source,operator) {
|
||||
const results = prepare_results(source),
|
||||
*/
|
||||
exports.putfirst = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
count = $tw.utils.getInt(operator.suffix,1);
|
||||
return [...results.slice(-count), ...results.slice(0, -count)];
|
||||
return results.slice(-count).concat(results.slice(0, -count));
|
||||
};
|
||||
|
||||
/*
|
||||
Moves a number of items from the head of the current list to the tail of the list
|
||||
*/
|
||||
exports.putlast = function(source,operator) {
|
||||
const results = prepare_results(source),
|
||||
*/
|
||||
exports.putlast = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
count = $tw.utils.getInt(operator.suffix,1);
|
||||
return [...results.slice(count), ...results.slice(0, count)];
|
||||
return results.slice(count).concat(results.slice(0, count));
|
||||
};
|
||||
|
||||
/*
|
||||
Moves the item named in the operand a number of places forward or backward in the list
|
||||
*/
|
||||
exports.move = function(source,operator) {
|
||||
const results = prepare_results(source),
|
||||
*/
|
||||
exports.move = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
index = results.indexOf(operator.operand),
|
||||
count = $tw.utils.getInt(operator.suffix,1),
|
||||
marker = results.splice(index, 1),
|
||||
offset = (index + count) > 0 ? index + count : 0;
|
||||
offset = (index + count) > 0 ? index + count : 0;
|
||||
return results.slice(0, offset).concat(marker).concat(results.slice(offset));
|
||||
};
|
||||
|
||||
/*
|
||||
Returns the items from the current list that are after the item named in the operand
|
||||
*/
|
||||
exports.allafter = function(source,operator) {
|
||||
const results = prepare_results(source),
|
||||
*/
|
||||
exports.allafter = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
index = results.indexOf(operator.operand);
|
||||
return (index === -1) ? [] :
|
||||
(operator.suffix) ? results.slice(index) :
|
||||
@@ -97,9 +99,9 @@ exports.allafter = function(source,operator) {
|
||||
|
||||
/*
|
||||
Returns the items from the current list that are before the item named in the operand
|
||||
*/
|
||||
exports.allbefore = function(source,operator) {
|
||||
const results = prepare_results(source),
|
||||
*/
|
||||
exports.allbefore = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
index = results.indexOf(operator.operand);
|
||||
return (index === -1) ? [] :
|
||||
(operator.suffix) ? results.slice(0, index + 1) :
|
||||
@@ -108,9 +110,9 @@ exports.allbefore = function(source,operator) {
|
||||
|
||||
/*
|
||||
Appends the items listed in the operand array to the tail of the current list
|
||||
*/
|
||||
exports.append = function(source,operator) {
|
||||
const append = $tw.utils.parseStringArray(operator.operand,"true"),
|
||||
*/
|
||||
exports.append = function (source, operator) {
|
||||
var append = $tw.utils.parseStringArray(operator.operand, "true"),
|
||||
results = prepare_results(source),
|
||||
count = parseInt(operator.suffix) || append.length;
|
||||
return (append.length === 0) ? results :
|
||||
@@ -120,9 +122,9 @@ exports.append = function(source,operator) {
|
||||
|
||||
/*
|
||||
Prepends the items listed in the operand array to the head of the current list
|
||||
*/
|
||||
exports.prepend = function(source,operator) {
|
||||
const prepend = $tw.utils.parseStringArray(operator.operand,"true"),
|
||||
*/
|
||||
exports.prepend = function (source, operator) {
|
||||
var prepend = $tw.utils.parseStringArray(operator.operand, "true"),
|
||||
results = prepare_results(source),
|
||||
count = $tw.utils.getInt(operator.suffix,prepend.length);
|
||||
return (prepend.length === 0) ? results :
|
||||
@@ -132,19 +134,21 @@ exports.prepend = function(source,operator) {
|
||||
|
||||
/*
|
||||
Returns all items from the current list except the items listed in the operand array
|
||||
*/
|
||||
exports.remove = function(source, operator) {
|
||||
const array = $tw.utils.parseStringArray(operator.operand, "true"),
|
||||
results = prepare_results(source);
|
||||
|
||||
if(array.length === 0) {
|
||||
return results;
|
||||
}
|
||||
const count = parseInt(operator.suffix, 10) || array.length,
|
||||
targetItems = operator.prefix ? array.slice(-count).reverse() : array.slice(0, count);
|
||||
|
||||
for(const item of targetItems) {
|
||||
const index = results.indexOf(item);
|
||||
*/
|
||||
exports.remove = function (source, operator) {
|
||||
var array = $tw.utils.parseStringArray(operator.operand, "true"),
|
||||
results = prepare_results(source),
|
||||
count = parseInt(operator.suffix) || array.length,
|
||||
p,
|
||||
len,
|
||||
index;
|
||||
len = array.length - 1;
|
||||
for(p = 0; p < count; ++p) {
|
||||
if(operator.prefix) {
|
||||
index = results.indexOf(array[len - p]);
|
||||
} else {
|
||||
index = results.indexOf(array[p]);
|
||||
}
|
||||
if(index !== -1) {
|
||||
results.splice(index, 1);
|
||||
}
|
||||
@@ -154,32 +158,40 @@ exports.remove = function(source, operator) {
|
||||
|
||||
/*
|
||||
Returns all items from the current list sorted in the order of the items in the operand array
|
||||
*/
|
||||
exports.sortby = function(source,operator) {
|
||||
const results = prepare_results(source);
|
||||
*/
|
||||
exports.sortby = function (source, operator) {
|
||||
var results = prepare_results(source);
|
||||
if(!results || results.length < 2) {
|
||||
return results;
|
||||
}
|
||||
const lookup = $tw.utils.parseStringArray(operator.operand,"true");
|
||||
return results.sort((a,b) => lookup.indexOf(a) - lookup.indexOf(b));
|
||||
var lookup = $tw.utils.parseStringArray(operator.operand, "true");
|
||||
results.sort(function (a, b) {
|
||||
return lookup.indexOf(a) - lookup.indexOf(b);
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Removes all duplicate items from the current list
|
||||
*/
|
||||
exports.unique = function(source, operator) {
|
||||
return Array.from(new Set(prepare_results(source)));
|
||||
*/
|
||||
exports.unique = function (source, operator) {
|
||||
var results = prepare_results(source);
|
||||
var set = results.reduce(function (a, b) {
|
||||
if(a.indexOf(b) < 0) {
|
||||
a.push(b);
|
||||
}
|
||||
return a;
|
||||
}, []);
|
||||
return set;
|
||||
};
|
||||
|
||||
const cycleValueInArray = function(results,operands,stepSize) {
|
||||
let resultsIndex,
|
||||
var cycleValueInArray = function(results,operands,stepSize) {
|
||||
var resultsIndex,
|
||||
step = stepSize || 1,
|
||||
i = 0,
|
||||
opLength = operands.length,
|
||||
nextOperandIndex;
|
||||
const opLength = operands.length;
|
||||
|
||||
for(; i < opLength; i++) {
|
||||
for(i; i < opLength; i++) {
|
||||
resultsIndex = results.indexOf(operands[i]);
|
||||
if(resultsIndex !== -1) {
|
||||
break;
|
||||
@@ -201,19 +213,18 @@ const cycleValueInArray = function(results,operands,stepSize) {
|
||||
|
||||
/*
|
||||
Toggles an item in the current list.
|
||||
*/
|
||||
*/
|
||||
exports.toggle = function(source,operator) {
|
||||
return cycleValueInArray(prepare_results(source),operator.operands);
|
||||
};
|
||||
|
||||
exports.cycle = function(source,operator) {
|
||||
const results = prepare_results(source),
|
||||
operands = operator.operand.length ? $tw.utils.parseStringArray(operator.operand,"true") : [""];
|
||||
let step = $tw.utils.getInt(operator.operands[1] || "",1);
|
||||
|
||||
var results = prepare_results(source),
|
||||
operands = (operator.operand.length ? $tw.utils.parseStringArray(operator.operand, "true") : [""]),
|
||||
step = $tw.utils.getInt(operator.operands[1]||"",1);
|
||||
if(step < 0) {
|
||||
operands.reverse();
|
||||
step = Math.abs(step);
|
||||
}
|
||||
return cycleValueInArray(results,operands,step);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -88,11 +88,10 @@ BackSubIndexer.prototype.update = function(updateDescriptor) {
|
||||
var newTargets = [],
|
||||
oldTargets = [],
|
||||
self = this;
|
||||
// System tiddlers are never indexed as sources, matching the _init() scan
|
||||
if(updateDescriptor.old.exists && !this.wiki.isSystemTiddler(updateDescriptor.old.tiddler.fields.title)) {
|
||||
if(updateDescriptor.old.exists) {
|
||||
oldTargets = this._getTarget(updateDescriptor.old.tiddler);
|
||||
}
|
||||
if(updateDescriptor.new.exists && !this.wiki.isSystemTiddler(updateDescriptor.new.tiddler.fields.title)) {
|
||||
if(updateDescriptor.new.exists) {
|
||||
newTargets = this._getTarget(updateDescriptor.new.tiddler);
|
||||
}
|
||||
|
||||
|
||||
@@ -528,79 +528,67 @@ exports.parseAttribute = function(source,pos) {
|
||||
pos = token.end;
|
||||
// Skip whitespace
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
do {
|
||||
// Look for a string literal
|
||||
var stringLiteral = $tw.utils.parseStringLiteral(source,pos);
|
||||
if(stringLiteral) {
|
||||
pos = stringLiteral.end;
|
||||
node.type = "string";
|
||||
node.value = stringLiteral.value;
|
||||
break;
|
||||
}
|
||||
|
||||
// Look for a string literal
|
||||
var stringLiteral = $tw.utils.parseStringLiteral(source,pos);
|
||||
if(stringLiteral) {
|
||||
pos = stringLiteral.end;
|
||||
node.type = "string";
|
||||
node.value = stringLiteral.value;
|
||||
} else {
|
||||
// Look for a filtered value
|
||||
var filteredValue = $tw.utils.parseTokenRegExp(source,pos,reFilteredValue);
|
||||
if(filteredValue) {
|
||||
pos = filteredValue.end;
|
||||
node.type = "filtered";
|
||||
node.filter = filteredValue.match[1];
|
||||
break;
|
||||
} else {
|
||||
// Look for an indirect value
|
||||
var indirectValue = $tw.utils.parseTokenRegExp(source,pos,reIndirectValue);
|
||||
if(indirectValue) {
|
||||
pos = indirectValue.end;
|
||||
node.type = "indirect";
|
||||
node.textReference = indirectValue.match[1];
|
||||
} else {
|
||||
// Look for a macro invocation value
|
||||
var macroInvocation = $tw.utils.parseMacroInvocationAsTransclusion(source,pos);
|
||||
if(macroInvocation) {
|
||||
pos = macroInvocation.end;
|
||||
node.type = "macro";
|
||||
node.value = macroInvocation;
|
||||
} else {
|
||||
// Look for an MVV reference value
|
||||
var mvvReference = $tw.utils.parseMVVReferenceAsTransclusion(source,pos);
|
||||
if(mvvReference) {
|
||||
pos = mvvReference.end;
|
||||
node.type = "macro";
|
||||
node.value = mvvReference;
|
||||
node.isMVV = true;
|
||||
} else {
|
||||
var substitutedValue = $tw.utils.parseTokenRegExp(source,pos,reSubstitutedValue);
|
||||
if(substitutedValue) {
|
||||
pos = substitutedValue.end;
|
||||
node.type = "substituted";
|
||||
node.rawValue = substitutedValue.match[1] || substitutedValue.match[2];
|
||||
} else {
|
||||
// Look for a unquoted value
|
||||
var unquotedValue = $tw.utils.parseTokenRegExp(source,pos,reUnquotedAttribute);
|
||||
if(unquotedValue) {
|
||||
pos = unquotedValue.end;
|
||||
node.type = "string";
|
||||
node.value = unquotedValue.match[1];
|
||||
} else if(source.charAt(pos) === "<" && source.charAt(pos + 1) === "<" && source.indexOf(">>",pos) !== -1) {
|
||||
// Value looks like a macro invocation (starts with << with a closing >> ahead) but does not parse as one. Return null so the enclosing tag fails to parse rather than silently binding the attribute to "true" and treating the remainder as further attributes (restores v5.3.8 behaviour)
|
||||
return null;
|
||||
} else {
|
||||
node.type = "string";
|
||||
node.value = "true";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look for an indirect value
|
||||
var indirectValue = $tw.utils.parseTokenRegExp(source,pos,reIndirectValue);
|
||||
if(indirectValue) {
|
||||
pos = indirectValue.end;
|
||||
node.type = "indirect";
|
||||
node.textReference = indirectValue.match[1];
|
||||
break;
|
||||
}
|
||||
|
||||
// Look for a macro invocation value
|
||||
var macroInvocation = $tw.utils.parseMacroInvocationAsTransclusion(source,pos);
|
||||
if(macroInvocation) {
|
||||
pos = macroInvocation.end;
|
||||
node.type = "macro";
|
||||
node.value = macroInvocation;
|
||||
break;
|
||||
}
|
||||
|
||||
// Look for an MVV reference value
|
||||
var mvvReference = $tw.utils.parseMVVReferenceAsTransclusion(source,pos);
|
||||
if(mvvReference) {
|
||||
pos = mvvReference.end;
|
||||
node.type = "macro";
|
||||
node.value = mvvReference;
|
||||
node.isMVV = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Look for a substituted value
|
||||
var substitutedValue = $tw.utils.parseTokenRegExp(source,pos,reSubstitutedValue);
|
||||
if(substitutedValue) {
|
||||
pos = substitutedValue.end;
|
||||
node.type = "substituted";
|
||||
node.rawValue = substitutedValue.match[1] || substitutedValue.match[2];
|
||||
break;
|
||||
}
|
||||
|
||||
// Look for a unquoted value
|
||||
var unquotedValue = $tw.utils.parseTokenRegExp(source,pos,reUnquotedAttribute);
|
||||
if(unquotedValue) {
|
||||
pos = unquotedValue.end;
|
||||
node.type = "string";
|
||||
node.value = unquotedValue.match[1];
|
||||
break;
|
||||
}
|
||||
|
||||
if(source.charAt(pos) === "<" && source.charAt(pos + 1) === "<" && source.indexOf(">>",pos) !== -1) {
|
||||
// Value looks like a macro invocation (starts with << with a closing >> ahead) but does not parse as one. Return null so the enclosing tag fails to parse rather than silently binding the attribute to "true" and treating the remainder as further attributes (restores v5.3.8 behaviour)
|
||||
return null;
|
||||
}
|
||||
|
||||
node.type = "string";
|
||||
node.value = "true";
|
||||
} while(false);
|
||||
}
|
||||
} else {
|
||||
// If there is no equals sign or colon, then this is an attribute with no value, defaulting to "true"
|
||||
node.type = "string";
|
||||
|
||||
@@ -31,16 +31,13 @@ exports.parse = function() {
|
||||
reEnd.lastIndex = this.parser.pos;
|
||||
var match = reEnd.exec(this.parser.source),
|
||||
text,
|
||||
start = this.parser.pos,
|
||||
textEnd;
|
||||
start = this.parser.pos;
|
||||
// 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 [{
|
||||
@@ -50,7 +47,7 @@ exports.parse = function() {
|
||||
type: "text",
|
||||
text: text,
|
||||
start: start,
|
||||
end: textEnd
|
||||
end: this.parser.pos
|
||||
}]
|
||||
}];
|
||||
};
|
||||
|
||||
@@ -32,8 +32,7 @@ exports.parse = function() {
|
||||
this.parser.pos = this.matchRegExp.lastIndex;
|
||||
// Create the link unless it is suppressed
|
||||
if(this.match[0].substr(0,1) === "~") {
|
||||
// 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}];
|
||||
return [{type: "text", text: this.match[0].substr(1), start: start, 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.pos;
|
||||
var filterStart = this.parser.source;
|
||||
var match = reMatch.exec(this.parser.source);
|
||||
this.parser.pos = reMatch.lastIndex;
|
||||
// Parse tree nodes to return
|
||||
|
||||
@@ -27,11 +27,9 @@ Parse the most recent match
|
||||
*/
|
||||
exports.parse = function() {
|
||||
// Get the details of the match
|
||||
var linkText = this.match[0],
|
||||
// Start after the suppressing "~" so the span matches the plain text
|
||||
start = this.parser.pos + 1;
|
||||
var linkText = this.match[0];
|
||||
// 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), start: start, end: this.parser.pos}];
|
||||
return [{type: "text", text: linkText.substr(1)}];
|
||||
};
|
||||
|
||||
@@ -115,9 +115,7 @@ var TW_Element = function(tag, namespace) {
|
||||
this.children = [];
|
||||
this._style = {}; // Internal style object
|
||||
this.style = new TW_Style(this); // Proxy for style management
|
||||
// createElementNS with empty-string or null normalises to null (no namespace) per spec.
|
||||
// https://dom.spec.whatwg.org/#dom-document-createelementns
|
||||
this.namespaceURI = namespace !== undefined ? (namespace || null) : "http://www.w3.org/1999/xhtml";
|
||||
this.namespaceURI = namespace || "http://www.w3.org/1999/xhtml";
|
||||
};
|
||||
|
||||
|
||||
@@ -208,16 +206,7 @@ TW_Element.prototype.addEventListener = function(type,listener,useCapture) {
|
||||
|
||||
Object.defineProperty(TW_Element.prototype, "tagName", {
|
||||
get: function() {
|
||||
if(!this.tag) {
|
||||
return "";
|
||||
}
|
||||
// HTML elements report uppercase tagName per DOM spec. Other namespaces
|
||||
// preserve case. Fakedom only models HTML documents.
|
||||
// https://dom.spec.whatwg.org/#dom-element-tagname
|
||||
if(this.namespaceURI === "http://www.w3.org/1999/xhtml") {
|
||||
return this.tag.toUpperCase();
|
||||
}
|
||||
return this.tag;
|
||||
return this.tag || "";
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -243,28 +243,8 @@ exports.slowInSlowOut = function(t) {
|
||||
};
|
||||
|
||||
exports.copyObjectPropertiesSafe = function(object) {
|
||||
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;
|
||||
}
|
||||
const seen = new Set(),
|
||||
isDOMElement = (value) => value instanceof Node || value instanceof Window;
|
||||
|
||||
function safeCopy(obj) {
|
||||
// skip circular references
|
||||
@@ -275,6 +255,10 @@ 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) => {
|
||||
@@ -282,11 +266,7 @@ exports.copyObjectPropertiesSafe = function(object) {
|
||||
return value === undefined ? null : value;
|
||||
});
|
||||
}
|
||||
// skip DOM elements
|
||||
if(isDOMElement(obj)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
seen.add(obj);
|
||||
const copy = {};
|
||||
let key,
|
||||
|
||||
@@ -22,7 +22,7 @@ Inherit from the base widget class
|
||||
DiffTextWidget.prototype = new Widget();
|
||||
|
||||
DiffTextWidget.prototype.invisibleCharacters = {
|
||||
"\n": "↲\n",
|
||||
"\n": "↩︎\n",
|
||||
"\r": "⇠",
|
||||
"\t": "⇥\t"
|
||||
};
|
||||
|
||||
@@ -4,15 +4,13 @@ description: create a new journal tiddler
|
||||
|
||||
\whitespace trim
|
||||
\function get-tags() [<textFieldTags>] [<tagsFieldTags>] +[join[ ]]
|
||||
<$let journalTitle=<<now format={{$:/config/NewJournal/Title}}>>
|
||||
textFieldTags={{$:/config/NewJournal/Tags}}
|
||||
tagsFieldTags={{$:/config/NewJournal/Tags!!tags}}
|
||||
journalText={{$:/config/NewJournal/Text}}
|
||||
>
|
||||
<$let journalTitleTemplate={{$:/config/NewJournal/Title}} textFieldTags={{$:/config/NewJournal/Tags}} tagsFieldTags={{$:/config/NewJournal/Tags!!tags}} journalText={{$:/config/NewJournal/Text}}>
|
||||
<$wikify name="journalTitle" text="<$transclude $variable='now' format=<<journalTitleTemplate>>/>">
|
||||
<$reveal type="nomatch" state=<<journalTitle>> text="">
|
||||
<$action-sendmessage $message="tm-new-tiddler" title=<<journalTitle>> tags=<<get-tags>> text={{{ [<journalTitle>get[]] }}}/>
|
||||
</$reveal>
|
||||
<$reveal type="match" state=<<journalTitle>> text="">
|
||||
<$action-sendmessage $message="tm-new-tiddler" title=<<journalTitle>> tags=<<get-tags>> text=<<journalText>>/>
|
||||
</$reveal>
|
||||
</$wikify>
|
||||
</$let>
|
||||
|
||||
@@ -6,23 +6,30 @@ tags: $:/tags/EditTemplate
|
||||
\procedure lingo-base() $:/language/EditTemplate/
|
||||
|
||||
\procedure tag-body-inner(colour,fallbackTarget,colourA,colourB,icon,tagField:"tags")
|
||||
<$let foregroundColor=<<contrastcolour target=<<colour>> fallbackTarget=<<fallbackTarget>> colourA=<<colourA>> colourB=<<colourB>> >>
|
||||
backgroundColor=<<colour>>
|
||||
>
|
||||
<span class="tc-tag-label tc-tag-list-item tc-small-gap-right"
|
||||
data-tag-title=<<currentTiddler>>
|
||||
style=`color:$(foregroundColor)$; fill:$(foregroundColor)$; background-color:$(backgroundColor)$;`
|
||||
>
|
||||
<$transclude tiddler=<<icon>>/>
|
||||
<$view field="title" format="text"/>
|
||||
<$button class="tc-btn-invisible tc-remove-tag-button"
|
||||
style.fill=<<foregroundColor>>
|
||||
<$wikify name="foregroundColor"
|
||||
text="""<$macrocall $name="contrastcolour"
|
||||
target=<<colour>>
|
||||
fallbackTarget=<<fallbackTarget>>
|
||||
colourA=<<colourA>>
|
||||
colourB=<<colourB>>/>
|
||||
"""
|
||||
>
|
||||
<$let backgroundColor=<<colour>> >
|
||||
<span class="tc-tag-label tc-tag-list-item tc-small-gap-right"
|
||||
data-tag-title=<<currentTiddler>>
|
||||
style=`color:$(foregroundColor)$; fill:$(foregroundColor)$; background-color:$(backgroundColor)$;`
|
||||
>
|
||||
<$action-listops $tiddler=<<saveTiddler>> $field=<<tagField>> $subfilter="-[{!!title}]"/>
|
||||
{{$:/core/images/close-button}}
|
||||
</$button>
|
||||
</span>
|
||||
</$let>
|
||||
<$transclude tiddler=<<icon>>/>
|
||||
<$view field="title" format="text"/>
|
||||
<$button class="tc-btn-invisible tc-remove-tag-button"
|
||||
style.fill=<<foregroundColor>>
|
||||
>
|
||||
<$action-listops $tiddler=<<saveTiddler>> $field=<<tagField>> $subfilter="-[{!!title}]"/>
|
||||
{{$:/core/images/close-button}}
|
||||
</$button>
|
||||
</span>
|
||||
</$let>
|
||||
</$wikify>
|
||||
\end
|
||||
|
||||
\procedure tag-body(colour,palette,icon,tagField:"tags")
|
||||
|
||||
@@ -16,13 +16,15 @@ title: $:/core/ui/TagPickerTagTemplate
|
||||
<$set name="backgroundColor"
|
||||
value={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}}
|
||||
>
|
||||
<$let foregroundColor=<<contrastcolour target=<<backgroundColor>> fallbackTarget=<<fallbackTarget>> colourA=<<colourA>> colourB=<<colourB>> >> >
|
||||
<$wikify name="foregroundColor"
|
||||
text="""<$macrocall $name="contrastcolour" target=<<backgroundColor>> fallbackTarget=<<fallbackTarget>> colourA=<<colourA>> colourB=<<colourB>>/>"""
|
||||
>
|
||||
<span class="tc-tag-label tc-btn-invisible"
|
||||
style=<<tag-pill-styles>>
|
||||
data-tag-title=<<currentTiddler>>
|
||||
>
|
||||
{{||$:/core/ui/TiddlerIcon}}<$view field="title" format="text"/>
|
||||
</span>
|
||||
</$let>
|
||||
</$wikify>
|
||||
</$set>
|
||||
</$button>
|
||||
|
||||
@@ -6,6 +6,7 @@ description: {{$:/language/Buttons/NewJournalHere/Hint}}
|
||||
\whitespace trim
|
||||
\procedure journalButton()
|
||||
<$button tooltip={{$:/language/Buttons/NewJournalHere/Hint}} aria-label={{$:/language/Buttons/NewJournalHere/Caption}} class=<<tv-config-toolbar-class>>>
|
||||
<$wikify name="journalTitle" text="""<$transclude $variable="now" format=<<journalTitleTemplate>>/>""">
|
||||
<$action-sendmessage $message="tm-new-tiddler" title=<<journalTitle>> tags=`[[$(currentTiddlerTag)$]] $(journalTags)$`/>
|
||||
<%if [<tv-config-toolbar-icons>match[yes]] %>
|
||||
{{$:/core/images/new-journal-button}}
|
||||
@@ -15,11 +16,9 @@ description: {{$:/language/Buttons/NewJournalHere/Hint}}
|
||||
<$text text={{$:/language/Buttons/NewJournalHere/Caption}}/>
|
||||
</span>
|
||||
<%endif%>
|
||||
</$wikify>
|
||||
</$button>
|
||||
\end
|
||||
<$let journalTitle=<<now format={{$:/config/NewJournal/Title}}>>
|
||||
journalTags={{$:/config/NewJournal/Tags}}
|
||||
currentTiddlerTag=<<currentTiddler>>
|
||||
>
|
||||
<$let journalTitleTemplate={{$:/config/NewJournal/Title}} journalTags={{$:/config/NewJournal/Tags}} currentTiddlerTag=<<currentTiddler>>>
|
||||
<<journalButton>>
|
||||
</$let>
|
||||
|
||||
@@ -17,10 +17,6 @@ This page summarises high level information about the configuration of this ~Tid
|
||||
Drag this link to copy this tool to another wiki
|
||||
\end intrinsic-lingo-Drag/Caption
|
||||
|
||||
\procedure intrinsic-lingo-Generate/Caption()
|
||||
Click to generate wiki information report
|
||||
\end intrinsic-lingo-Generate/Caption
|
||||
|
||||
\procedure lingo(title,mode:"inline")
|
||||
<%if [<title>addprefix<lingo-base>is[shadow]] %>
|
||||
<$transclude $tiddler={{{ [<title>addprefix<lingo-base>] }}} $mode=<<mode>>/>
|
||||
@@ -109,7 +105,7 @@ Click to generate wiki information report
|
||||
|
||||
<$button>
|
||||
<<display-wiki-info-modal>>
|
||||
<<lingo title:"Generate/Caption">>
|
||||
Click to generate wiki information report
|
||||
</$button>
|
||||
|
||||
<$link to="$:/core/ui/ControlPanel/WikiInformation">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
title: $:/config/OfficialPluginLibrary
|
||||
tags: $:/tags/PluginLibrary
|
||||
url: https://tiddlywiki.com/library/v5.5.0/index.html
|
||||
url: https://tiddlywiki.com/library/v5.4.1/index.html
|
||||
caption: {{$:/language/OfficialPluginLibrary}}
|
||||
|
||||
{{$:/language/OfficialPluginLibrary/Hint}}
|
||||
|
||||
@@ -8,8 +8,9 @@ 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`
|
||||
# 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
|
||||
# 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 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
|
||||
@@ -22,6 +23,7 @@ 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
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"description": "Developer documentation from https://tiddlywiki.com/dev/",
|
||||
"plugins": [
|
||||
"tiddlywiki/highlight",
|
||||
"tiddlywiki/nodewebkitsaver",
|
||||
"tiddlywiki/github-fork-ribbon",
|
||||
"tiddlywiki/menubar",
|
||||
"tiddlywiki/internals",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"description": "A step by step introduction to TiddlyWiki",
|
||||
"plugins": [
|
||||
"tiddlywiki/cecily",
|
||||
"tiddlywiki/codemirror",
|
||||
"tiddlywiki/highlight",
|
||||
"tiddlywiki/katex"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
title: $:/config/LocalPluginLibrary
|
||||
tags: $:/tags/PluginLibrary
|
||||
url: http://127.0.0.1:8080/prerelease/library/v5.5.0/index.html
|
||||
url: http://127.0.0.1:8080/prerelease/library/v5.4.1/index.html
|
||||
caption: {{$:/language/OfficialPluginLibrary}} (Prerelease Local)
|
||||
|
||||
A locally installed version of the official ~TiddlyWiki plugin library at tiddlywiki.com for testing and debugging. //Requires a local web server to share the library//
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
title: $:/config/OfficialPluginLibrary
|
||||
tags: $:/tags/PluginLibrary
|
||||
url: https://tiddlywiki.com/prerelease/library/v5.5.0/index.html
|
||||
url: https://tiddlywiki.com/prerelease/library/v5.4.1/index.html
|
||||
caption: {{$:/language/OfficialPluginLibrary}} (Prerelease)
|
||||
|
||||
The prerelease version of the official ~TiddlyWiki plugin library at tiddlywiki.com. Plugins, themes and language packs are maintained by the core team.
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
created: 20260714055110474
|
||||
modified: 20260714055129225
|
||||
tags:
|
||||
title: TiddlyWiki Pre-release Size Comparison
|
||||
|
||||
|
||||
\function bytes.to.mib(size) [abs[]divide[1048576]fixed[2]addsuffix[ MiB]]
|
||||
\function bytes.to.kib(size) [abs[]divide[1024]fixed[2]addsuffix[ KiB]]
|
||||
\function format.file.size(size) [<size>abs[]compare:number:gteq[1048576]bytes.to.mib<size>] :else[<size>abs[]compare:number:gteq[1024]bytes.to.kib<size>] :else[<size>addsuffix[ bytes]]
|
||||
|
||||
|
||||
\procedure get-wiki-filesize(filepath)
|
||||
\procedure completion-get-json()
|
||||
<!-- Success -->
|
||||
<$list filter="[<status>compare:number:gteq[200]compare:number:lteq[299]]" variable="ignore">
|
||||
<$action-log msg="completed" size={{{ [<data>jsonget[size]] }}}/>
|
||||
<$action-setfield $tiddler=`$:/temp/file-size-comparison/$(filepath)$` text={{{ [<data>jsonget[size]] }}} />
|
||||
</$list>
|
||||
\end completion-get-json
|
||||
|
||||
<$action-sendmessage
|
||||
$message="tm-http-request"
|
||||
url=`https://api.github.com/repos/TiddlyWiki/tiddlywiki.com-gh-pages/contents/$(filepath)$?ref=master`
|
||||
method="GET"
|
||||
oncompletion=<<completion-get-json>>
|
||||
var-filepath=<<filepath>>
|
||||
/>
|
||||
\end get-wiki-filesize
|
||||
|
||||
\procedure get-wiki-filesizes()
|
||||
<$list filter="empty.html prerelease/empty.html" variable="filepath">
|
||||
<$action-log />
|
||||
<<$transclude $variable="get-wiki-filesize" filepath=<<filepath>> >>
|
||||
|
||||
</$list>
|
||||
\end get-wiki-filesizes
|
||||
|
||||
|
||||
<%if [[$:/temp/file-size-comparison/empty.html]is[tiddler]] [[$:/temp/file-size-comparison/prerelease/empty.html]is[tiddler]] :and[count[]match[2]]%>
|
||||
<$let delta={{{ [{$:/temp/file-size-comparison/empty.html}subtract{$:/temp/file-size-comparison/prerelease/empty.html}] }}}
|
||||
message={{{ [<delta>sign[]match[-1]then[Size increased]] :else[<delta>sign[]match[1]then[Size decreased]] :else[[Size has not changed]] }}}
|
||||
>
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="158" height="20" role="img" style.zoom="1.5" aria-label="<$text text=<<message>>/>: <$text text={{{ [format.file.size<delta>] }}} /> bytes">
|
||||
<title><$text text=<<message>>/>: <$text text={{{ [format.file.size<delta>] }}} /></title>
|
||||
<filter id="blur">
|
||||
<feGaussianBlur stdDeviation="16"/>
|
||||
</filter>
|
||||
<linearGradient id="s" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<clipPath id="r">
|
||||
<rect width="158" height="20" rx="3"/>
|
||||
</clipPath>
|
||||
<g clip-path="url(#r)">
|
||||
<rect width="93" height="20" fill="#555"/>
|
||||
<rect x="93" width="65" height="20" fill="#67ac09"/>
|
||||
<rect width="158" height="20" fill="url(#s)"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110">
|
||||
<g transform="scale(.1)">
|
||||
<g aria-hidden="true" fill="#010101">
|
||||
<text x="475" y="150" fill-opacity=".8" filter="url(#blur)" textLength="830"><$text text=<<message>>/></text>
|
||||
<text x="475" y="150" fill-opacity=".3" textLength="830"><$text text=<<message>>/></text>
|
||||
</g>
|
||||
<text x="475" y="140" textLength="830"><$text text=<<message>>/></text>
|
||||
</g>
|
||||
<g transform="scale(.1)">
|
||||
<g aria-hidden="true" fill="#010101">
|
||||
<text x="1245" y="150" fill-opacity=".8" filter="url(#blur)" textLength="550"><$text text={{{ [format.file.size<delta>] }}} /></text>
|
||||
<text x="1245" y="150" fill-opacity=".3" textLength="550"><$text text={{{ [format.file.size<delta>] }}} /></text>
|
||||
</g>
|
||||
<text x="1245" y="140" textLength="550"><$text text={{{ [format.file.size<delta>] }}} /></text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
; Pre-release empty.html
|
||||
: <$text text={{{ [{$:/temp/file-size-comparison/prerelease/empty.html}] }}} /> bytes
|
||||
; <$text text={{{ [tag[ReleaseNotes]sort[released]last[]] }}} /> empty.html
|
||||
: <$text text={{{ [{$:/temp/file-size-comparison/empty.html}] }}} /> bytes
|
||||
|
||||
---
|
||||
|
||||
</$let>
|
||||
<%else %>
|
||||
<$button actions=<<get-wiki-filesizes>> class="tc-btn-big-green">
|
||||
Compare size of empty.html
|
||||
</$button>
|
||||
<%endif%>
|
||||
|
||||
|
||||
|
||||
@@ -13,8 +13,6 @@ The pre-release is also available as an [[empty wiki|https://tiddlywiki.com/prer
|
||||
|
||||
</div>
|
||||
|
||||
{{ TiddlyWiki Pre-release Size Comparison }}
|
||||
|
||||
<$list filter="[tag[ReleaseNotes]!has[released]!sort[created]]">
|
||||
<div class="tc-titlebar">
|
||||
<h2 class="tc-title"><$text text=<<currentTiddler>>/></h2>
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
title: Filters/ListOps
|
||||
description: Test listops operators
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
(<$text text={{{ =[[E]] =[[A]] =[[B]] =[[C]] =[[C]] =[[D]] =[[C]] +[unique[]join[]] }}}/>)
|
||||
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>(EABCD)</p>
|
||||
@@ -1,162 +0,0 @@
|
||||
/*\
|
||||
title: test-back-indexer.js
|
||||
type: application/javascript
|
||||
tags: [[$:/tags/test-spec]]
|
||||
|
||||
Regression tests for #9916: the back-indexer must never record system
|
||||
tiddlers as backlink or backtransclude sources, neither when the index is
|
||||
first built nor when it is incrementally updated.
|
||||
|
||||
\*/
|
||||
"use strict";
|
||||
|
||||
describe("Back-indexer system source tests (#9916)", function() {
|
||||
function setupWiki() {
|
||||
// Create a wiki with indexers and one primed backlink pair
|
||||
var wiki = new $tw.Wiki();
|
||||
wiki.addIndexersToWiki();
|
||||
|
||||
wiki.addTiddler({
|
||||
title: "TestIncoming",
|
||||
text: ""});
|
||||
|
||||
wiki.addTiddler({
|
||||
title: "TestOutgoing",
|
||||
text: "A link to [[TestIncoming]]"});
|
||||
return wiki;
|
||||
}
|
||||
|
||||
it("should never report a system tiddler as a backlink source", function() {
|
||||
// Browser console: $tw.wiki.filterTiddlers("[[HelloThere]backlinks[]]") to prime the index,
|
||||
// then $tw.wiki.addTiddler(new $tw.Tiddler({title: "$:/temp/demo", text: "[[HelloThere]]"}))
|
||||
// and run the filter again; $:/temp/demo must not appear.
|
||||
var wiki = setupWiki();
|
||||
// The first lookup builds the lazy index; its initial scan skips system tiddlers
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing");
|
||||
// The incremental update() must skip them too
|
||||
wiki.addTiddler({
|
||||
title: "$:/temp/system-source",
|
||||
text: "A link to [[TestIncoming]]"});
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing");
|
||||
});
|
||||
|
||||
it("should keep backlinks stable while a linking system tiddler is modified and deleted", function() {
|
||||
// Browser console: $tw.wiki.filterTiddlers("[[HelloThere]backlinks[]]") to prime the index,
|
||||
// then $tw.wiki.addTiddler(new $tw.Tiddler({title: "$:/temp/demo", text: "[[HelloThere]]"})),
|
||||
// change its text, $tw.wiki.deleteTiddler("$:/temp/demo"); the filter result never changes.
|
||||
var wiki = setupWiki();
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing");
|
||||
wiki.addTiddler({
|
||||
title: "$:/temp/system-source",
|
||||
text: "A link to [[TestIncoming]]"});
|
||||
// Modify: both the old and the new side of the index update are system tiddlers
|
||||
wiki.addTiddler({
|
||||
title: "$:/temp/system-source",
|
||||
text: "Links to [[TestIncoming]] and [[TestOutgoing]]"});
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing");
|
||||
wiki.deleteTiddler("$:/temp/system-source");
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing");
|
||||
});
|
||||
|
||||
it("should drop the backlink when its source is renamed to a system title", function() {
|
||||
// Browser console: $tw.wiki.addTiddler(new $tw.Tiddler({title: "Demo", text: "[[HelloThere]]"})),
|
||||
// confirm Demo is in $tw.wiki.filterTiddlers("[[HelloThere]backlinks[]]"), then
|
||||
// $tw.wiki.renameTiddler("Demo","$:/Demo"); neither Demo nor $:/Demo remains a source.
|
||||
var wiki = setupWiki();
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing");
|
||||
wiki.renameTiddler("TestOutgoing","$:/TestOutgoing");
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("");
|
||||
});
|
||||
|
||||
it("should gain the backlink when a linking system tiddler is renamed to a normal title", function() {
|
||||
// Browser console: $tw.wiki.addTiddler(new $tw.Tiddler({title: "$:/temp/demo", text: "[[HelloThere]]"})),
|
||||
// then $tw.wiki.renameTiddler("$:/temp/demo","DemoVisible");
|
||||
// DemoVisible now appears in $tw.wiki.filterTiddlers("[[HelloThere]backlinks[]]").
|
||||
var wiki = setupWiki();
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing");
|
||||
wiki.addTiddler({
|
||||
title: "$:/temp/system-source",
|
||||
text: "A link to [[TestIncoming]]"});
|
||||
wiki.renameTiddler("$:/temp/system-source","VisibleSource");
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing,VisibleSource");
|
||||
});
|
||||
|
||||
it("should still report normal sources for a system tiddler target", function() {
|
||||
// Only sources are filtered, targets are not. Browser console:
|
||||
// $tw.wiki.addTiddler(new $tw.Tiddler({title: "Demo", text: "[[$:/config/NewJournal/Tags]]"}));
|
||||
// $tw.wiki.filterTiddlers("[[$:/config/NewJournal/Tags]backlinks[]]") contains Demo.
|
||||
var wiki = setupWiki();
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing");
|
||||
wiki.addTiddler({
|
||||
title: "TestSystemLinker",
|
||||
text: "A link to [[$:/config/Target]]"});
|
||||
expect(wiki.filterTiddlers("[[$:/config/Target]backlinks[]]").join(",")).toBe("TestSystemLinker");
|
||||
});
|
||||
|
||||
describe("Adversarial probes", function() {
|
||||
it("should never index a shadow tiddler as a source, even when revealed by deleting its override", function() {
|
||||
// Goes red when a refactor of BackSubIndexer.update() checks the tiddler instead of
|
||||
// the exists flag:
|
||||
// if(updateDescriptor["new"].tiddler) { ... } // broken: goes red here
|
||||
// if(updateDescriptor["new"].exists) { ... } // correct: stays green
|
||||
// After deleteTiddler() on an override, boot.js fills new.tiddler via getTiddler(),
|
||||
// which falls back to the revealed shadow, while new.exists stays false because
|
||||
// only the real store counts. The broken variant indexes the shadow's links and
|
||||
// the final expect fails with "TestOutgoing,ShadowSource".
|
||||
// The first expect also goes red if _init() ever starts scanning shadows; the
|
||||
// middle expect pins that a real override IS indexed, so this probe cannot be
|
||||
// satisfied by indexing nothing at all.
|
||||
// Browser console: override a plugin shadow with text [[HelloThere]], check it appears in
|
||||
// $tw.wiki.filterTiddlers("[[HelloThere]backlinks[]]"), then $tw.wiki.deleteTiddler(title);
|
||||
// the title disappears from the filter result even though the shadow still renders.
|
||||
var wiki = setupWiki();
|
||||
wiki.addTiddler({
|
||||
title: "$:/plugins/test/shadow-plugin",
|
||||
type: "application/json",
|
||||
"plugin-type": "plugin",
|
||||
text: JSON.stringify({tiddlers: {
|
||||
"ShadowSource": {title: "ShadowSource", text: "A shadow link to [[TestIncoming]]"}
|
||||
}})});
|
||||
wiki.readPluginInfo();
|
||||
wiki.registerPluginTiddlers("plugin");
|
||||
wiki.unpackPluginTiddlers();
|
||||
// The initial scan sees only real tiddlers, not the shadow
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing");
|
||||
wiki.addTiddler({
|
||||
title: "ShadowSource",
|
||||
text: "An overriding link to [[TestIncoming]]"});
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing,ShadowSource");
|
||||
// Deleting the override reveals the shadow; it must not enter the index
|
||||
wiki.deleteTiddler("ShadowSource");
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing");
|
||||
});
|
||||
|
||||
it("should handle hostile titles like __proto__ as source and target", function() {
|
||||
// Goes red when any title-keyed hashmap on the backlinks path is created like this:
|
||||
// this.index = {}; // broken: goes red here
|
||||
// instead of:
|
||||
// this.index = Object.create(null); // correct: stays green
|
||||
// this.index = new Map(); // a Map/Set refactor also stays green
|
||||
// The same applies to the boot tiddler store and to the per-target source maps
|
||||
// (self.index[target] = ...). On a plain {} the assignment index["__proto__"] = x
|
||||
// stores no key; it silently replaces the object's prototype. Depending on which
|
||||
// map regresses, the __proto__ tiddler never registers as a source (second expect),
|
||||
// or its target entry lands in the shared prototype, where it pollutes every other
|
||||
// lookup and lookup("__proto__") returns garbage (third expect).
|
||||
// Browser console: $tw.wiki.addTiddler(new $tw.Tiddler({title: "__proto__",
|
||||
// text: "[[HelloThere]]"})); __proto__ appears in
|
||||
// $tw.wiki.filterTiddlers("[[HelloThere]backlinks[]]") and
|
||||
// $tw.wiki.filterTiddlers("[[__proto__]backlinks[]]") lists tiddlers linking to it.
|
||||
var wiki = setupWiki();
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing");
|
||||
wiki.addTiddler({
|
||||
title: "__proto__",
|
||||
text: "A link to [[TestIncoming]]"});
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing,__proto__");
|
||||
wiki.addTiddler({
|
||||
title: "ProtoLinker",
|
||||
text: "A link to [[__proto__]]"});
|
||||
expect(wiki.filterTiddlers("[[__proto__]backlinks[]]").join(",")).toBe("ProtoLinker");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -19,56 +19,6 @@ describe("fakedom tests", function() {
|
||||
expect($tw.fakeDocument.createTextNode("text").TEXT_NODE).toBe(3);
|
||||
});
|
||||
|
||||
// Per DOM spec, tagName returns the HTML-uppercased qualified name for HTML
|
||||
// elements. Other namespaces preserve case.
|
||||
// https://dom.spec.whatwg.org/#dom-element-tagname
|
||||
var HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
var SVG_NS = "http://www.w3.org/2000/svg";
|
||||
|
||||
it("tagName uppercases for HTML namespace", function() {
|
||||
// Default namespace is HTML
|
||||
expect($tw.fakeDocument.createElement("div").tagName).toBe("DIV");
|
||||
// The exact predicate the select widget relies on (#9839)
|
||||
expect($tw.fakeDocument.createElement("optgroup").tagName === "OPTGROUP").toBe(true);
|
||||
// Already-uppercase input stays uppercase
|
||||
expect($tw.fakeDocument.createElement("OPTGROUP").tagName).toBe("OPTGROUP");
|
||||
// Mixed-case input is normalised
|
||||
expect($tw.fakeDocument.createElement("Div").tagName).toBe("DIV");
|
||||
// Hyphenated custom-element names uppercase whole tag, hyphens survive
|
||||
expect($tw.fakeDocument.createElement("my-button").tagName).toBe("MY-BUTTON");
|
||||
// Empty tag returns empty string
|
||||
expect($tw.fakeDocument.createElement("").tagName).toBe("");
|
||||
// Explicit HTML namespace via createElementNS uppercases the same way
|
||||
expect($tw.fakeDocument.createElementNS(HTML_NS,"Div").tagName).toBe("DIV");
|
||||
});
|
||||
|
||||
it("tagName preserves case for non-HTML namespaces", function() {
|
||||
// SVG: lowercase preserved
|
||||
expect($tw.fakeDocument.createElementNS(SVG_NS,"circle").tagName).toBe("circle");
|
||||
// SVG: camelCase preserved (linearGradient is the canonical example)
|
||||
expect($tw.fakeDocument.createElementNS(SVG_NS,"linearGradient").tagName).toBe("linearGradient");
|
||||
// SVG: already-uppercase input is also preserved (NOT lowercased)
|
||||
expect($tw.fakeDocument.createElementNS(SVG_NS,"DIV").tagName).toBe("DIV");
|
||||
// Empty namespace string is "no namespace", not HTML. Case preserved.
|
||||
expect($tw.fakeDocument.createElementNS("","div").tagName).toBe("div");
|
||||
});
|
||||
|
||||
it("tagName reflects current state without mutating it", function() {
|
||||
// Reading tagName must not overwrite the internal `tag` field
|
||||
var el = $tw.fakeDocument.createElement("div");
|
||||
expect(el.tagName).toBe("DIV");
|
||||
expect(el.tag).toBe("div");
|
||||
// Idempotent: two reads return identical values
|
||||
var first = el.tagName, second = el.tagName;
|
||||
expect(first).toBe(second);
|
||||
// Dynamic namespace change is reflected. The getter must read current
|
||||
// state, not a value cached at construction time.
|
||||
var dynamic = $tw.fakeDocument.createElement("foo");
|
||||
expect(dynamic.tagName).toBe("FOO");
|
||||
dynamic.namespaceURI = SVG_NS;
|
||||
expect(dynamic.tagName).toBe("foo");
|
||||
});
|
||||
|
||||
// Real CSSStyleDeclaration returns undefined for Symbol property keys.
|
||||
// Without a guard, the TW_Style Proxy throws on Symbol access. This bites
|
||||
// in practice when Jasmine pretty-prints fakedom elements on failure.
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
/*\
|
||||
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: [] } ]
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,240 +0,0 @@
|
||||
/*\
|
||||
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,8 +1,7 @@
|
||||
created: 20231005205623086
|
||||
modified: 20260710090951727
|
||||
modified: 20250807100434131
|
||||
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
|
||||
@@ -10,7 +9,7 @@ type: text/vnd.tiddlywiki
|
||||
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.1
|
||||
5.4.0
|
||||
\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 Image" alt="Shaping the future of TiddlyWiki with the Community Survey 2025" width="280"/>
|
||||
<$image source="Community Survey 2025" 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,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: 20260710091122803
|
||||
modified: 20260420192600833
|
||||
tags: Welcome
|
||||
title: HelloThere
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 54 KiB |
@@ -1,8 +0,0 @@
|
||||
title: $:/changenotes/5.4.0/#9715
|
||||
change-type: performance
|
||||
change-category: filters
|
||||
tags: $:/tags/ChangeNote
|
||||
github-contributors: linonetwo
|
||||
release: 5.4.0
|
||||
description: Optimized tag[] and !tag[] filter operators to use Set for O(1) lookup, matching search:tags[] performance.
|
||||
github-links: [[https://github.com/Jermolene/TiddlyWiki5/pull/9715]]
|
||||
@@ -1,13 +0,0 @@
|
||||
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.
|
||||
@@ -1,9 +0,0 @@
|
||||
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,14 +1,11 @@
|
||||
change-category: plugin
|
||||
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
|
||||
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
|
||||
github-contributors: s793016
|
||||
|
||||
Fixes correctness and reliability issues in the freelinks plugin.
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
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.
|
||||
@@ -1,9 +0,0 @@
|
||||
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
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
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
@@ -1,21 +0,0 @@
|
||||
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.
|
||||
@@ -1,13 +0,0 @@
|
||||
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,19 +1,13 @@
|
||||
caption: 5.4.1
|
||||
created: 20260508181012812
|
||||
description: Release v5.4.1 addresses regressions and bugs introduced in the v5.4.0 release.
|
||||
modified: 20260710091945414
|
||||
released: 20260710090509444
|
||||
modified: 20260508181012812
|
||||
tags: ReleaseNotes
|
||||
title: Release 5.4.1
|
||||
type: text/vnd.tiddlywiki
|
||||
description: Under development
|
||||
|
||||
\procedure release-introduction()
|
||||
Release v5.4.1 addresses regressions and bugs introduced in the v5.4.0 release.
|
||||
Release v5.4.1 is under development.
|
||||
\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>>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
change-category: widget
|
||||
change-type: enhancement
|
||||
created: 20260711020921000
|
||||
description: The diff-text widget shows removed or added newlines with a visible ↲ glyph
|
||||
github-contributors: pmario
|
||||
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9736
|
||||
release: 5.5.0
|
||||
tags: $:/tags/ChangeNote
|
||||
title: $:/changenotes/5.5.0/#9736
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
* The diff-text widget marks newline characters in a diff with the visible `↲` glyph (U+21B2) instead of an invisible character, so line break changes are recognisable in the rendered diff (fixes [[Issue #9461|https://github.com/TiddlyWiki/TiddlyWiki5/issues/9461]])
|
||||
@@ -1,12 +0,0 @@
|
||||
change-category: translation
|
||||
change-type: enhancement
|
||||
created: 20260711020729000
|
||||
description: The "Click to generate wiki info" button in the control panel is now translatable
|
||||
github-contributors: pmario
|
||||
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9737
|
||||
release: 5.5.0
|
||||
tags: $:/tags/ChangeNote
|
||||
title: $:/changenotes/5.5.0/#9737
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
* The "Click to generate wiki info" button on the control panel's wiki information tab uses a language string instead of hard coded English text, so translators can localise it (fixes [[Issue #9654|https://github.com/TiddlyWiki/TiddlyWiki5/issues/9654]])
|
||||
@@ -1,12 +0,0 @@
|
||||
change-category: nodejs
|
||||
change-type: bugfix
|
||||
created: 20260711020057000
|
||||
description: The Node.js server no longer emits the DEP0169 url.parse() deprecation warning
|
||||
github-contributors: pmario
|
||||
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9742
|
||||
release: 5.5.0
|
||||
tags: $:/tags/ChangeNote
|
||||
title: $:/changenotes/5.5.0/#9742
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
* The server module parses request URLs with the WHATWG `URL` API instead of the deprecated `url.parse()`, so recent Node.js versions no longer print the DEP0169 deprecation warning on startup (fixes [[Issue #9628|https://github.com/TiddlyWiki/TiddlyWiki5/issues/9628]])
|
||||
@@ -1,10 +0,0 @@
|
||||
title: $:/changenotes/5.5.0/#9816
|
||||
description: Replaces some wikify widget with call dynamic syntax
|
||||
release: 5.5.0
|
||||
tags: $:/tags/ChangeNote
|
||||
change-type: enhancement
|
||||
change-category: internal
|
||||
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9816
|
||||
github-contributors: Leilei332
|
||||
|
||||
Replaces unnecessary wikify widget usage with call dynamic attribute syntax.
|
||||
@@ -1,13 +0,0 @@
|
||||
change-category: internal
|
||||
change-type: bugfix
|
||||
created: 20260711014241000
|
||||
description: fakedom tagName is uppercase for HTML elements, matching the DOM spec
|
||||
github-contributors: pmario
|
||||
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9843
|
||||
release: 5.5.0
|
||||
tags: $:/tags/ChangeNote
|
||||
title: $:/changenotes/5.5.0/#9843
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
* `$tw.fakeDocument` elements now report `tagName` in uppercase for HTML elements, matching the DOM specification and real browsers; code that compares fakedom `tagName` against lowercase strings must be updated
|
||||
* Creating an element with an empty or null namespace now produces a plain element without a namespace
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
change-category: filters
|
||||
change-type: bugfix
|
||||
created: 20260711011920000
|
||||
description: backlinks[] and backtranscludes[] no longer report edited system tiddlers as sources
|
||||
github-contributors: pmario
|
||||
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/issues/9917
|
||||
release: 5.5.0
|
||||
tags: $:/tags/ChangeNote
|
||||
title: $:/changenotes/5.5.0/#9917
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
* Editing a system tiddler that links or transcludes another tiddler no longer adds it as a [[backlinks|backlinks Operator]] or [[backtranscludes|backtranscludes Operator]] source; the incremental back-index update now skips system tiddlers like the initial scan always did
|
||||
@@ -1,13 +0,0 @@
|
||||
change-category: filters
|
||||
change-type: enhancement
|
||||
created: 20260726124618748
|
||||
description: Optimizes listops filter operators
|
||||
github-contributors: saqimtiaz
|
||||
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/issues/9942
|
||||
modified: 20260726125232992
|
||||
release: 5.5.0
|
||||
tags: $:/tags/ChangeNote
|
||||
title: $:/changenotes/5.5.0/#9942
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
* The listops filter operators have been rewritten using more modern JavaScript (ES2017). The `unique` and `remove` operators have improved performance in certain circumstances.
|
||||
@@ -1,13 +0,0 @@
|
||||
caption: 5.5.0
|
||||
created: 20260710103826404
|
||||
modified: 20260710103826404
|
||||
tags: ReleaseNotes
|
||||
title: Release 5.5.0
|
||||
type: text/vnd.tiddlywiki
|
||||
description: Under development
|
||||
|
||||
\procedure release-introduction()
|
||||
Release v5.5.0 is under development.
|
||||
\end release-introduction
|
||||
|
||||
<<releasenote 5.5.0>>
|
||||
@@ -10,6 +10,7 @@ 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-2025, UnaMesa Association
|
||||
Copyright (c) 2007-2026, UnaMesa Association
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
||||
Generated
+6
-16
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "tiddlywiki",
|
||||
"version": "5.5.0-prerelease",
|
||||
"version": "5.4.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "tiddlywiki",
|
||||
"version": "5.4.1",
|
||||
"version": "5.4.0",
|
||||
"license": "BSD",
|
||||
"bin": {
|
||||
"tiddlywiki": "tiddlywiki.js"
|
||||
@@ -19,7 +19,7 @@
|
||||
"globals": "16.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
"node": ">=0.8.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
@@ -717,20 +717,10 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
|
||||
"integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/puzrin"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/nodeca"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^2.0.1"
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "tiddlywiki",
|
||||
"preferGlobal": true,
|
||||
"version": "5.5.0-prerelease",
|
||||
"version": "5.4.1-prerelease",
|
||||
"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.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">
|
||||
</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">
|
||||
<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