1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2025-12-08 17:58:05 +00:00

Revamp markdown plugin (#6528)

* Rename markdown to markdown-legacy

* Change how default renderWikiTextPragma value is displayed

To prevent out-of-sync, dynamically display the default value of
renderWikiTextPragma from the shadow tiddler instead of hard coding
the text in the "usage.tid".

* Repackage remarkable-based markdown plugin as markdown-legacy

- Rename plugin title to $:/plugins/tiddlywiki/markdown-legacy

- Add support for "text/markdown" MIME type and set that as the default
  when creating new markdown tiddlers

* Create new markdown plugin

* add support to text/markdown MIME type

* remove linkify and linkNewWindow config options

- linkify feature should be controlled by "extlink" TW parser rule;
  enabling markdown's linkify option will interfere with parsing

- remove the possibility to open external links in the same tab/window
  to match TW's behavior

* Ignore latex-parser wikirule in rednerWikiTextPragma

* Prevent camel-case link text from generating a link

* Update editions/markdowndemo

* Produce better parse tree

* Improve markdown/tiddlywiki integration

- widget block should not interrupt paragraph
- ignore tw-syntax links inside markdown-syntax links
- remove repeated renderWikiTextPragma parsing
- more efficient findNextMatch when examining tw rules

* Update user docs

* Replace includes() with indexOf() for legacy browsers
This commit is contained in:
cdruan
2023-01-14 01:49:04 -08:00
committed by GitHub
parent b5134951e5
commit 0c328a1696
84 changed files with 2443 additions and 359 deletions

View File

@@ -0,0 +1,15 @@
title: $:/plugins/tiddlywiki/markdown-legacy/EditorToolbar/bold
list-after: $:/core/ui/EditorToolbar/bold
tags: $:/tags/EditorToolbar
icon: $:/core/images/bold
caption: {{$:/language/Buttons/Bold/Caption}} (Markdown)
description: {{$:/language/Buttons/Bold/Hint}}
condition: [<targetTiddler>type[text/x-markdown]] [<targetTiddler>type[text/markdown]]
shortcuts: ((bold))
<$action-sendmessage
$message="tm-edit-text-operation"
$param="wrap-selection"
prefix="**"
suffix="**"
/>

View File

@@ -0,0 +1,15 @@
title: $:/plugins/tiddlywiki/markdown-legacy/EditorToolbar/heading-1
list-after: $:/core/ui/EditorToolbar/heading-1
tags: $:/tags/EditorToolbar
icon: $:/core/images/heading-1
caption: {{$:/language/Buttons/Heading1/Caption}} (Markdown)
description: {{$:/language/Buttons/Heading1/Hint}}
condition: [<targetTiddler>type[text/x-markdown]] [<targetTiddler>type[text/markdown]]
shortcuts: ((heading-1))
<$action-sendmessage
$message="tm-edit-text-operation"
$param="prefix-lines"
character="#"
count="1"
/>

View File

@@ -0,0 +1,15 @@
title: $:/plugins/tiddlywiki/markdown-legacy/EditorToolbar/heading-2
list-after: $:/core/ui/EditorToolbar/heading-2
tags: $:/tags/EditorToolbar
icon: $:/core/images/heading-2
caption: {{$:/language/Buttons/Heading2/Caption}} (Markdown)
description: {{$:/language/Buttons/Heading2/Hint}}
condition: [<targetTiddler>type[text/x-markdown]] [<targetTiddler>type[text/markdown]]
shortcuts: ((heading-2))
<$action-sendmessage
$message="tm-edit-text-operation"
$param="prefix-lines"
character="#"
count="2"
/>

View File

@@ -0,0 +1,15 @@
title: $:/plugins/tiddlywiki/markdown-legacy/EditorToolbar/heading-3
list-after: $:/core/ui/EditorToolbar/heading-3
tags: $:/tags/EditorToolbar
icon: $:/core/images/heading-3
caption: {{$:/language/Buttons/Heading3/Caption}} (Markdown)
description: {{$:/language/Buttons/Heading3/Hint}}
condition: [<targetTiddler>type[text/x-markdown]] [<targetTiddler>type[text/markdown]]
shortcuts: ((heading-3))
<$action-sendmessage
$message="tm-edit-text-operation"
$param="prefix-lines"
character="#"
count="3"
/>

View File

@@ -0,0 +1,15 @@
title: $:/plugins/tiddlywiki/markdown-legacy/EditorToolbar/heading-4
list-after: $:/core/ui/EditorToolbar/heading-4
tags: $:/tags/EditorToolbar
icon: $:/core/images/heading-4
caption: {{$:/language/Buttons/Heading4/Caption}} (Markdown)
description: {{$:/language/Buttons/Heading4/Hint}}
condition: [<targetTiddler>type[text/x-markdown]] [<targetTiddler>type[text/markdown]]
shortcuts: ((heading-4))
<$action-sendmessage
$message="tm-edit-text-operation"
$param="prefix-lines"
character="#"
count="4"
/>

View File

@@ -0,0 +1,15 @@
title: $:/plugins/tiddlywiki/markdown-legacy/EditorToolbar/heading-5
list-after: $:/core/ui/EditorToolbar/heading-5
tags: $:/tags/EditorToolbar
icon: $:/core/images/heading-5
caption: {{$:/language/Buttons/Heading5/Caption}} (Markdown)
description: {{$:/language/Buttons/Heading5/Hint}}
condition: [<targetTiddler>type[text/x-markdown]] [<targetTiddler>type[text/markdown]]
shortcuts: ((heading-5))
<$action-sendmessage
$message="tm-edit-text-operation"
$param="prefix-lines"
character="#"
count="5"
/>

View File

@@ -0,0 +1,15 @@
title: $:/plugins/tiddlywiki/markdown-legacy/EditorToolbar/heading-6
list-after: $:/core/ui/EditorToolbar/heading-6
tags: $:/tags/EditorToolbar
icon: $:/core/images/heading-6
caption: {{$:/language/Buttons/Heading6/Caption}} (Markdown)
description: {{$:/language/Buttons/Heading6/Hint}}
condition: [<targetTiddler>type[text/x-markdown]] [<targetTiddler>type[text/markdown]]
shortcuts: ((heading-6))
<$action-sendmessage
$message="tm-edit-text-operation"
$param="prefix-lines"
character="#"
count="6"
/>

View File

@@ -0,0 +1,15 @@
title: $:/plugins/tiddlywiki/markdown-legacy/EditorToolbar/italic
list-after: $:/core/ui/EditorToolbar/italic
tags: $:/tags/EditorToolbar
icon: $:/core/images/italic
caption: {{$:/language/Buttons/Italic/Caption}} (Markdown)
description: {{$:/language/Buttons/Italic/Hint}}
condition: [<targetTiddler>type[text/x-markdown]] [<targetTiddler>type[text/markdown]]
shortcuts: ((italic))
<$action-sendmessage
$message="tm-edit-text-operation"
$param="wrap-selection"
prefix="*"
suffix="*"
/>

View File

@@ -0,0 +1,73 @@
title: $:/plugins/tiddlywiki/markdown-legacy/EditorToolbar/link-dropdown
\define lingo-base() $:/language/Buttons/Link/
\define add-link-actions()
\whitespace trim
<$action-sendmessage $message="tm-edit-text-operation" $param="make-markdown-link" text={{$(linkTiddler)$}} />
<$action-deletetiddler $filter="[<dropdown-state>] [<searchTiddler>] [<linkTiddler>] [<storeTitle>] [<searchListState>]"/>
\end
\define get-focus-selector() [data-tiddler-title="$(cssEscapedTitle)$"] .tc-create-wikitext-link input
\define cancel-search-actions-inner()
<$set name="userInput" value={{{ [<storeTitle>get[text]] }}}><$list filter="[<searchTiddler>get[text]!match<userInput>]" emptyMessage="<$action-deletetiddler $filter='[<searchTiddler>] [<linkTiddler>] [<storeTitle>] [<searchListState>]'/>"><$action-setfield $tiddler=<<searchTiddler>> text=<<userInput>>/><$action-setfield $tiddler=<<refreshTitle>> text="yes"/></$list></$set>
\end
\define cancel-search-actions() <$list filter="[<storeTitle>!has[text]] +[<searchTiddler>!has[text]]" emptyMessage="<<cancel-search-actions-inner>>"><$action-sendmessage $message="tm-edit-text-operation" $param="focus-editor"/></$list>
\define external-link()
\whitespace trim
<$button class="tc-btn-invisible" style="width: auto; display: inline-block; background-colour: inherit;" actions=<<add-link-actions>>>
{{$:/core/images/chevron-right}}
</$button>
\end
\define set-next-input-tab(beforeafter:"after") <$macrocall $name="change-input-tab" stateTitle="$:/state/tab/search-results/sidebar" tag="$:/tags/SearchResults" beforeafter="$beforeafter$" defaultState={{$:/config/SearchResults/Default}} actions="<$action-setfield $tiddler='$:/state/search/currentTab' text=<<nextTab>>/>"/>
\define body(config-title)
\whitespace trim
''<<lingo Hint>>''
<$vars searchTiddler="""$config-title$/search""" linkTiddler="""$config-title$/link""" linktext="" searchListState=<<qualify "$:/temp/link-search/selected-item">> refreshTitle=<<qualify "$:/temp/link-search/refresh">> storeTitle=<<qualify "$:/temp/link-search/input">>>
<$vars linkTiddler=<<searchTiddler>>>
<$keyboard key="((input-tab-right))" actions=<<set-next-input-tab>>>
<$keyboard key="((input-tab-left))" actions=<<set-next-input-tab "before">> class="tc-create-wikitext-link">
<$macrocall $name="keyboard-driven-input" tiddler=<<searchTiddler>> storeTitle=<<storeTitle>>
selectionStateTitle=<<searchListState>> refreshTitle=<<refreshTitle>> type="search" filterMinLength="1"
tag="input" focus="true" class="tc-popup-handle" inputCancelActions=<<cancel-search-actions>>
inputAcceptActions=<<add-link-actions>> placeholder={{$:/language/Search/Search}} default=""
configTiddlerFilter="[[$:/state/search/currentTab]!is[missing]get[text]] ~[{$:/config/SearchResults/Default}]" />
</$keyboard>
</$keyboard>
&#32;
<$reveal tag="span" state=<<storeTitle>> type="nomatch" text="">
<<external-link>>
&#32;
<$button class="tc-btn-invisible" style="width: auto; display: inline-block; background-colour: inherit;">
<<cancel-search-actions>><$set name="cssEscapedTitle" value={{{ [<storyTiddler>escapecss[]] }}}><$action-sendmessage $message="tm-focus-selector" $param=<<get-focus-selector>>/></$set>
{{$:/core/images/close-button}}
</$button>
</$reveal>
</$vars>
<$reveal tag="div" state=<<storeTitle>> type="nomatch" text="">
<$linkcatcher actions=<<add-link-actions>> to=<<linkTiddler>>>
<$vars userInput={{{ [<storeTitle>get[text]] }}} configTiddler={{{ [[$:/state/search/currentTab]!is[missing]get[text]] ~[{$:/config/SearchResults/Default}] }}}>
{{$:/core/ui/SearchResults}}
</$vars>
</$linkcatcher>
</$reveal>
</$vars>
\end
<$macrocall $name="body" config-title=<<qualify "$:/state/Link/">>/>

View File

@@ -0,0 +1,11 @@
title: $:/plugins/tiddlywiki/markdown-legacy/EditorToolbar/link
list-after: $:/core/ui/EditorToolbar/link
tags: $:/tags/EditorToolbar
icon: $:/core/images/link
caption: {{$:/language/Buttons/Link/Caption}}
description: {{$:/language/Buttons/Link/Hint}}
condition: [<targetTiddler>type[text/x-markdown]] [<targetTiddler>type[text/markdown]]
button-classes: tc-text-editor-toolbar-item-start-group
shortcuts: ((link))
dropdown: $:/plugins/tiddlywiki/markdown-legacy/EditorToolbar/link-dropdown

View File

@@ -0,0 +1,15 @@
caption: {{$:/language/Buttons/Linkify/Caption}} (Markdown)
condition: [<targetTiddler>type[text/x-markdown]] [<targetTiddler>type[text/markdown]]
description: {{$:/language/Buttons/Linkify/Hint}}
icon: $:/plugins/tiddlywiki/markdown-legacy/images/markdown-linkify
list-after: $:/core/ui/EditorToolbar/linkify
shortcuts: ((linkify))
title: $:/plugins/tiddlywiki/markdown-legacy/EditorToolbar/linkify
tags: $:/tags/EditorToolbar
<$action-sendmessage
$message="tm-edit-text-operation"
$param="wrap-selection"
prefix="["
suffix="]()"
/>

View File

@@ -0,0 +1,15 @@
title: $:/plugins/tiddlywiki/markdown-legacy/EditorToolbar/list-bullet
list-after: $:/core/ui/EditorToolbar/list-bullet
tags: $:/tags/EditorToolbar
icon: $:/core/images/list-bullet
caption: {{$:/language/Buttons/ListBullet/Caption}} (Markdown)
description: {{$:/language/Buttons/ListBullet/Hint}}
condition: [<targetTiddler>type[text/x-markdown]] [<targetTiddler>type[text/markdown]]
shortcuts: ((list-bullet))
<$action-sendmessage
$message="tm-edit-text-operation"
$param="prefix-lines"
character="*"
count="1"
/>

View File

@@ -0,0 +1,15 @@
title: $:/plugins/tiddlywiki/markdown-legacy/EditorToolbar/list-number
list-after: $:/core/ui/EditorToolbar/list-number
tags: $:/tags/EditorToolbar
icon: $:/core/images/list-number
caption: {{$:/language/Buttons/ListNumber/Caption}} (Markdown)
description: {{$:/language/Buttons/ListNumber/Hint}}
condition: [<targetTiddler>type[text/x-markdown]] [<targetTiddler>type[text/markdown]]
shortcuts: ((list-number))
<$action-sendmessage
$message="tm-edit-text-operation"
$param="prefix-lines"
character="1."
count="1"
/>

View File

@@ -0,0 +1,17 @@
title: $:/plugins/tiddlywiki/markdown-legacy/EditorToolbar/mono-block
list-after: $:/core/ui/EditorToolbar/mono-block
tags: $:/tags/EditorToolbar
icon: $:/core/images/mono-block
caption: {{$:/language/Buttons/MonoBlock/Caption}} (Markdown)
description: {{$:/language/Buttons/MonoBlock/Hint}}
condition: [<targetTiddler>type[text/x-markdown]] [<targetTiddler>type[text/markdown]]
button-classes: tc-text-editor-toolbar-item-start-group
shortcuts: ((mono-block))
<$action-sendmessage
$message="tm-edit-text-operation"
$param="wrap-lines"
prefix="
```"
suffix="```"
/>

View File

@@ -0,0 +1,15 @@
title: $:/plugins/tiddlywiki/markdown-legacy/EditorToolbar/mono-line
list-after: $:/core/ui/EditorToolbar/mono-line
tags: $:/tags/EditorToolbar
icon: $:/core/images/mono-line
caption: {{$:/language/Buttons/MonoLine/Caption}} (Markdown)
description: {{$:/language/Buttons/MonoLine/Hint}}
condition: [<targetTiddler>type[text/x-markdown]] [<targetTiddler>type[text/markdown]]
shortcuts: ((mono-line))
<$action-sendmessage
$message="tm-edit-text-operation"
$param="wrap-selection"
prefix="`"
suffix="`"
/>

View File

@@ -0,0 +1,15 @@
title: $:/plugins/tiddlywiki/markdown-legacy/EditorToolbar/quote
list-after: $:/core/ui/EditorToolbar/quote
tags: $:/tags/EditorToolbar
icon: $:/core/images/quote
caption: {{$:/language/Buttons/Quote/Caption}} (Markdown)
description: {{$:/language/Buttons/Quote/Hint}}
condition: [<targetTiddler>type[text/x-markdown]] [<targetTiddler>type[text/markdown]]
shortcuts: ((quote))
<$action-sendmessage
$message="tm-edit-text-operation"
$param="prefix-lines"
character=">"
count="1"
/>

View File

@@ -0,0 +1,8 @@
title: $:/plugins/tiddlywiki/markdown-legacy/KeyboardShortcuts/new-markdown-tiddler
tags: $:/tags/KeyboardShortcut
key: ((new-markdown-tiddler))
\whitespace trim
<$navigator story="$:/StoryList" history="$:/HistoryList" openLinkFromInsideRiver={{$:/config/Navigation/openLinkFromInsideRiver}} openLinkFromOutsideRiver={{$:/config/Navigation/openLinkFromOutsideRiver}} relinkOnRename={{$:/config/RelinkOnRename}}>
<$action-sendmessage $message="tm-new-tiddler" type="text/markdown"/>
</$navigator>

View File

@@ -0,0 +1,5 @@
title: $:/config/
ShortcutInfo/new-markdown-tiddler: {{$:/language/Buttons/NewMarkdown/Hint}}
shortcuts-mac/new-markdown-tiddler: ctrl-M
shortcuts-not-mac/new-markdown-tiddler: alt-M

View File

@@ -0,0 +1,3 @@
title: $:/config/markdown/breaks
false

View File

@@ -0,0 +1,3 @@
title: $:/config/markdown/linkNewWindow
true

View File

@@ -0,0 +1,3 @@
title: $:/config/markdown/linkify
false

View File

@@ -0,0 +1,3 @@
title: $:/config/markdown/quotes
“”‘’

View File

@@ -0,0 +1,3 @@
title: $:/config/markdown/renderWikiText
true

View File

@@ -0,0 +1,3 @@
title: $:/config/markdown/renderWikiTextPragma
\rules only html image macrocallinline syslink transcludeinline wikilink filteredtranscludeblock macrocallblock transcludeblock latex-parser

View File

@@ -0,0 +1,3 @@
title: $:/config/markdown/typographer
false

View File

@@ -0,0 +1,4 @@
title: $:/language/Docs/Types/text/markdown
description: Markdown
name: text/markdown
group: Text

View File

@@ -0,0 +1,37 @@
/*\
title: $:/plugins/tiddlywiki/markdown-legacy/editor-operations/make-markdown-link.js
type: application/javascript
module-type: texteditoroperation
Text editor operation to make a markdown link
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports["make-markdown-link"] = function(event,operation) {
if(operation.selection) {
if(event.paramObject.text.indexOf("://") !== -1) {
operation.replacement = "[" + operation.selection + "](" + event.paramObject.text + ")";
} else {
operation.replacement = "[" + operation.selection + "](#" + event.paramObject.text.replaceAll(" ", "%20") + ")";
}
operation.cutStart = operation.selStart;
operation.cutEnd = operation.selEnd;
} else {
if(event.paramObject.text.indexOf("://") !== -1) {
operation.replacement = "<" + event.paramObject.text + ">";
} else {
operation.replacement = "[](#" + event.paramObject.text.replaceAll(" ", "%20") + ")";
}
operation.cutStart = operation.selStart;
operation.cutEnd = operation.selEnd;
}
operation.newSelStart = operation.selStart + operation.replacement.length;
operation.newSelEnd = operation.newSelStart;
};
})();

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Brad Howes
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,137 @@
"use strict";
/**
* Plugin for Remarkable Markdown processor which transforms $..$ and $$..$$ sequences into math HTML using the
* Katex package.
*/
const rkatex = (md, options) => {
const backslash = '\\';
const dollar = '$';
const opts = options || {};
const delimiter = opts.delimiter || dollar;
if (delimiter.length !== 1) { throw new Error('invalid delimiter'); }
const katex = require("katex");
/**
* Render the contents as KaTeX
*/
const renderKatex = (source, displayMode) => katex.renderToString(source,
{displayMode: displayMode,
throwOnError: false});
/**
* Parse '$$' as a block. Based off of similar method in remarkable.
*/
const parseBlockKatex = (state, startLine, endLine) => {
let haveEndMarker = false;
let pos = state.bMarks[startLine] + state.tShift[startLine];
let max = state.eMarks[startLine];
if (pos + 1 > max) { return false; }
const marker = state.src.charAt(pos);
if (marker !== delimiter) { return false; }
// scan marker length
let mem = pos;
pos = state.skipChars(pos, marker);
let len = pos - mem;
if (len !== 2) { return false; }
// search end of block
let nextLine = startLine;
for (;;) {
++nextLine;
if (nextLine >= endLine) { break; }
pos = mem = state.bMarks[nextLine] + state.tShift[nextLine];
max = state.eMarks[nextLine];
if (pos < max && state.tShift[nextLine] < state.blkIndent) { break; }
if (state.src.charAt(pos) !== delimiter) { continue; }
if (state.tShift[nextLine] - state.blkIndent >= 4) { continue; }
pos = state.skipChars(pos, marker);
if (pos - mem < len) { continue; }
pos = state.skipSpaces(pos);
if (pos < max) { continue; }
haveEndMarker = true;
break;
}
// If a fence has heading spaces, they should be removed from its inner block
len = state.tShift[startLine];
state.line = nextLine + (haveEndMarker ? 1 : 0);
const content = state.getLines(startLine + 1, nextLine, len, true)
.replace(/[ \n]+/g, ' ')
.trim();
state.tokens.push({type: 'katex', params: null, content: content, lines: [startLine, state.line],
level: state.level, block: true});
return true;
};
/**
* Look for '$' or '$$' spans in Markdown text. Based off of the 'fenced' parser in remarkable.
*/
const parseInlineKatex = (state, silent) => {
const start = state.pos;
const max = state.posMax;
let pos = start;
// Unexpected starting character
if (state.src.charAt(pos) !== delimiter) { return false; }
++pos;
while (pos < max && state.src.charAt(pos) === delimiter) { ++pos; }
// Capture the length of the starting delimiter -- closing one must match in size
const marker = state.src.slice(start, pos);
if (marker.length > 2) { return false; }
const spanStart = pos;
let escapedDepth = 0;
while (pos < max) {
const char = state.src.charAt(pos);
if (char === '{' && (pos == 0 || state.src.charAt(pos - 1) != backslash)) {
escapedDepth += 1;
} else if (char === '}' && (pos == 0 || state.src.charAt(pos - 1) != backslash)) {
escapedDepth -= 1;
if (escapedDepth < 0) { return false; }
} else if (char === delimiter && escapedDepth === 0) {
const matchStart = pos;
let matchEnd = pos + 1;
while (matchEnd < max && state.src.charAt(matchEnd) === delimiter) { ++matchEnd; }
if (matchEnd - matchStart === marker.length) {
if (!silent) {
const content = state.src.slice(spanStart, matchStart)
.replace(/[ \n]+/g, ' ')
.trim();
state.push({type: 'katex', content: content, block: marker.length > 1, level: state.level});
}
state.pos = matchEnd;
return true;
}
}
pos += 1;
}
if (!silent) { state.pending += marker; }
state.pos += marker.length;
return true;
};
md.inline.ruler.push('katex', parseInlineKatex, options);
md.block.ruler.push('katex', parseBlockKatex, options);
md.renderer.rules.katex = (tokens, idx) => renderKatex(tokens[idx].content, tokens[idx].block);
md.renderer.rules.katex.delimiter = delimiter;
};
module.exports = rkatex;

View File

@@ -0,0 +1,20 @@
"use strict";const rkatex=(md,options)=>{const backslash='\\';const dollar='$';const opts=options||{};const delimiter=opts.delimiter||dollar;if(delimiter.length!==1){throw new Error('invalid delimiter');}
const katex=require("katex");const renderKatex=(source,displayMode)=>katex.renderToString(source,{displayMode:displayMode,throwOnError:false});const parseBlockKatex=(state,startLine,endLine)=>{let haveEndMarker=false;let pos=state.bMarks[startLine]+state.tShift[startLine];let max=state.eMarks[startLine];if(pos+1>max){return false;}
const marker=state.src.charAt(pos);if(marker!==delimiter){return false;}
let mem=pos;pos=state.skipChars(pos,marker);let len=pos-mem;if(len!==2){return false;}
let nextLine=startLine;for(;;){++nextLine;if(nextLine>=endLine){break;}
pos=mem=state.bMarks[nextLine]+state.tShift[nextLine];max=state.eMarks[nextLine];if(pos<max&&state.tShift[nextLine]<state.blkIndent){break;}
if(state.src.charAt(pos)!==delimiter){continue;}
if(state.tShift[nextLine]-state.blkIndent>=4){continue;}
pos=state.skipChars(pos,marker);if(pos-mem<len){continue;}
pos=state.skipSpaces(pos);if(pos<max){continue;}
haveEndMarker=true;break;}
len=state.tShift[startLine];state.line=nextLine+(haveEndMarker?1:0);const content=state.getLines(startLine+1,nextLine,len,true).replace(/[ \n]+/g,' ').trim();state.tokens.push({type:'katex',params:null,content:content,lines:[startLine,state.line],level:state.level,block:true});return true;};const parseInlineKatex=(state,silent)=>{const start=state.pos;const max=state.posMax;let pos=start;if(state.src.charAt(pos)!==delimiter){return false;}
++pos;while(pos<max&&state.src.charAt(pos)===delimiter){++pos;}
const marker=state.src.slice(start,pos);if(marker.length>2){return false;}
const spanStart=pos;let escapedDepth=0;while(pos<max){const char=state.src.charAt(pos);if(char==='{'&&(pos==0||state.src.charAt(pos-1)!=backslash)){escapedDepth+=1;}else if(char==='}'&&(pos==0||state.src.charAt(pos-1)!=backslash)){escapedDepth-=1;if(escapedDepth<0){return false;}}else if(char===delimiter&&escapedDepth===0){const matchStart=pos;let matchEnd=pos+1;while(matchEnd<max&&state.src.charAt(matchEnd)===delimiter){++matchEnd;}
if(matchEnd-matchStart===marker.length){if(!silent){const content=state.src.slice(spanStart,matchStart).replace(/[ \n]+/g,' ').trim();state.push({type:'katex',content:content,block:marker.length>1,level:state.level});}
state.pos=matchEnd;return true;}}
pos+=1;}
if(!silent){state.pending+=marker;}
state.pos+=marker.length;return true;};md.inline.ruler.push('katex',parseInlineKatex,options);md.block.ruler.push('katex',parseBlockKatex,options);md.renderer.rules.katex=(tokens,idx)=>renderKatex(tokens[idx].content,tokens[idx].block);md.renderer.rules.katex.delimiter=delimiter;};module.exports=rkatex;

View File

@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2014-2016, Jon Schlinkert
Copyright (c) 2014 Jon Schlinkert, Vitaly Puzrin.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,36 @@
{
"tiddlers": [
{
"file": "remarkable.js",
"fields": {
"type": "application/javascript",
"title": "$:/plugins/tiddlywiki/markdown-legacy/remarkable.js",
"module-type": "library"
}
},
{
"file": "remarkable-license.txt",
"fields": {
"type": "text/plain",
"title": "$:/plugins/tiddlywiki/markdown-legacy/remarkable-license"
}
},
{
"file": "remarkable-katex.min.js",
"fields": {
"type": "application/javascript",
"title": "$:/plugins/tiddlywiki/markdown-legacy/remarkable-katex.js",
"module-type": "library"
},
"prefix": "(function(realRequire) {var require = function(m) {if(m===\"katex\"){m = \"$:/plugins/tiddlywiki/katex/katex.min.js\"};return realRequire(m);};",
"suffix": "})(require);\n"
},
{
"file": "remarkable-katex-license.txt",
"fields": {
"type": "text/plain",
"title": "$:/plugins/tiddlywiki/markdown-legacy/remarkable-katex-license"
}
}
]
}

View File

@@ -0,0 +1,6 @@
title: $:/plugins/tiddlywiki/markdown-legacy/images/markdown-linkify
tags: $:/tags/Image
<svg width="22pt" height="22pt" class="tc-markdown-linkify-button tc-image-button" viewBox="0 0 128 128">
<path d="M17.031185,32.1989189 L9.04781705,32.1989189 L9.04781705,97.1303119 L17.031185,97.1303119 L17.031185,104.049231 L0,104.049231 L0,25.28 L17.031185,25.28 L17.031185,32.1989189 Z M93.6716009,24.75 C90.4007675,30.8326023 88.0193713,37.1590826 86.5274123,43.7294408 C85.0354532,50.299799 84.2894737,56.9705775 84.2894737,63.7417763 C84.2894737,70.6277412 85.0211075,77.3702485 86.484375,83.9692982 C87.9476425,90.568348 90.314693,96.9952485 93.5855263,103.25 L93.5855263,103.25 L83.4287281,103.25 C79.8135965,97.3395468 77.0161732,91.1134868 75.0364583,84.5718202 C73.0567434,78.0301535 72.066886,71.3737208 72.066886,64.6025219 C72.066886,61.3890716 72.3107639,58.017818 72.7985197,54.488761 C73.2862756,50.9597039 74.0035636,47.4449927 74.9503838,43.9446272 C75.8972039,40.4442617 77.0735563,37.0586623 78.4794408,33.7878289 C79.8853253,30.5169956 81.5350877,27.504386 83.4287281,24.75 L83.4287281,24.75 Z M116.638158,24.75 C120.253289,30.6604532 123.050713,36.9152047 125.030428,43.5142544 C127.010143,50.1133041 128,56.7984284 128,63.5696272 C128,66.7830775 127.770468,70.1543311 127.311404,73.6833882 C126.852339,77.2124452 126.149397,80.7128107 125.202577,84.1844846 C124.255757,87.6561586 123.065058,91.0274123 121.630482,94.2982456 C120.195906,97.5690789 118.531798,100.552997 116.638158,103.25 L116.638158,103.25 L106.48136,103.25 C109.637427,97.1673977 111.975786,90.8696089 113.496436,84.3566338 C115.017087,77.8436586 115.777412,71.2015716 115.777412,64.4303728 C115.777412,57.5444079 115.031433,50.7732091 113.539474,44.1167763 C112.047515,37.4603436 109.723501,31.0047515 106.567434,24.75 L106.567434,24.75 Z M37.1101871,44.1061384 L37.1101871,56.702119 L49.0852391,52.799139 L51.3915454,59.8954661 L39.3277893,63.798446 L46.956341,74.1768244 L40.8357588,78.6120289 L33.2072072,68.1449464 L25.7560638,78.3459166 L19.8128898,73.8220081 L27.4414414,63.798446 L15.2889813,59.6293539 L17.5952876,52.5330268 L29.6590437,56.702119 L29.6590437,44.1061384 L37.1101871,44.1061384 Z M49.6493416,97.1303119 L57.6327096,97.1303119 L57.6327096,32.1989189 L49.6493416,32.1989189 L49.6493416,25.28 L66.6805267,25.28 L66.6805267,104.049231 L49.6493416,104.049231 L49.6493416,97.1303119 Z"></path>
</svg>

View File

@@ -0,0 +1,10 @@
title: $:/plugins/tiddlywiki/markdown-legacy/images/new-markdown-button
tags: $:/tags/Image
<svg class="tc-image-new-markdown-button tc-image-button" viewBox="0 0 128 128" width="22pt" height="22pt">
<g fill-rule="evenodd">
<rect x="80" y="96" width="48" height="16" rx="8"></rect>
<rect x="96" y="80" width="16" height="48" rx="8"></rect>
<path d="M3.23876972,39.5396716 C3.23876972,35.9653274 6.13586353,33.0691646 9.7141757,33.0691646 L98.1283744,33.0691646 C101.706101,33.0691646 104.60378,35.9646626 104.60378,39.5396716 L104.60378,84.8296213 C104.60378,88.4039654 101.706687,91.3001282 98.1283744,91.3001282 L9.7141757,91.3001282 C6.13644944,91.3001282 3.23876972,88.4046302 3.23876972,84.8296213 L3.23876972,39.5396716 L3.23876972,39.5396716 Z M-2.15298617,39.5396716 L-2.15298617,84.8296213 C-2.15298617,91.3833243 3.15957363,96.6918841 9.7141757,96.6918841 L98.1283744,96.6918841 C104.684083,96.6918841 109.995536,91.382138 109.995536,84.8296213 L109.995536,39.5396716 C109.995536,32.9859686 104.682977,27.6774087 98.1283744,27.6774087 L9.7141757,27.6774087 C3.15846686,27.6774087 -2.15298617,32.9871549 -2.15298617,39.5396716 Z M14.0222815,80.5166164 L14.0222815,43.8526764 L24.8057933,43.8526764 L35.589305,57.3320661 L46.3728168,43.8526764 L57.1563286,43.8526764 L57.1563286,80.5166164 L46.3728168,80.5166164 L46.3728168,59.4887685 L35.589305,72.9681582 L24.8057933,59.4887685 L24.8057933,80.5166164 L14.0222815,80.5166164 Z M81.4192301,80.5166164 L65.2439624,62.723822 L76.0274742,62.723822 L76.0274742,43.8526764 L86.810986,43.8526764 L86.810986,62.723822 L97.5944978,62.723822 L81.4192301,80.5166164 Z"transform="translate(53.921275, 62.184646) rotate(-60.000000) translate(-53.921275, -62.184646) "></path>
</g>
</svg>

View File

@@ -0,0 +1,16 @@
title: $:/plugins/tiddlywiki/markdown-legacy/new-markdown-button
tags: $:/tags/PageControls
caption: {{$:/plugins/tiddlywiki/markdown-legacy/images/new-markdown-button}} {{$:/language/Buttons/NewMarkdown/Caption}}
description: {{$:/language/Buttons/NewMarkdown/Hint}}
list-after: $:/core/ui/Buttons/new-tiddler
\whitespace trim
<$button tooltip={{$:/language/Buttons/NewMarkdown/Hint}} aria-label={{$:/language/Buttons/NewMarkdown/Caption}} class=<<tv-config-toolbar-class>>>
<$action-sendmessage $message="tm-new-tiddler" type="text/markdown"/>
<$list filter="[<tv-config-toolbar-icons>match[yes]]">
{{$:/plugins/tiddlywiki/markdown-legacy/images/new-markdown-button}}
</$list>
<$list filter="[<tv-config-toolbar-text>match[yes]]">
<span class="tc-btn-text"><$text text={{$:/language/Buttons/NewMarkdown/Caption}}/></span>
</$list>
</$button>

View File

@@ -0,0 +1,6 @@
{
"title": "$:/plugins/tiddlywiki/markdown-legacy",
"name": "Markdown (Legacy)",
"description": "Markdown parser based on remarkable by Jon Schlinkert and remarkable-katex by Brad Howes",
"list": "readme usage remarkable-license remarkable-katex-license"
}

View File

@@ -0,0 +1,7 @@
title: $:/plugins/tiddlywiki/markdown-legacy/readme
This is a TiddlyWiki plugin for parsing Markdown text, using the [[Remarkable|https://github.com/jonschlinkert/remarkable]] library. If the KaTeX TiddlyWiki plugin is installed, KaTeX support is enabled using the [[remarkable-katex|https://github.com/bradhowes/remarkable-katex]] Remarkable plugin.
It is completely self-contained, and doesn't need an Internet connection in order to work. It works both in the browser and under Node.js.
[[Source code|https://github.com/Jermolene/TiddlyWiki5/blob/master/plugins/tiddlywiki/markdown-legacy]]

View File

@@ -0,0 +1,36 @@
title: $:/plugins/tiddlywiki/markdown-legacy/usage
! Plugin Configuration
|!Config |!Default |!Description |
| <code>[[breaks|$:/config/markdown/breaks]]</code>| ``false``|Remarkable library config: Convert '\n' in paragraphs into ``<br>`` |
| <code>[[linkify|$:/config/markdown/linkify]]</code>| ``false``|Remarkable library config: Autoconvert URL-like text to links |
| <code>[[linkNewWindow|$:/config/markdown/linkNewWindow]]</code>| ``true``|For external links, should clicking on them open a new window/tab automatically? |
| <code>[[quotes|$:/config/markdown/quotes]]</code>| ``“”‘’``|Remarkable library config: Double + single quotes replacement pairs, when ``typographer`` enabled |
| <code>[[renderWikiText|$:/config/markdown/renderWikiText]]</code>| ``true``|After Markdown is parsed, should any text elements be handed off to the ~WikiText parser for further processing? |
| <code>[[renderWikiTextPragma|$:/config/markdown/renderWikiTextPragma]]</code>|<code><$view tiddler="$:/plugins/tiddlywiki/markdown-legacy" subtiddler="$:/config/markdown/renderWikiTextPragma" mode="inline"/></code>|When handing off to the ~WikiText parser, what pragma rules should it follow? |
| <code>[[typographer|$:/config/markdown/typographer]]</code>| ``false``|Remarkable library config: Enable some language-neutral replacement + quotes beautification |
! Creating ~WikiLinks
Create wiki links with the usual Markdown link syntax targeting `#` and the target tiddler title:
```
[link text](#TiddlerTitle)
```
If the target tiddler has a space in its name, that name must be URL-escaped to be detected as a URL:
```
[link text](#Test%20Tiddler)
```
! Images
Markdown image syntax can be used to reference images by tiddler title or an external URI. For example:
```
![alt text](/path/to/img.jpg "Title")
![alt text](Motovun Jack.jpg "Title")
```

View File

@@ -0,0 +1,341 @@
/*\
title: $:/plugins/tiddlywiki/markdown-legacy/wrapper.js
type: application/javascript
module-type: parser
Wraps up the remarkable parser for use as a Parser in TiddlyWiki
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var r = require("$:/plugins/tiddlywiki/markdown-legacy/remarkable.js");
var Remarkable = r.Remarkable,
linkify = r.linkify,
utils = r.utils;
///// Set up configuration options /////
function parseAsBoolean(tiddlerName) {
return $tw.wiki.getTiddlerText(tiddlerName).toLowerCase() === "true";
}
var pluginOpts = {
linkNewWindow: parseAsBoolean("$:/config/markdown/linkNewWindow"),
renderWikiText: parseAsBoolean("$:/config/markdown/renderWikiText"),
renderWikiTextPragma: $tw.wiki.getTiddlerText("$:/config/markdown/renderWikiTextPragma").trim()
};
var remarkableOpts = {
breaks: parseAsBoolean("$:/config/markdown/breaks"),
quotes: $tw.wiki.getTiddlerText("$:/config/markdown/quotes"),
typographer: parseAsBoolean("$:/config/markdown/typographer")
};
var accumulatingTypes = {
"text": true,
"softbreak": true
};
// If rendering WikiText, we treat katex nodes as text.
if(pluginOpts.renderWikiText) {
accumulatingTypes["katex"] = true;
}
var md = new Remarkable(remarkableOpts);
// If tiddlywiki/katex plugin is present, use remarkable-katex to enable katex support.
if($tw.modules.titles["$:/plugins/tiddlywiki/katex/katex.min.js"]) {
var rk = require("$:/plugins/tiddlywiki/markdown-legacy/remarkable-katex.js");
md = md.use(rk);
}
if(parseAsBoolean("$:/config/markdown/linkify")) {
md = md.use(linkify);
}
function findTagWithType(nodes, startPoint, type, level) {
for (var i = startPoint; i < nodes.length; i++) {
if(nodes[i].type === type && nodes[i].level === level) {
return i;
}
}
return false;
}
/**
* Remarkable creates nodes that look like:
* [
* { type: 'paragraph_open'},
* { type: 'inline', content: 'Hello World', children:[{type: 'text', content: 'Hello World'}]},
* { type: 'paragraph_close'}
* ]
*
* But TiddlyWiki wants the Parser (https://tiddlywiki.com/dev/static/Parser.html) to emit nodes like:
*
* [
* { type: 'element', tag: 'p', children: [{type: 'text', text: 'Hello World'}]}
* ]
*/
function convertNodes(remarkableTree, isStartOfInline) {
let out = [];
var accumulatedText = '';
function withChildren(currentIndex, currentLevel, closingType, nodes, callback) {
var j = findTagWithType(nodes, currentIndex + 1, closingType, currentLevel);
if(j === false) {
console.error("Failed to find a " + closingType + " node after position " + currentIndex);
console.log(nodes);
return currentIndex + 1;
}
let children = convertNodes(nodes.slice(currentIndex + 1, j));
callback(children);
return j;
}
function wrappedElement(elementTag, currentIndex, currentLevel, closingType, nodes) {
return withChildren(currentIndex, currentLevel, closingType, nodes, function(children) {
out.push({
type: "element",
tag: elementTag,
children: children
});
});
}
for (var i = 0; i < remarkableTree.length; i++) {
var currentNode = remarkableTree[i];
switch (currentNode.type) {
case "paragraph_open":
// If the paragraph is a "tight" layout paragraph, don't wrap children in a <p> tag.
if(currentNode.tight) {
i = withChildren(i, currentNode.level, "paragraph_close", remarkableTree, function(children) {
Array.prototype.push.apply(out, children);
});
} else {
i = wrappedElement("p", i, currentNode.level, "paragraph_close", remarkableTree);
}
break;
case "heading_open":
i = wrappedElement("h" + currentNode.hLevel, i, currentNode.level, "heading_close", remarkableTree);
break;
case "bullet_list_open":
i = wrappedElement("ul", i, currentNode.level, "bullet_list_close", remarkableTree);
break;
case "ordered_list_open":
i = wrappedElement('ol', i, currentNode.level,'ordered_list_close', remarkableTree);
break;
case "list_item_open":
i = wrappedElement("li", i, currentNode.level, "list_item_close", remarkableTree);
break;
case "link_open":
i = withChildren(i, currentNode.level, "link_close", remarkableTree, function(children) {
if(currentNode.href[0] !== "#") {
// External link
var attributes = {
class: { type: "string", value: "tc-tiddlylink-external" },
href: { type: "string", value: currentNode.href },
rel: { type: "string", value: "noopener noreferrer" }
};
if(pluginOpts.linkNewWindow) {
attributes.target = { type: "string", value: "_blank" };
}
out.push({
type: "element",
tag: "a",
attributes: attributes,
children: children
});
} else {
// Internal link
out.push({
type: "link",
attributes: {
to: { type: "string", value: $tw.utils.decodeURISafe(currentNode.href.substr(1)) }
},
children: children
});
}
});
break;
case "code":
out.push({
type: "element",
tag: currentNode.block ? "pre" : "code",
children: [{ type: "text", text: currentNode.content }]
});
break;
case "fence":
out.push({
type: "codeblock",
attributes: {
language: { type: "string", value: currentNode.params },
code: { type: "string", value: currentNode.content }
}
});
break;
case "image":
out.push({
type: "image",
attributes: {
tooltip: { type: "string", value: currentNode.alt },
source: { type: "string", value: $tw.utils.decodeURIComponentSafe(currentNode.src) }
}
});
break;
case "softbreak":
if(remarkableOpts.breaks) {
out.push({
type: "element",
tag: "br",
});
} else {
accumulatedText = accumulatedText + '\n';
}
break;
case "hardbreak":
out.push({
type: "element",
tag: "br",
});
break;
case "th_open":
case "td_open":
var elementTag = currentNode.type.slice(0, 2);
i = withChildren(i, currentNode.level, elementTag + "_close", remarkableTree, function(children) {
var attributes = {};
if(currentNode.align) {
attributes.style = { type: "string", value: "text-align:" + currentNode.align };
}
out.push({
type: "element",
tag: elementTag,
attributes: attributes,
children: children
});
});
break;
case "hr":
out.push({
type: 'element',
tag: 'hr',
});
break;
case "inline":
out = out.concat(convertNodes(currentNode.children, true));
break;
case "text":
// We need to merge this text block with the upcoming text block and parse it all together.
accumulatedText = accumulatedText + currentNode.content;
break;
case "katex":
// If rendering WikiText, convert the katex node back to text for parsing by the WikiText LaTeX parser.
if(pluginOpts.renderWikiText) {
// If this is a block, add a newline to trigger the KaTeX plugins block detection.
var displayModeSuffix = currentNode.block ? "\n" : "";
accumulatedText = accumulatedText + "$$" + currentNode.content + displayModeSuffix + "$$";
} else {
out.push({
type: "latex",
attributes: {
text: { type: "text", value: currentNode.content },
displayMode: { type: "text", value: currentNode.block ? "true" : "false" }
}
});
}
break;
default:
if(currentNode.type.substr(currentNode.type.length - 5) === "_open") {
var tagName = currentNode.type.substr(0, currentNode.type.length - 5);
i = wrappedElement(tagName, i, currentNode.level, tagName + "_close", remarkableTree);
} else {
console.error("Unknown node type: " + currentNode.type, currentNode);
out.push({
type: "text",
text: currentNode.content
});
}
break;
}
// We test to see if we process the block now, or if there's
// more to accumulate first.
if(accumulatedText
&& (
remarkableOpts.breaks ||
(i+1) >= remarkableTree.length ||
!accumulatingTypes[remarkableTree[i+1].type]
)
) {
// The Markdown compiler thinks this is just text.
// Hand off to the WikiText parser to see if there's more to render
// But only if it's configured to, and we have more than whitespace
if(!pluginOpts.renderWikiText || accumulatedText.match(/^\s*$/)) {
out.push({
type: "text",
text: accumulatedText
});
} else {
// If we're inside a block element (div, p, td, h1), and this is the first child in the tree,
// handle as a block-level parse. Otherwise not.
var parseAsInline = !(isStartOfInline && i === 0);
var textToParse = accumulatedText;
if(pluginOpts.renderWikiTextPragma !== "") {
textToParse = pluginOpts.renderWikiTextPragma + "\n" + textToParse;
}
var wikiParser = $tw.wiki.parseText("text/vnd.tiddlywiki", textToParse, {
parseAsInline: parseAsInline
});
var rs = wikiParser.tree;
// If we parsed as a block, but the root element the WikiText parser gave is a paragraph,
// we should discard the paragraph, since the way Remarkable nests its nodes, this "inline"
// node is always inside something else that's a block-level element
if(!parseAsInline
&& rs.length === 1
&& rs[0].type === "element"
&& rs[0].tag === "p"
) {
rs = rs[0].children;
}
// If the original text element started with a space, add it back in
if(rs.length > 0
&& rs[0].type === "text"
&& (accumulatedText[0] === " " || accumulatedText[0] === "\n")
) {
rs[0].text = " " + rs[0].text;
}
out = out.concat(rs);
}
accumulatedText = '';
}
}
return out;
}
var MarkdownParser = function(type, text, options) {
var tree = md.parse(text, {});
//console.debug(tree);
tree = convertNodes(tree);
//console.debug(tree);
this.tree = tree;
};
exports["text/x-markdown"] = MarkdownParser;
exports["text/markdown"] = MarkdownParser;
})();