Background: #fff Foreground: #000 PrimaryPale: #8cf PrimaryLight: #18f PrimaryMid: #04b PrimaryDark: #014 SecondaryPale: #ffc SecondaryLight: #fe8 SecondaryMid: #db4 SecondaryDark: #841 TertiaryPale: #eee TertiaryLight: #ccc TertiaryMid: #999 TertiaryDark: #666 Error: #f88
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='edit text'></div>
<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser excludeLists'></span></div>
<!--}}}-->
When getting started, you may want to: * Set your username for signing your edits: <<option txtUserName>> * Change the page [[title|SiteTitle]] (now "<<tiddler SiteTitle>>") and [[subtitle|SiteSubtitle]] (now "<<tiddler SiteSubtitle>>"); they also set the browser tab title * Create a tiddler where your content "starts" ** Use the button on the sidebar or [[link|My first tiddler]] it here, follow the link, edit, and click "done" ** It will be shown in the Timeline (usually on the right), but you may want to link it in the MainMenu (usually on the left) ** and/or make it open when the ~TiddlyWiki is opened by editing the list of [[DefaultTiddlers]] (separate links with spaces or linebreaks) * Save your ~TiddlyWiki ** Although "download saving" works in any browser, it's not that convenient, so you'll probably want to use [[a dedicated saver|https://classic.tiddlywiki.com/#%5B%5BSetting up saving%5D%5D]]
<<importTiddlers>>
<!--{{{-->
<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml' />
<!--}}}-->
These [[InterfaceOptions]] for customising [[TiddlyWiki]] are saved in your browser Your username for signing your edits. Write it as a [[WikiWord]] (eg [[JoeBloggs]]) <<option txtUserName>> <<option chkSaveBackups>> [[SaveBackups]] <<option chkAutoSave>> [[AutoSave]] <<option chkRegExpSearch>> [[RegExpSearch]] <<option chkCaseSensitiveSearch>> [[CaseSensitiveSearch]] <<option chkAnimate>> [[EnableAnimations]] ---- Also see [[AdvancedOptions]]
<!--{{{-->
<div class='header' role='banner'>
<div class='headerShadow'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
</div>
<div id='mainMenu' role='navigation' refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
<div id='sidebarOptions' role='navigation' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' role='complementary' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<div id='displayArea' role='main'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
/*{{{*/
body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
a {color:[[ColorPalette::PrimaryMid]];}
a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
a img {border:0;}
h1, h2, h3, h4, h5, h6 { color: [[ColorPalette::SecondaryDark]]; }
h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}
.txtOptionInput {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}
.header {
background: -moz-linear-gradient(to bottom, [[ColorPalette::PrimaryLight]], [[ColorPalette::PrimaryMid]]);
background: linear-gradient(to bottom, [[ColorPalette::PrimaryLight]], [[ColorPalette::PrimaryMid]]);
}
.header a:hover {background:transparent;}
.headerShadow {color:[[ColorPalette::Foreground]];}
.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
.headerForeground {color:[[ColorPalette::Background]];}
.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}
.tabSelected {
color:[[ColorPalette::Foreground]];
background:[[ColorPalette::Background]];
border-left:1px solid [[ColorPalette::TertiaryLight]];
border-top:1px solid [[ColorPalette::TertiaryLight]];
border-right:1px solid [[ColorPalette::TertiaryLight]];
}
.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
.tabContents {border:1px solid [[ColorPalette::TertiaryLight]];}
.tabContents .button {border:0;}
#sidebar {}
#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}
.wizard { background:[[ColorPalette::PrimaryPale]]; }
.wizard__title { color:[[ColorPalette::PrimaryDark]]; border:none; }
.wizard__subtitle { color:[[ColorPalette::Foreground]]; border:none; }
.wizardStep { background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]]; }
.wizardStep.wizardStepDone {background:[[ColorPalette::TertiaryLight]];}
.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
.wizardFooter .status a { color: [[ColorPalette::PrimaryPale]]; }
.wizard .button {
color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
border-color:[[ColorPalette::SecondaryDark]];
}
.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
.wizard .button:active {
color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];
}
.wizard .notChanged {background:transparent;}
.wizard .changedLocally {background:#80ff80;}
.wizard .changedServer {background:#8080ff;}
.wizard .changedBoth {background:#ff8080;}
.wizard .notFound {background:#ffff80;}
.wizard .putToServer {background:#ff80ff;}
.wizard .gotFromServer {background:#80ffff;}
#messageArea { background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; box-shadow: 1px 2px 5px [[ColorPalette::TertiaryMid]]; }
.messageToolbar__button { color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none; }
.messageToolbar__button_withIcon { background:inherit; }
.messageToolbar__button_withIcon:active { background:inherit; border:none; }
.tw-icon line { stroke: [[ColorPalette::TertiaryDark]]; }
.messageToolbar__button:hover .tw-icon line { stroke: [[ColorPalette::Foreground]]; }
.popup {
background: [[ColorPalette::Background]];
color: [[ColorPalette::TertiaryDark]];
box-shadow: 1px 2px 5px [[ColorPalette::TertiaryMid]];
}
.popup li a, .popup li a:visited, .popup li a:hover, .popup li a:active {
color:[[ColorPalette::Foreground]]; border: none;
}
.popup li a:hover { background:[[ColorPalette::SecondaryLight]]; }
.popup li a:active { background:[[ColorPalette::SecondaryPale]]; }
.popup li.disabled { color:[[ColorPalette::TertiaryMid]]; }
.popupHighlight {color:[[ColorPalette::Foreground]];}
.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}
.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}
.tiddler .defaultCommand {font-weight:bold;}
.shadow .title {color:[[ColorPalette::TertiaryDark]];}
.title {color:[[ColorPalette::SecondaryDark]];}
.subtitle {color:[[ColorPalette::TertiaryDark]];}
.toolbar {color:[[ColorPalette::PrimaryMid]];}
.toolbar a {color:[[ColorPalette::TertiaryLight]];}
.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}
.tagging, .tagged { border: 2px solid [[ColorPalette::TertiaryPale]]; }
.selected .tagging, .selected .tagged { border: 2px solid [[ColorPalette::TertiaryLight]]; }
.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
.tagging .button, .tagged .button { border:none; }
.footer {color:[[ColorPalette::TertiaryLight]];}
.selected .footer {color:[[ColorPalette::TertiaryMid]];}
.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
.lowlight {background:[[ColorPalette::TertiaryLight]];}
.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}
.imageLink, #displayArea .imageLink {background:transparent;}
.annotation { background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
.viewer .listTitle {list-style-type:none; margin-left:-2em;}
.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}
.viewer th, .viewer thead td, .twtable th, .twtable thead td { background: [[ColorPalette::SecondaryMid]]; color: [[ColorPalette::Background]]; }
.viewer td, .viewer tr, .twtable td, .twtable tr { border: 1px solid [[ColorPalette::TertiaryLight]]; }
.twtable caption { color: [[ColorPalette::TertiaryMid]]; }
.viewer pre {background:[[ColorPalette::SecondaryPale]];}
.viewer code {color:[[ColorPalette::SecondaryDark]];}
.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}
.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}
.editor input {border:1px solid [[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%; background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
.editorFooter {color:[[ColorPalette::TertiaryMid]];}
.readOnly {background:[[ColorPalette::TertiaryPale]];}
#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:alpha(opacity=60);}
/*}}}*/
/*{{{*/
body { font-size:.75em; font-family:arial,helvetica,sans-serif; margin:0; padding:0; }
* html .tiddler {height:1%;}
h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
h4,h5,h6 {margin-top:1em;}
h1 {font-size:1.35em;}
h2 {font-size:1.25em;}
h3 {font-size:1.1em;}
h4 {font-size:1em;}
h5 {font-size:.9em;}
hr {height:1px;}
dt {font-weight:bold;}
ol {list-style-type:decimal;}
ol ol {list-style-type:lower-alpha;}
ol ol ol {list-style-type:lower-roman;}
ol ol ol ol {list-style-type:decimal;}
ol ol ol ol ol {list-style-type:lower-alpha;}
ol ol ol ol ol ol {list-style-type:lower-roman;}
ol ol ol ol ol ol ol {list-style-type:decimal;}
.txtOptionInput {width:11em; border-width: 1px; }
#contentWrapper .chkOptionInput {border:0;}
.indent {margin-left:3em;}
.outdent {margin-left:3em; text-indent:-3em;}
code.escaped {white-space:nowrap;}
a {text-decoration:none;}
.externalLink {text-decoration:underline;}
.tiddlyLinkExisting {font-weight:bold;}
.tiddlyLinkNonExisting {font-style:italic;}
/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
a.tiddlyLinkNonExisting.shadow {font-weight:bold;}
#mainMenu .tiddlyLinkExisting,
#mainMenu .tiddlyLinkNonExisting,
#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}
.header {position:relative;}
.headerShadow {position:relative; padding:3em 0 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:3em 0 1em 1em; left:0; top:0;}
.siteTitle {font-size:3em;}
.siteSubtitle {font-size:1.2em;}
#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}
#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
#sidebarOptions {padding-top:0.3em;}
#sidebarOptions a {margin:0 0.2em; padding:0.2em 0.3em; display:block;}
#sidebarOptions input {margin:0.4em 0.5em;}
#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
#sidebarOptions .sliderPanel input {margin:0 0 0.3em 0;}
#sidebarTabs .tabContents {width:15em; overflow:hidden;}
.wizard { padding:0.1em 2em 0; }
.wizard__title { font-size:2em; }
.wizard__subtitle { font-size:1.2em; }
.wizard__title, .wizard__subtitle { font-weight:bold; background:none; padding:0; margin:0.4em 0 0.2em; }
.wizardStep { padding:1em; }
.wizardFooter { padding: 0.8em 0; }
.wizardFooter .status { display: inline-block; line-height: 1.5; padding: 0.3em 1em; }
.wizardFooter .button { margin:0.5em 0 0; font-size:1.2em; padding:0.2em 0.5em; }
#messageArea { position:fixed; top:2em; right:0; margin:0.5em; padding:0.7em 1em; z-index:2000; }
.messageToolbar { text-align:right; padding:0.2em 0; }
.messageToolbar__button { text-decoration:underline; }
.messageToolbar__button_withIcon { display: inline-block; }
.tw-icon { height: 1em; width: 1em; } /* width for IE */
.tw-icon line { stroke-width: 1; stroke-linecap: round; }
.messageArea__text a { text-decoration:underline; }
.popup {position:absolute; z-index:300; font-size:.9em; padding:0.3em 0; list-style:none; margin:0;}
.popup .popupMessage, .popup li.disabled, .popup li a { padding: 0.3em 0.7em; }
.popup li a {display:block; font-weight:normal; cursor:pointer;}
.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0;}
.listBreak {font-size:1px; line-height:1px;}
.listBreak div {margin:2px 0;}
.tiddlerPopupButton {padding:0.2em;}
.popupTiddler {position: absolute; z-index:300; padding:1em; margin:0;}
.tabset {padding:1em 0 0 0.5em;}
.tab {display: inline-block; white-space: nowrap; position: relative; bottom: -0.7px; margin: 0 0.25em 0 0; padding:0.2em;}
.tabContents {padding:0.5em;}
.tabContents ul, .tabContents ol {margin:0; padding:0;}
.txtMainTab .tabContents li {list-style:none;}
.tabContents li.listLink { margin-left:.75em;}
#contentWrapper {display:block;}
#splashScreen {display:none;}
#displayArea {margin:1em 17em 0 14em;}
.toolbar {text-align:right; font-size:.9em;}
.tiddler { padding: 1em; }
.title { font-size: 1.6em; font-weight: bold; }
.subtitle { font-size: 1.1em; }
.missing .viewer, .missing .title { font-style: italic; }
.missing .subtitle { display: none; }
.tiddler .button {padding:0.2em 0.4em;}
.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
.isTag .tagging {display:block;}
.tagged {margin:0.5em; float:right;}
.tagging, .tagged {font-size:0.9em; padding:0.25em;}
.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
.tagged li, .tagging li { margin: 0.3em 0; }
.tagClear {clear:both;}
.footer {font-size:.9em;}
.footer li {display:inline;}
.annotation { padding: 0.5em 0.8em; margin: 0.5em 1px; }
.viewer {line-height:1.4em; padding-top:0.5em;}
.viewer .button {margin:0 0.25em; padding:0 0.25em;}
.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}
.viewer table, table.twtable { border-collapse: collapse; margin: 0.8em 0; }
.viewer th, .viewer td, .viewer tr, .viewer caption, .twtable th, .twtable td, .twtable tr, .twtable caption { padding: 0.2em 0.4em; }
.twtable caption { font-size: 0.9em; }
table.listView { margin: 0.8em 1.0em; }
table.listView th, table.listView td, table.listView tr { text-align: left; }
.listView > thead { position: sticky; top: 0; }
* html .viewer pre {width:99%; padding:0 0 1em 0;}
.viewer pre {padding:0.5em; overflow:auto;}
pre, code { font-family: monospace, monospace; font-size: 1em; }
.viewer pre, .viewer code { line-height: 1.4em; }
.editor {font-size:1.1em; line-height:1.4em;}
.editor input, .editor textarea {display:block; width:100%; box-sizing: border-box; font:inherit;}
.editorFooter {padding:0.25em 0; font-size:.9em;}
.editorFooter .button {padding-top:0; padding-bottom:0;}
.fieldsetFix {border:0; padding:0; margin:1px 0;}
.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
.zoomer div {padding:1em;}
* html #backstage {width:99%;}
* html #backstageArea {width:99%;}
#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em;}
#backstageToolbar {position:relative;}
#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em;}
#backstageButton {display:none; position:absolute; z-index:175; top:0; right:0;}
#backstageButton a {padding: 0.3em 0.5em; display: inline-block;}
#backstage {position:relative; width:100%; z-index:50;}
#backstagePanel { display:none; z-index:100; position:absolute; width:90%; margin:0 5%; }
.backstagePanelFooter {padding-top:0.2em; float:right;}
.backstagePanelFooter a {padding:0.2em 0.4em;}
#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}
.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
/*}}}*/
/***
StyleSheet for use when a translation requires any css style changes.
This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which need larger font sizes.
***/
/*{{{*/
body {font-size:0.8em;}
#sidebarOptions {font-size:1.05em;}
#sidebarOptions a {font-style:normal;}
#sidebarOptions .sliderPanel {font-size:0.95em;}
.subtitle {font-size:0.8em;}
.viewer table.listView {font-size:0.95em;}
/*}}}*/
/*{{{*/
@media print {
#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton, #backstageArea { display: none !important; }
#displayArea { margin: 1em 1em 0em; }
}
/*}}}*/
<!--{{{-->
<div class='toolbar' role='navigation' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='tagging' macro='tagging'></div>
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<!--}}}-->
/***
|Description|Allows to store any number of tiddlers as external files and more|
|Version|1.1.2|
|Source|https://github.com/YakovL/TiddlyWiki_TiddlerInFilePlugin|
|Author|Yakov Litvin|
|License|MIT|
!!!Usage
Once the plugin is installed (copy - tag {{{systemConfig}}} - reload) storing tiddlers in files is done via 2 steps:
# list (describe) those in [[ExternalTiddlersList]] by writing {{{<<external>>}}} macros there
# if the file exists and the tiddler doesn't, reload TW (external tiddler will be loaded on startup);<br>if the tiddler exists and the file doesn't, just save your TW
Here's how the macro is used:
{{{
<<external [[MyTestTiddler]]>>
}}}
will store the tiddler's text in {{{MyTestTiddler.txt}}} in the same folder as TW. There's a number of other options:
{{{
<<external [[tiddler name]]
[file:<relative path with or without filename and extension>]
[format:{externalized | text}]
[keepInternal:true]
[plugin:true]
>>
}}}
Examples of the {{{file}}} param usage:
* {{{<<external [[MyTestTiddler]] file:"other name">>}}} makes file name different from tiddler name
* {{{<<external [[MyPlugin]] file:"MyPlugin.js" plugin:true>>}}} sets a custom file extension (see also the {{{plugin}}} option below)
* {{{<<external [[MyLog]] file:"../logs/">>}}} will store the file in another folder (note that omitted filename after {{{/}}} means "use tiddler name as filename and default extension")
* the plugin doesn't take care of forbidden characters yet ({{{*}}}, {{{?}}}, {{{"}}} etc), so be careful about those
Supported formats are
* {{{text}}} (default) – only tiddler text is stored in the file with {{{.txt}}} extension; and
* {{{externalized}}} – whole tiddler is stored in the same format as in TW store, file has {{{.tid.html}}} extension and is displayed is monospace tiddler text when opened in browser
Formats can be added by extending {{{config.macros.external.fileFormats}}}.
The {{{keepInternal}}} option makes TW save the tiddler in both external file and TW itself. This, for instance, affects tiddlers stored in {{{text}}} format: without it, all fields like creator, modified etc are destroyed on reload (since are not saved), but with it they are preserved.
The {{{plugin}}} option makes TW evaluate the tiddler like it does with plugins. Note that it doesn't handle plugin dependencies yet. @@color:red;Warning@@: TIFP doesn't currently take care of installing only once, so use {{{plugin}}} with {{{keepInternal}}} ''only'' if you understand the outcome (for instance, some plugins may create infinite loops; also remember that internal version will be always installed first).
***/
//{{{
config.macros.external = {
fileFormats: {
text: {
extension: 'txt',
externalize: function(tiddler) { return tiddler.text },
// changes tiddler as a side-effect not to remove existing fields
internalize: function(tiddler, source) {
tiddler.text = source;
}
},
externalized: {
extension: 'tid.html',
externalize: function(tiddler) {
return store.getSaver().externalizeTiddler(store, tiddler);
},
// like for 'text', extends tiddler, doesn't create from scratch
internalize: function(tiddler, source) {
var div = createTiddlyElement(document.body, 'div');
div.setAttribute('style','display:none;');
div.innerHTML = source;
store.getLoader().internalizeTiddler(store, tiddler,
tiddler.title, div.firstChild);
div.remove();
}
}/*,
tid: { extension: 'tid' },
json: { extension: 'tid.json' }
externalizedWithFormatter? sane to implement?
*/
},
// here and below "meta" means "info about registered external tiddler,
// be it loaded or not"
getExtension: function(meta) {
const format = this.fileFormats[meta.fileFormat];
if(!format) return; //# ok??
return format.extension;
},
externalizeTiddler: function(meta) {
const format = this.fileFormats[meta.fileFormat];
if(!format) return; //# ok??
return format.externalize(meta.tiddler);
},
internalizeTiddler: function(meta, source) {
const format = this.fileFormats[meta.fileFormat];
if(!format) return; //# ok??
const tiddler = store.fetchTiddler(meta.tiddlerName) ||
new Tiddler(meta.tiddlerName);
format.internalize(tiddler, source); //# pass meta to tiddler?
tiddler.doNotSave = function() { return !meta.keepInTW; };
meta.tiddler = tiddler;
return tiddler;
},
listName: "ExternalTiddlersList",
// read files list, load
init: function() {
const listTiddler = store.fetchTiddler(this.listName);
if(!listTiddler || !listTiddler.text) return;
wikify(listTiddler.text, createTiddlyElement(null, 'div'));
for(let meta of this.tiddlersMeta) this.loadExternal(meta);
},
handler: function(place, macroName, params, wikifier, paramString, tiddler) {
// parse params, register
const defaultParam = 'tiddler';
const pParams = paramString.parseParams(defaultParam, null, true);
const meta = {};
meta.tiddlerName = getParam(pParams, defaultParam);
if(!meta.tiddlerName) return;
// although called .fileName, it actually can contain relative part of a path
// fallback to meta.tiddlerName is set when calculating the full path
meta.fileName = getParam(pParams, 'file', '');
//# check if contains "bad" characters (like " or * ..for local paths only)
meta.fileFormat = getParam(pParams, 'format', 'text');
meta.isPlugin = getFlag(pParams, 'plugin'); //# allow just "plugin" instead of "plugin:true"?
const keepInternal = getParam(pParams, 'keepInternal');
meta.keepInTW = !!keepInternal && keepInternal !== 'false'; //# ~
this.registerExternal(meta);
// visual feedback
const macroText = wikifier.source.substring(wikifier.matchStart, wikifier.nextMatch);
createTiddlyText(place, 'external ');
createTiddlyLink(place, meta.tiddlerName, true);
createTiddlyText(place, ' (');
createTiddlyElement(place, 'code', '', '', macroText.slice(2 + macroName.length + 1, -2));
createTiddlyText(place, ')');
},
// describes tiddlers registered as external, not necessarily loaded
tiddlersMeta: [],
registerExternal: function(meta) {
//# check if already registered, don't register twice
this.tiddlersMeta.push(meta);
},
getMetaFor: function(tiddlerOrTitle) {
var isTitle = typeof tiddlerOrTitle == "string";
for(meta of this.tiddlersMeta)
if(isTitle && meta.tiddlerName == tiddlerOrTitle ||
!isTitle && meta.tiddler == tiddlerOrTitle)
return meta;
},
loadExternal: function(meta) {
// sync loading fails on startup because TF injects new mozillaLoadFile too late
// var tiddlerText = loadFile(getLocalPath(getFullPath(meta.fileName)));
// onExternalTiddlerLoad(tiddlerText !== null, meta, tiddlerText);
// so we use async instead:
const callback = this.onExternalTiddlerLoad
const path = getFullPath(meta.fileName, meta.tiddlerName, this.getExtension(meta));
// httpReq("GET", path, callback, meta) uses default dataType,
// which causes js to get evaluated on load. To avoid this, we customize the ajax call:
jQuery.ajax({
type: "GET",
url: path,
dataType: "text",
processData: false,
cache: false,
complete: function(xhr, textStatus) {
if((!xhr.status && location.protocol === "file:") || (xhr.status >= 200 && xhr.status < 300) || xhr.status === 304)
callback(true, meta, xhr.responseText, path, xhr)
else
callback(false, meta, null, path, xhr)
}
})
//# rename onExternalTiddlerLoad into internalizeAndRegister?
},
onExternalTiddlerLoad: function(success, meta, responseText) {
if(!success) return; //# notify somehow? may fail because file is not created yet or ...
const tiddler = config.macros.external.internalizeTiddler(meta, responseText);
store.addTiddler(tiddler);
//# what if tiddler already exists?
if(meta.isPlugin) {
// make it look normally
tiddler.tags.push('systemConfig');
const author = store.getTiddlerText(tiddler.title + "::Author");
if(author) {
tiddler.creator = tiddler.creator || author;
tiddler.modifier = tiddler.modifier || tiddler.creator;
}
eval(tiddler.text);
// for plugins introducing macros, formatters etc (may be adjusted in the future)
story.refreshAllTiddlers();
}
//meta.lastLoaded = responseText;
},
saveExternal: function(meta, callback) {
const fullPath = getFullPath(meta.fileName, meta.tiddlerName, this.getExtension(meta));
// we don't try to save remote files (yet)
if(!isLocalAbsolutePath(fullPath)) {
//# if(callback) callback(.., '[saving remote is not supported]')
return;
}
const localPath = getLocalPath(fullPath);
const contentToSave = this.externalizeTiddler(meta);
// save only if have something to save
//if(contentToSave != meta.lastLoaded) {
saveFile(localPath, contentToSave);
//# get result of saving, return it
//# or move externalizing into a separate helper?
// meta.lastLoaded = contentToSave;
//# this assumes saving didn't fail, which may be wrong
//}
//# if(callback) callback(.., '[...]')
},
saveAll: function() {
let overallSuccess = true;
for(let meta of this.tiddlersMeta) {
// a tiddler may got created after registration
if(!meta.tiddler) {
let tiddlerInStore = store.fetchTiddler(meta.tiddlerName);
if(tiddlerInStore) {
meta.tiddler = tiddlerInStore;
} else
// tiddler doesn't exist and we do nothing
continue;
//# based on config, we can show a warning instead
}
//# if(meta.tiddler.title != meta.tiddlerName)
// means tiddler got renamed → change meta.tiddlerName &
// update this.listName . If store contains another tiddler
// with that name, still keep the registered one?
overallSuccess = this.saveExternal(meta) && overallSuccess;
//# if saving failed, do something! (.oO dirty, notifying)
}
return overallSuccess;
}
};
//# see implementations in STP (share to the core?)
function isAbsolutePath(path) {
// covers http:, https:, file:, other schemas, windows paths (D:\...)
if(/^\w+\:/.exec(path))
return true;
// unix absolute paths, starting with /
if(/^\//.exec(path))
return true;
return false;
}
function isLocalAbsolutePath(path) {
//# rename? we're going to check whether an absolute path is local, not path is absolute local
return /^\w\:/.exec(path) || /^\//.exec(path) || /^file\:/.exec(path);
}
function getFullPath(subPath, nameFallback, extension) {
const fileNamePosition = subPath.lastIndexOf('/') + 1;
const fileName = subPath.substr(fileNamePosition);
if(fileName && fileName.indexOf('.') == -1)
subPath += '.' + extension;
if(!fileName)
subPath += nameFallback + '.' + extension;
if(isAbsolutePath(subPath))
return subPath;
const url = window.location.toString();
const base = url.substr(0, url.lastIndexOf('/') + 1);
return base + subPath;
}
//# ideally, don't save main store if it were not changed
if(!config.macros.external.orig_saveChanges) {
config.macros.external.orig_saveChanges = saveChanges;
saveChanges = function(onlyIfDirty, tiddlers) {
config.macros.external.saveAll();
//# should we do smth about setDirty (if saving of a tiddler failed)?
return config.macros.external.orig_saveChanges.apply(this, arguments);
}
}
// hijack method of store (since not present in TiddlyWiki.prototype)
if(!config.macros.external.orig_deleteTiddler) {
config.macros.external.orig_deleteTiddler = store.deleteTiddler;
store.deleteTiddler = function(title) {
var registeredMeta = config.macros.external.getMetaFor(title);
if(registeredMeta) registeredMeta.tiddler = null;
return config.macros.external.orig_deleteTiddler.apply(this, arguments);
}
}
//}}}
<<external [[DarkModePlugin]] file:"DarkModePlugin.js" plugin:true>>
~DarkModePlugin
the tool to save your eyes and your battery in dark places :)
<!--{{{-->
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2280%22>🌘</text></svg>">
<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml' />
<!--}}}-->
DarkModePlugin
DarkModePlugin