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