You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-06-21 23:17:42 +02:00
Desktop: Added support for checkboxes and fixed various issues with WYSIWYG editor
This commit is contained in:
@ -40,6 +40,7 @@ const BaseService = require('lib/services/BaseService');
|
||||
const SearchEngine = require('lib/services/SearchEngine');
|
||||
const KvStore = require('lib/services/KvStore');
|
||||
const MigrationService = require('lib/services/MigrationService');
|
||||
const { toSystemSlashes } = require('lib/path-utils.js');
|
||||
|
||||
class BaseApplication {
|
||||
constructor() {
|
||||
@ -580,7 +581,7 @@ class BaseApplication {
|
||||
|
||||
if (process && process.env && process.env.PORTABLE_EXECUTABLE_DIR) return `${process.env.PORTABLE_EXECUTABLE_DIR}/JoplinProfile`;
|
||||
|
||||
return `${os.homedir()}/.config/${Setting.value('appName')}`;
|
||||
return toSystemSlashes(`${os.homedir()}/.config/${Setting.value('appName')}`, 'linux');
|
||||
}
|
||||
|
||||
async start(argv) {
|
||||
|
@ -9,6 +9,9 @@ class HtmlToMd {
|
||||
anchorNames: options.anchorNames ? options.anchorNames.map(n => n.trim().toLowerCase()) : [],
|
||||
codeBlockStyle: 'fenced',
|
||||
preserveImageTagsWithSize: !!options.preserveImageTagsWithSize,
|
||||
bulletListMarker: '-',
|
||||
emDelimiter: '*',
|
||||
strongDelimiter: '**',
|
||||
});
|
||||
turndown.use(turndownPluginGfm);
|
||||
turndown.remove('script');
|
||||
|
@ -55,14 +55,21 @@ class NoteBodyViewer extends Component {
|
||||
this.forceUpdate();
|
||||
}, 100);
|
||||
},
|
||||
paddingBottom: '3.8em', // Extra bottom padding to make it possible to scroll past the action button (so that it doesn't overlap the text)
|
||||
highlightedKeywords: this.props.highlightedKeywords,
|
||||
resources: this.props.noteResources, // await shared.attachedResources(bodyToRender),
|
||||
codeTheme: theme.codeThemeCss,
|
||||
postMessageSyntax: 'window.ReactNativeWebView.postMessage',
|
||||
};
|
||||
|
||||
const result = await this.markupToHtml_.render(note.markup_language, bodyToRender, this.props.webViewStyle, mdOptions);
|
||||
const result = await this.markupToHtml_.render(
|
||||
note.markup_language,
|
||||
bodyToRender,
|
||||
{
|
||||
bodyPaddingBottom: '3.8em', // Extra bottom padding to make it possible to scroll past the action button (so that it doesn't overlap the text)
|
||||
...this.props.webViewStyle,
|
||||
},
|
||||
mdOptions
|
||||
);
|
||||
let html = result.html;
|
||||
|
||||
const resourceDownloadMode = Setting.value('sync.resourceDownloadMode');
|
||||
|
@ -73,7 +73,7 @@ class FsDriverBase {
|
||||
// TODO: move out of here and make it part of joplin-renderer
|
||||
// or assign to option using .bind(fsDriver())
|
||||
async cacheCssToFile(cssStrings) {
|
||||
const cssString = cssStrings.join('\n');
|
||||
const cssString = Array.isArray(cssStrings) ? cssStrings.join('\n') : cssStrings;
|
||||
const cssFilePath = `${Setting.value('tempDir')}/${md5(escape(cssString))}.css`;
|
||||
if (!(await this.exists(cssFilePath))) {
|
||||
await this.writeFile(cssFilePath, cssString, 'utf8');
|
||||
|
@ -40,6 +40,10 @@ class HtmlToHtml {
|
||||
};
|
||||
}
|
||||
|
||||
async allAssets(/* theme*/) {
|
||||
return []; // TODO
|
||||
}
|
||||
|
||||
async render(markup, theme, options) {
|
||||
options = Object.assign({}, {
|
||||
splitted: false,
|
||||
@ -80,7 +84,7 @@ class HtmlToHtml {
|
||||
};
|
||||
}
|
||||
|
||||
let cssStrings = noteStyle(theme, options);
|
||||
let cssStrings = noteStyle(theme);
|
||||
|
||||
if (options.splitted) {
|
||||
const splitted = this.splitHtml(html);
|
||||
|
@ -36,6 +36,10 @@ class MarkupToHtml {
|
||||
async render(markupLanguage, markup, theme, options) {
|
||||
return this.renderer(markupLanguage).render(markup, theme, options);
|
||||
}
|
||||
|
||||
async allAssets(markupLanguage, theme) {
|
||||
return this.renderer(markupLanguage).allAssets(theme);
|
||||
}
|
||||
}
|
||||
|
||||
MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN = 1;
|
||||
|
@ -3,19 +3,22 @@ const md5 = require('md5');
|
||||
const noteStyle = require('./noteStyle');
|
||||
const { fileExtension } = require('./pathUtils');
|
||||
const memoryCache = require('memory-cache');
|
||||
|
||||
// /!\/!\ Note: the order of rules is important!! /!\/!\
|
||||
const rules = {
|
||||
fence: require('./MdToHtml/rules/fence').default,
|
||||
sanitize_html: require('./MdToHtml/rules/sanitize_html').default,
|
||||
image: require('./MdToHtml/rules/image'),
|
||||
checkbox: require('./MdToHtml/rules/checkbox'),
|
||||
checkbox: require('./MdToHtml/rules/checkbox').default,
|
||||
katex: require('./MdToHtml/rules/katex'),
|
||||
link_open: require('./MdToHtml/rules/link_open'),
|
||||
html_image: require('./MdToHtml/rules/html_image'),
|
||||
highlight_keywords: require('./MdToHtml/rules/highlight_keywords'),
|
||||
code_inline: require('./MdToHtml/rules/code_inline'),
|
||||
fence: require('./MdToHtml/rules/fence').default,
|
||||
fountain: require('./MdToHtml/rules/fountain'),
|
||||
mermaid: require('./MdToHtml/rules/mermaid').default,
|
||||
sanitize_html: require('./MdToHtml/rules/sanitize_html').default,
|
||||
};
|
||||
|
||||
const setupLinkify = require('./MdToHtml/setupLinkify');
|
||||
const hljs = require('highlight.js');
|
||||
const uslug = require('uslug');
|
||||
@ -77,6 +80,13 @@ class MdToHtml {
|
||||
return this.tempDir_;
|
||||
}
|
||||
|
||||
static pluginNames() {
|
||||
const output = [];
|
||||
for (const n in rules) output.push(n);
|
||||
for (const n in plugins) output.push(n);
|
||||
return output;
|
||||
}
|
||||
|
||||
pluginOptions(name) {
|
||||
let o = this.pluginOptions_[name] ? this.pluginOptions_[name] : {};
|
||||
o = Object.assign({
|
||||
@ -115,8 +125,10 @@ class MdToHtml {
|
||||
throw new Error(`Unsupported inline mime type: ${mime}`);
|
||||
}
|
||||
} else {
|
||||
const name = `${pluginName}/${asset.name}`;
|
||||
files.push(Object.assign({}, asset, {
|
||||
name: `${pluginName}/${asset.name}`,
|
||||
name: name,
|
||||
path: `pluginAssets/${name}`,
|
||||
mime: mime,
|
||||
}));
|
||||
}
|
||||
@ -124,21 +136,52 @@ class MdToHtml {
|
||||
}
|
||||
|
||||
return {
|
||||
files: files,
|
||||
pluginAssets: files,
|
||||
cssStrings: cssStrings,
|
||||
};
|
||||
}
|
||||
|
||||
async render(body, style = null, options = null) {
|
||||
async allAssets(theme) {
|
||||
const assets = {};
|
||||
for (const key in rules) {
|
||||
if (!this.pluginEnabled(key)) continue;
|
||||
const rule = rules[key];
|
||||
|
||||
if (rule.style) {
|
||||
assets[key] = rule.style(theme);
|
||||
}
|
||||
}
|
||||
|
||||
const processedAssets = this.processPluginAssets(assets);
|
||||
processedAssets.cssStrings.splice(0, 0, noteStyle(theme));
|
||||
const output = await this.outputAssetsToExternalAssets_(processedAssets);
|
||||
return output.pluginAssets;
|
||||
}
|
||||
|
||||
async outputAssetsToExternalAssets_(output) {
|
||||
for (const cssString of output.cssStrings) {
|
||||
output.pluginAssets.push(await this.fsDriver().cacheCssToFile(cssString));
|
||||
}
|
||||
delete output.cssStrings;
|
||||
return output;
|
||||
}
|
||||
|
||||
// "style" here is really the theme, as returned by themeStyle()
|
||||
async render(body, theme = null, options = null) {
|
||||
options = Object.assign({}, {
|
||||
// In bodyOnly mode, the rendered Markdown is returned without the wrapper DIV
|
||||
bodyOnly: false,
|
||||
// In splitted mode, the CSS and HTML will be returned in separate properties.
|
||||
// In non-splitted mode, CSS and HTML will be merged in the same document.
|
||||
splitted: false,
|
||||
// When this is true, all assets such as CSS or JS are returned as external
|
||||
// files. Otherwise some of them might be in the cssStrings property.
|
||||
externalAssetsOnly: false,
|
||||
postMessageSyntax: 'postMessage',
|
||||
paddingBottom: '0',
|
||||
highlightedKeywords: [],
|
||||
codeTheme: 'atom-one-light.css',
|
||||
style: Object.assign({}, defaultNoteStyle),
|
||||
theme: Object.assign({}, defaultNoteStyle, theme),
|
||||
plugins: {},
|
||||
}, options);
|
||||
|
||||
// The "codeHighlightCacheKey" option indicates what set of cached object should be
|
||||
@ -150,7 +193,7 @@ class MdToHtml {
|
||||
this.lastCodeHighlightCacheKey_ = options.codeHighlightCacheKey;
|
||||
}
|
||||
|
||||
const cacheKey = md5(escape(body + JSON.stringify(options) + JSON.stringify(style)));
|
||||
const cacheKey = md5(escape(body + JSON.stringify(options) + JSON.stringify(options.theme)));
|
||||
const cachedOutput = this.cachedOutputs_[cacheKey];
|
||||
if (cachedOutput) return cachedOutput;
|
||||
|
||||
@ -237,19 +280,13 @@ class MdToHtml {
|
||||
// Using the `context` object, a plugin can define what additional assets they need (css, fonts, etc.) using context.pluginAssets.
|
||||
// The calling application will need to handle loading these assets.
|
||||
|
||||
// /!\/!\ Note: the order of rules is important!! /!\/!\
|
||||
for (const key in rules) {
|
||||
if (!this.pluginEnabled(key)) continue;
|
||||
const rule = rules[key];
|
||||
const ruleInstall = rule.install ? rule.install : rule;
|
||||
markdownIt.use(ruleInstall(context, { ...ruleOptions }));
|
||||
}
|
||||
|
||||
markdownIt.use(rules.fence(context, ruleOptions));
|
||||
markdownIt.use(rules.sanitize_html(context, ruleOptions));
|
||||
markdownIt.use(rules.image(context, ruleOptions));
|
||||
markdownIt.use(rules.checkbox(context, ruleOptions));
|
||||
markdownIt.use(rules.link_open(context, ruleOptions));
|
||||
markdownIt.use(rules.html_image(context, ruleOptions));
|
||||
if (this.pluginEnabled('katex')) markdownIt.use(rules.katex(context, ruleOptions));
|
||||
if (this.pluginEnabled('fountain')) markdownIt.use(rules.fountain(context, ruleOptions));
|
||||
if (this.pluginEnabled('mermaid')) markdownIt.use(rules.mermaid(context, ruleOptions));
|
||||
markdownIt.use(rules.highlight_keywords(context, ruleOptions));
|
||||
markdownIt.use(rules.code_inline(context, ruleOptions));
|
||||
markdownIt.use(markdownItAnchor, { slugify: uslugify });
|
||||
|
||||
for (const key in plugins) {
|
||||
@ -260,41 +297,28 @@ class MdToHtml {
|
||||
|
||||
const renderedBody = markdownIt.render(body);
|
||||
|
||||
let cssStrings = noteStyle(style, options);
|
||||
let cssStrings = noteStyle(options.theme);
|
||||
|
||||
const pluginAssets = this.processPluginAssets(context.pluginAssets);
|
||||
cssStrings = cssStrings.concat(pluginAssets.cssStrings);
|
||||
|
||||
const output = {
|
||||
pluginAssets: pluginAssets.files.map(f => {
|
||||
return Object.assign({}, f, {
|
||||
path: `pluginAssets/${f.name}`,
|
||||
});
|
||||
}),
|
||||
};
|
||||
|
||||
if (options.bodyOnly) {
|
||||
output.html = renderedBody;
|
||||
return output;
|
||||
}
|
||||
let output = this.processPluginAssets(context.pluginAssets);
|
||||
cssStrings = cssStrings.concat(output.cssStrings);
|
||||
|
||||
if (options.userCss) cssStrings.push(options.userCss);
|
||||
|
||||
const styleHtml = `<style>${cssStrings.join('\n')}</style>`;
|
||||
|
||||
const html = `${styleHtml}<div id="rendered-md">${renderedBody}</div>`;
|
||||
|
||||
output.html = html;
|
||||
|
||||
if (options.splitted) {
|
||||
if (options.bodyOnly) {
|
||||
output.html = renderedBody;
|
||||
output.cssStrings = cssStrings;
|
||||
output.html = `<div id="rendered-md">${renderedBody}</div>`;
|
||||
} else {
|
||||
const styleHtml = `<style>${cssStrings.join('\n')}</style>`;
|
||||
output.html = `${styleHtml}<div id="rendered-md">${renderedBody}</div>`;
|
||||
|
||||
if (options.externalAssetsOnly) {
|
||||
output.pluginAssets.push(await this.fsDriver().cacheCssToFile(cssStrings));
|
||||
if (options.splitted) {
|
||||
output.cssStrings = cssStrings;
|
||||
output.html = `<div id="rendered-md">${renderedBody}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.externalAssetsOnly) output = await this.outputAssetsToExternalAssets_(output);
|
||||
|
||||
// Fow now, we keep only the last entry in the cache
|
||||
this.cachedOutputs_ = {};
|
||||
this.cachedOutputs_[cacheKey] = output;
|
||||
|
@ -1,153 +0,0 @@
|
||||
let checkboxIndex_ = -1;
|
||||
|
||||
const checkboxStyle = `
|
||||
/* Remove the indentation from the checkboxes at the root of the document
|
||||
(otherwise they are too far right), but keep it for their children to allow
|
||||
nested lists. Make sure this value matches the UL margin. */
|
||||
|
||||
.md-checkbox .checkbox-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
li.md-checkbox {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
li.md-checkbox input[type=checkbox] {
|
||||
margin-left: -1.71em;
|
||||
margin-right: 0.7em;
|
||||
}
|
||||
`;
|
||||
|
||||
function createPrefixTokens(Token, id, checked, label, postMessageSyntax, sourceToken) {
|
||||
let token = null;
|
||||
const tokens = [];
|
||||
|
||||
// A bit hard to handle errors here and it's unlikely that the token won't have a valid
|
||||
// map parameter, but if it does set it to a very high value, which will be more easy to notice
|
||||
// in calling code.
|
||||
const lineIndex = sourceToken.map && sourceToken.map.length ? sourceToken.map[0] : 99999999;
|
||||
const checkedString = checked ? 'checked' : 'unchecked';
|
||||
|
||||
const labelId = `cb-label-${id}`;
|
||||
|
||||
const js = `
|
||||
try {
|
||||
if (this.checked) {
|
||||
this.setAttribute('checked', 'checked');
|
||||
} else {
|
||||
this.removeAttribute('checked');
|
||||
}
|
||||
|
||||
${postMessageSyntax}('checkboxclick:${checkedString}:${lineIndex}');
|
||||
const label = document.getElementById("${labelId}");
|
||||
label.classList.remove(this.checked ? 'checkbox-label-unchecked' : 'checkbox-label-checked');
|
||||
label.classList.add(this.checked ? 'checkbox-label-checked' : 'checkbox-label-unchecked');
|
||||
} catch (error) {
|
||||
console.warn('Checkbox ${checkedString}:${lineIndex} error', error);
|
||||
}
|
||||
return true;
|
||||
`;
|
||||
|
||||
token = new Token('checkbox_wrapper_open', 'div', 1);
|
||||
token.attrs = [['class', 'checkbox-wrapper']];
|
||||
tokens.push(token);
|
||||
|
||||
token = new Token('checkbox_input', 'input', 0);
|
||||
token.attrs = [['type', 'checkbox'], ['id', id], ['onclick', js]];
|
||||
if (checked) token.attrs.push(['checked', 'checked']);
|
||||
tokens.push(token);
|
||||
|
||||
token = new Token('label_open', 'label', 1);
|
||||
token.attrs = [['id', labelId], ['for', id], ['class', `checkbox-label-${checkedString}`]];
|
||||
tokens.push(token);
|
||||
|
||||
if (label) {
|
||||
token = new Token('text', '', 0);
|
||||
token.content = label;
|
||||
tokens.push(token);
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
function createSuffixTokens(Token) {
|
||||
return [
|
||||
new Token('label_close', 'label', -1),
|
||||
new Token('checkbox_wrapper_close', 'div', -1),
|
||||
];
|
||||
}
|
||||
|
||||
function installRule(markdownIt, mdOptions, ruleOptions, context) {
|
||||
markdownIt.core.ruler.push('checkbox', state => {
|
||||
const tokens = state.tokens;
|
||||
const Token = state.Token;
|
||||
|
||||
const checkboxPattern = /^\[([x|X| ])\] (.*)$/;
|
||||
let currentListItem = null;
|
||||
let processedFirstInline = false;
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
const token = tokens[i];
|
||||
|
||||
if (token.type === 'list_item_open') {
|
||||
currentListItem = token;
|
||||
processedFirstInline = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token.type === 'list_item_close') {
|
||||
currentListItem = null;
|
||||
processedFirstInline = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Note that we only support list items that start with "-" (not with "*")
|
||||
if (currentListItem && currentListItem.markup === '-' && !processedFirstInline && token.type === 'inline') {
|
||||
processedFirstInline = true;
|
||||
const firstChild = token.children && token.children.length ? token.children[0] : null;
|
||||
if (!firstChild) continue;
|
||||
|
||||
const matches = checkboxPattern.exec(firstChild.content);
|
||||
if (!matches || matches.length < 2) continue;
|
||||
|
||||
checkboxIndex_++;
|
||||
const checked = matches[1] !== ' ';
|
||||
const id = `md-checkbox-${checkboxIndex_}`;
|
||||
const label = matches.length >= 3 ? matches[2] : '';
|
||||
|
||||
// Prepend the text content with the checkbox markup and the opening <label> tag
|
||||
// then append the </label> tag at the end of the text content.
|
||||
|
||||
const prefix = createPrefixTokens(Token, id, checked, label, ruleOptions.postMessageSyntax, token);
|
||||
const suffix = createSuffixTokens(Token);
|
||||
|
||||
token.children = markdownIt.utils.arrayReplaceAt(token.children, 0, prefix);
|
||||
token.children = token.children.concat(suffix);
|
||||
|
||||
// Add a class to the <li> container so that it can be targetted with CSS.
|
||||
|
||||
let itemClass = currentListItem.attrGet('class');
|
||||
if (!itemClass) itemClass = '';
|
||||
itemClass += ' md-checkbox joplin-checkbox';
|
||||
currentListItem.attrSet('class', itemClass.trim());
|
||||
|
||||
if (!('checkbox' in context.pluginAssets)) {
|
||||
context.pluginAssets['checkbox'] = [
|
||||
{
|
||||
inline: true,
|
||||
text: checkboxStyle,
|
||||
mime: 'text/css',
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = function(context, ruleOptions) {
|
||||
return function(md, mdOptions) {
|
||||
installRule(md, mdOptions, ruleOptions, context);
|
||||
};
|
||||
};
|
228
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/checkbox.ts
Normal file
228
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/checkbox.ts
Normal file
@ -0,0 +1,228 @@
|
||||
let checkboxIndex_ = -1;
|
||||
|
||||
const pluginAssets:Function[] = [];
|
||||
|
||||
pluginAssets[1] = function() {
|
||||
return [
|
||||
{
|
||||
inline: true,
|
||||
mime: 'text/css',
|
||||
text: `
|
||||
/* Remove the indentation from the checkboxes at the root of the document
|
||||
(otherwise they are too far right), but keep it for their children to allow
|
||||
nested lists. Make sure this value matches the UL margin. */
|
||||
|
||||
.md-checkbox .checkbox-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
li.md-checkbox {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
li.md-checkbox input[type=checkbox] {
|
||||
margin-left: -1.71em;
|
||||
margin-right: 0.7em;
|
||||
}`,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
pluginAssets[2] = function(theme:any) {
|
||||
return [
|
||||
{
|
||||
inline: true,
|
||||
mime: 'text/css',
|
||||
text: `
|
||||
/* https://stackoverflow.com/questions/7478336/only-detect-click-event-on-pseudo-element#comment39751366_7478344 */
|
||||
|
||||
ul.joplin-checklist li {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
ul.joplin-checklist {
|
||||
list-style:none;
|
||||
}
|
||||
|
||||
ul.joplin-checklist li::before {
|
||||
content:"\\f14a";
|
||||
font-family:ForkAwesome;
|
||||
background-size: 16px 16px;
|
||||
pointer-events: all;
|
||||
cursor: pointer;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
margin-left: -1.3em;
|
||||
position: absolute;
|
||||
color: ${theme.htmlColor};
|
||||
}
|
||||
|
||||
.joplin-checklist li:not(.checked)::before {
|
||||
content:"\\f0c8";
|
||||
}`,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
function createPrefixTokens(Token:any, id:string, checked:boolean, label:string, postMessageSyntax:string, sourceToken:any):any[] {
|
||||
let token = null;
|
||||
const tokens = [];
|
||||
|
||||
// A bit hard to handle errors here and it's unlikely that the token won't have a valid
|
||||
// map parameter, but if it does set it to a very high value, which will be more easy to notice
|
||||
// in calling code.
|
||||
const lineIndex = sourceToken.map && sourceToken.map.length ? sourceToken.map[0] : 99999999;
|
||||
const checkedString = checked ? 'checked' : 'unchecked';
|
||||
|
||||
const labelId = `cb-label-${id}`;
|
||||
|
||||
const js = `
|
||||
try {
|
||||
if (this.checked) {
|
||||
this.setAttribute('checked', 'checked');
|
||||
} else {
|
||||
this.removeAttribute('checked');
|
||||
}
|
||||
|
||||
${postMessageSyntax}('checkboxclick:${checkedString}:${lineIndex}');
|
||||
const label = document.getElementById("${labelId}");
|
||||
label.classList.remove(this.checked ? 'checkbox-label-unchecked' : 'checkbox-label-checked');
|
||||
label.classList.add(this.checked ? 'checkbox-label-checked' : 'checkbox-label-unchecked');
|
||||
} catch (error) {
|
||||
console.warn('Checkbox ${checkedString}:${lineIndex} error', error);
|
||||
}
|
||||
return true;
|
||||
`;
|
||||
|
||||
token = new Token('checkbox_wrapper_open', 'div', 1);
|
||||
token.attrs = [['class', 'checkbox-wrapper']];
|
||||
tokens.push(token);
|
||||
|
||||
token = new Token('checkbox_input', 'input', 0);
|
||||
token.attrs = [['type', 'checkbox'], ['id', id], ['onclick', js]];
|
||||
if (checked) token.attrs.push(['checked', 'checked']);
|
||||
tokens.push(token);
|
||||
|
||||
token = new Token('label_open', 'label', 1);
|
||||
token.attrs = [['id', labelId], ['for', id], ['class', `checkbox-label-${checkedString}`]];
|
||||
tokens.push(token);
|
||||
|
||||
if (label) {
|
||||
token = new Token('text', '', 0);
|
||||
token.content = label;
|
||||
tokens.push(token);
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
function createSuffixTokens(Token:any):any[] {
|
||||
return [
|
||||
new Token('label_close', 'label', -1),
|
||||
new Token('checkbox_wrapper_close', 'div', -1),
|
||||
];
|
||||
}
|
||||
|
||||
// @ts-ignore: Keep the function signature as-is despite unusued arguments
|
||||
function installRule(markdownIt:any, mdOptions:any, ruleOptions:any, context:any) {
|
||||
const pluginOptions = { renderingType: 1, ...ruleOptions.plugins['checkbox'] };
|
||||
|
||||
markdownIt.core.ruler.push('checkbox', (state:any) => {
|
||||
const tokens = state.tokens;
|
||||
const Token = state.Token;
|
||||
|
||||
const checkboxPattern = /^\[([x|X| ])\] (.*)$/;
|
||||
let currentListItem = null;
|
||||
let processedFirstInline = false;
|
||||
const lists = [];
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
const token = tokens[i];
|
||||
|
||||
if (token.type === 'bullet_list_open') {
|
||||
lists.push(token);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token.type === 'bullet_list_close') {
|
||||
lists.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token.type === 'list_item_open') {
|
||||
currentListItem = token;
|
||||
processedFirstInline = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token.type === 'list_item_close') {
|
||||
currentListItem = null;
|
||||
processedFirstInline = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Note that we only support list items that start with "-" (not with "*")
|
||||
if (currentListItem && currentListItem.markup === '-' && !processedFirstInline && token.type === 'inline') {
|
||||
processedFirstInline = true;
|
||||
const firstChild = token.children && token.children.length ? token.children[0] : null;
|
||||
if (!firstChild) continue;
|
||||
|
||||
const matches = checkboxPattern.exec(firstChild.content);
|
||||
if (!matches || matches.length < 2) continue;
|
||||
|
||||
const checked = matches[1] !== ' ';
|
||||
const label = matches.length >= 3 ? matches[2] : '';
|
||||
|
||||
const currentList = lists[lists.length - 1];
|
||||
|
||||
if (pluginOptions.renderingType === 1) {
|
||||
checkboxIndex_++;
|
||||
const id = `md-checkbox-${checkboxIndex_}`;
|
||||
|
||||
// Prepend the text content with the checkbox markup and the opening <label> tag
|
||||
// then append the </label> tag at the end of the text content.
|
||||
|
||||
const prefix = createPrefixTokens(Token, id, checked, label, ruleOptions.postMessageSyntax, token);
|
||||
const suffix = createSuffixTokens(Token);
|
||||
|
||||
token.children = markdownIt.utils.arrayReplaceAt(token.children, 0, prefix);
|
||||
token.children = token.children.concat(suffix);
|
||||
|
||||
// Add a class to the <li> container so that it can be targetted with CSS.
|
||||
|
||||
let itemClass = currentListItem.attrGet('class');
|
||||
if (!itemClass) itemClass = '';
|
||||
itemClass += ' md-checkbox joplin-checkbox';
|
||||
currentListItem.attrSet('class', itemClass.trim());
|
||||
} else {
|
||||
const textToken = new Token('text', '', 0);
|
||||
textToken.content = label;
|
||||
const tokens = [];
|
||||
tokens.push(textToken);
|
||||
|
||||
token.children = markdownIt.utils.arrayReplaceAt(token.children, 0, tokens);
|
||||
|
||||
const listClass = currentList.attrGet('class') || '';
|
||||
if (listClass.indexOf('joplin-') < 0) currentList.attrSet('class', (`${listClass} joplin-checklist`).trim());
|
||||
|
||||
if (checked) {
|
||||
currentListItem.attrSet('class', (`${currentListItem.attrGet('class') || ''} checked`).trim());
|
||||
}
|
||||
}
|
||||
|
||||
if (!('checkbox' in context.pluginAssets)) {
|
||||
context.pluginAssets['checkbox'] = pluginAssets[pluginOptions.renderingType](ruleOptions.theme);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
install: function(context:any, ruleOptions:any) {
|
||||
return function(md:any, mdOptions:any) {
|
||||
installRule(md, mdOptions, ruleOptions, context);
|
||||
};
|
||||
},
|
||||
style: pluginAssets[2],
|
||||
};
|
@ -1,99 +1,106 @@
|
||||
const fountain = require('../../vendor/fountain.min.js');
|
||||
|
||||
const fountainCss = `
|
||||
.fountain {
|
||||
font-family: monospace;
|
||||
line-height: 107%;
|
||||
max-width: 1000px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
const fountainCss = function() {
|
||||
return [
|
||||
{
|
||||
inline: true,
|
||||
mime: 'text/css',
|
||||
text: `
|
||||
.fountain {
|
||||
font-family: monospace;
|
||||
line-height: 107%;
|
||||
max-width: 1000px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.fountain .title-page,
|
||||
.fountain .page {
|
||||
box-shadow: 0 0 5px rgba(0,0,0,0.1);
|
||||
border: 1px solid #d2d2d2;
|
||||
padding: 10%;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
.fountain .title-page,
|
||||
.fountain .page {
|
||||
box-shadow: 0 0 5px rgba(0,0,0,0.1);
|
||||
border: 1px solid #d2d2d2;
|
||||
padding: 10%;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.fountain h1,
|
||||
.fountain h2,
|
||||
.fountain h3,
|
||||
.fountain h4,
|
||||
.fountain p {
|
||||
font-weight: normal;
|
||||
line-height: 107%;
|
||||
margin: 1em 0;
|
||||
border: none;
|
||||
font-size: 1em;
|
||||
}
|
||||
.fountain h1,
|
||||
.fountain h2,
|
||||
.fountain h3,
|
||||
.fountain h4,
|
||||
.fountain p {
|
||||
font-weight: normal;
|
||||
line-height: 107%;
|
||||
margin: 1em 0;
|
||||
border: none;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.fountain .bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.fountain .bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.fountain .underline {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.fountain .underline {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.fountain .centered {
|
||||
text-align: center;
|
||||
}
|
||||
.fountain .centered {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fountain h2 {
|
||||
text-align: right;
|
||||
}
|
||||
.fountain h2 {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.fountain .dialogue p.parenthetical {
|
||||
margin-left: 11%;
|
||||
}
|
||||
.fountain .dialogue p.parenthetical {
|
||||
margin-left: 11%;
|
||||
}
|
||||
|
||||
.fountain .title-page .credit,
|
||||
.fountain .title-page .authors,
|
||||
.fountain .title-page .source {
|
||||
text-align: center;
|
||||
}
|
||||
.fountain .title-page .credit,
|
||||
.fountain .title-page .authors,
|
||||
.fountain .title-page .source {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fountain .title-page h1 {
|
||||
margin-bottom: 1.5em;
|
||||
text-align: center;
|
||||
}
|
||||
.fountain .title-page h1 {
|
||||
margin-bottom: 1.5em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fountain .title-page .source {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
.fountain .title-page .source {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
|
||||
.fountain .title-page .notes {
|
||||
text-align: right;
|
||||
margin: 3em 0;
|
||||
}
|
||||
.fountain .title-page .notes {
|
||||
text-align: right;
|
||||
margin: 3em 0;
|
||||
}
|
||||
|
||||
.fountain .title-page h1 {
|
||||
margin-bottom: 1.5em;
|
||||
text-align: center;
|
||||
}
|
||||
.fountain .title-page h1 {
|
||||
margin-bottom: 1.5em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fountain .dialogue {
|
||||
margin-left: 3em;
|
||||
margin-right: 3em;
|
||||
}
|
||||
.fountain .dialogue {
|
||||
margin-left: 3em;
|
||||
margin-right: 3em;
|
||||
}
|
||||
|
||||
.fountain .dialogue p,
|
||||
.fountain .dialogue h1,
|
||||
.fountain .dialogue h2,
|
||||
.fountain .dialogue h3,
|
||||
.fountain .dialogue h4 {
|
||||
margin: 0;
|
||||
}
|
||||
.fountain .dialogue p,
|
||||
.fountain .dialogue h1,
|
||||
.fountain .dialogue h2,
|
||||
.fountain .dialogue h3,
|
||||
.fountain .dialogue h4 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.fountain .dialogue h1,
|
||||
.fountain .dialogue h2,
|
||||
.fountain .dialogue h3,
|
||||
.fountain .dialogue h4 {
|
||||
text-align: center;
|
||||
}
|
||||
`;
|
||||
.fountain .dialogue h1,
|
||||
.fountain .dialogue h2,
|
||||
.fountain .dialogue h3,
|
||||
.fountain .dialogue h4 {
|
||||
text-align: center;
|
||||
}`,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
function renderFountainScript(markdownIt, content) {
|
||||
const result = fountain.parse(content);
|
||||
@ -114,13 +121,7 @@ function renderFountainScript(markdownIt, content) {
|
||||
function addContextAssets(context) {
|
||||
if ('fountain' in context.pluginAssets) return;
|
||||
|
||||
context.pluginAssets['fountain'] = [
|
||||
{
|
||||
inline: true,
|
||||
text: fountainCss,
|
||||
mime: 'text/css',
|
||||
},
|
||||
];
|
||||
context.pluginAssets['fountain'] = fountainCss();
|
||||
}
|
||||
|
||||
function installRule(markdownIt, mdOptions, ruleOptions, context) {
|
||||
@ -136,8 +137,11 @@ function installRule(markdownIt, mdOptions, ruleOptions, context) {
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = function(context, ruleOptions) {
|
||||
return function(md, mdOptions) {
|
||||
installRule(md, mdOptions, ruleOptions, context);
|
||||
};
|
||||
module.exports = {
|
||||
install: function(context, ruleOptions) {
|
||||
return function(md, mdOptions) {
|
||||
installRule(md, mdOptions, ruleOptions, context);
|
||||
};
|
||||
},
|
||||
style: fountainCss,
|
||||
};
|
||||
|
@ -14,6 +14,14 @@ const stringifySafe = require('json-stringify-safe');
|
||||
|
||||
katex = mhchemModule(katex);
|
||||
|
||||
function katexStyle() {
|
||||
return [
|
||||
{ name: 'katex.css' },
|
||||
// Note: Katex also requires a number of fonts but they don't need to be specified here
|
||||
// since they will be loaded as needed from the CSS.
|
||||
];
|
||||
}
|
||||
|
||||
// Test if potential opening or closing delimieter
|
||||
// Assumes that there is a "$" at state.src[pos]
|
||||
function isValidDelim(state, pos) {
|
||||
@ -184,97 +192,78 @@ function math_block(state, start, end, silent) {
|
||||
|
||||
const cache_ = {};
|
||||
|
||||
module.exports = function(context) {
|
||||
// Keep macros that persist across Katex blocks to allow defining a macro
|
||||
// in one block and re-using it later in other blocks.
|
||||
// https://github.com/laurent22/joplin/issues/1105
|
||||
context.__katex = { macros: {} };
|
||||
module.exports = {
|
||||
install: function(context) {
|
||||
// Keep macros that persist across Katex blocks to allow defining a macro
|
||||
// in one block and re-using it later in other blocks.
|
||||
// https://github.com/laurent22/joplin/issues/1105
|
||||
context.__katex = { macros: {} };
|
||||
|
||||
const addContextAssets = () => {
|
||||
context.pluginAssets['katex'] = [
|
||||
{ name: 'katex.css' },
|
||||
{ name: 'fonts/KaTeX_Main-Regular.woff2' },
|
||||
{ name: 'fonts/KaTeX_Main-Bold.woff2' },
|
||||
{ name: 'fonts/KaTeX_Main-BoldItalic.woff2' },
|
||||
{ name: 'fonts/KaTeX_Main-Italic.woff2' },
|
||||
{ name: 'fonts/KaTeX_Math-Italic.woff2' },
|
||||
{ name: 'fonts/KaTeX_Math-BoldItalic.woff2' },
|
||||
{ name: 'fonts/KaTeX_Size1-Regular.woff2' },
|
||||
{ name: 'fonts/KaTeX_Size2-Regular.woff2' },
|
||||
{ name: 'fonts/KaTeX_Size3-Regular.woff2' },
|
||||
{ name: 'fonts/KaTeX_Size4-Regular.woff2' },
|
||||
{ name: 'fonts/KaTeX_AMS-Regular.woff2' },
|
||||
{ name: 'fonts/KaTeX_Caligraphic-Bold.woff2' },
|
||||
{ name: 'fonts/KaTeX_Caligraphic-Regular.woff2' },
|
||||
{ name: 'fonts/KaTeX_Fraktur-Bold.woff2' },
|
||||
{ name: 'fonts/KaTeX_Fraktur-Regular.woff2' },
|
||||
{ name: 'fonts/KaTeX_SansSerif-Bold.woff2' },
|
||||
{ name: 'fonts/KaTeX_SansSerif-Italic.woff2' },
|
||||
{ name: 'fonts/KaTeX_SansSerif-Regular.woff2' },
|
||||
{ name: 'fonts/KaTeX_Script-Regular.woff2' },
|
||||
{ name: 'fonts/KaTeX_Typewriter-Regular.woff2' },
|
||||
];
|
||||
};
|
||||
const addContextAssets = () => {
|
||||
context.pluginAssets['katex'] = katexStyle();
|
||||
};
|
||||
|
||||
function renderToStringWithCache(latex, options) {
|
||||
const cacheKey = md5(escape(latex) + escape(stringifySafe(options)));
|
||||
if (cacheKey in cache_) {
|
||||
return cache_[cacheKey];
|
||||
} else {
|
||||
const beforeMacros = stringifySafe(options.macros);
|
||||
const output = katex.renderToString(latex, options);
|
||||
const afterMacros = stringifySafe(options.macros);
|
||||
function renderToStringWithCache(latex, options) {
|
||||
const cacheKey = md5(escape(latex) + escape(stringifySafe(options)));
|
||||
if (cacheKey in cache_) {
|
||||
return cache_[cacheKey];
|
||||
} else {
|
||||
const beforeMacros = stringifySafe(options.macros);
|
||||
const output = katex.renderToString(latex, options);
|
||||
const afterMacros = stringifySafe(options.macros);
|
||||
|
||||
// Don't cache the formulas that add macros, otherwise
|
||||
// they won't be added on second run.
|
||||
if (beforeMacros === afterMacros) cache_[cacheKey] = output;
|
||||
return output;
|
||||
// Don't cache the formulas that add macros, otherwise
|
||||
// they won't be added on second run.
|
||||
if (beforeMacros === afterMacros) cache_[cacheKey] = output;
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return function(md, options) {
|
||||
// Default options
|
||||
return function(md, options) {
|
||||
// Default options
|
||||
|
||||
options = options || {};
|
||||
options.macros = context.__katex.macros;
|
||||
options.trust = true;
|
||||
options = options || {};
|
||||
options.macros = context.__katex.macros;
|
||||
options.trust = true;
|
||||
|
||||
// set KaTeX as the renderer for markdown-it-simplemath
|
||||
const katexInline = function(latex) {
|
||||
options.displayMode = false;
|
||||
try {
|
||||
return `<span class="joplin-editable"><pre class="joplin-source" data-joplin-source-open="$" data-joplin-source-close="$">${latex}</pre>${renderToStringWithCache(latex, options)}</span>`;
|
||||
} catch (error) {
|
||||
console.error('Katex error for:', latex, error);
|
||||
return latex;
|
||||
}
|
||||
// set KaTeX as the renderer for markdown-it-simplemath
|
||||
const katexInline = function(latex) {
|
||||
options.displayMode = false;
|
||||
try {
|
||||
return `<span class="joplin-editable"><span class="joplin-source" data-joplin-source-open="$" data-joplin-source-close="$">${latex}</span>${renderToStringWithCache(latex, options)}</span>`;
|
||||
} catch (error) {
|
||||
console.error('Katex error for:', latex, error);
|
||||
return latex;
|
||||
}
|
||||
};
|
||||
|
||||
const inlineRenderer = function(tokens, idx) {
|
||||
addContextAssets();
|
||||
return katexInline(tokens[idx].content);
|
||||
};
|
||||
|
||||
const katexBlock = function(latex) {
|
||||
options.displayMode = true;
|
||||
try {
|
||||
return `<div class="joplin-editable"><pre class="joplin-source" data-joplin-source-open="$$ " data-joplin-source-close=" $$ ">${latex}</pre>${renderToStringWithCache(latex, options)}</div>`;
|
||||
} catch (error) {
|
||||
console.error('Katex error for:', latex, error);
|
||||
return latex;
|
||||
}
|
||||
};
|
||||
|
||||
const blockRenderer = function(tokens, idx) {
|
||||
addContextAssets();
|
||||
return `${katexBlock(tokens[idx].content)}\n`;
|
||||
};
|
||||
|
||||
md.inline.ruler.after('escape', 'math_inline', math_inline);
|
||||
md.block.ruler.after('blockquote', 'math_block', math_block, {
|
||||
alt: ['paragraph', 'reference', 'blockquote', 'list'],
|
||||
});
|
||||
md.renderer.rules.math_inline = inlineRenderer;
|
||||
md.renderer.rules.math_block = blockRenderer;
|
||||
};
|
||||
|
||||
const inlineRenderer = function(tokens, idx) {
|
||||
addContextAssets();
|
||||
return katexInline(tokens[idx].content);
|
||||
};
|
||||
|
||||
const katexBlock = function(latex) {
|
||||
options.displayMode = true;
|
||||
try {
|
||||
return `<div class="joplin-editable"><pre class="joplin-source" data-joplin-source-open="$$ " data-joplin-source-close=" $$ ">${latex}</pre>${renderToStringWithCache(latex, options)}</div>`;
|
||||
} catch (error) {
|
||||
console.error('Katex error for:', latex, error);
|
||||
return latex;
|
||||
}
|
||||
};
|
||||
|
||||
const blockRenderer = function(tokens, idx) {
|
||||
addContextAssets();
|
||||
return `${katexBlock(tokens[idx].content)}\n`;
|
||||
};
|
||||
|
||||
md.inline.ruler.after('escape', 'math_inline', math_inline);
|
||||
md.block.ruler.after('blockquote', 'math_block', math_block, {
|
||||
alt: ['paragraph', 'reference', 'blockquote', 'list'],
|
||||
});
|
||||
md.renderer.rules.math_inline = inlineRenderer;
|
||||
md.renderer.rules.math_block = blockRenderer;
|
||||
};
|
||||
},
|
||||
style: katexStyle,
|
||||
};
|
||||
|
@ -1,7 +1,5 @@
|
||||
function addContextAssets(context:any) {
|
||||
if ('mermaid' in context.pluginAssets) return;
|
||||
|
||||
context.pluginAssets['mermaid'] = [
|
||||
function style() {
|
||||
return [
|
||||
{ name: 'mermaid.min.js' },
|
||||
{ name: 'mermaid_render.js' },
|
||||
{
|
||||
@ -15,6 +13,12 @@ function addContextAssets(context:any) {
|
||||
];
|
||||
}
|
||||
|
||||
function addContextAssets(context:any) {
|
||||
if ('mermaid' in context.pluginAssets) return;
|
||||
|
||||
context.pluginAssets['mermaid'] = style();
|
||||
}
|
||||
|
||||
// @ts-ignore: Keep the function signature as-is despite unusued arguments
|
||||
function installRule(markdownIt:any, mdOptions:any, ruleOptions:any, context:any) {
|
||||
const defaultRender:Function = markdownIt.renderer.rules.fence || function(tokens:any[], idx:number, options:any, env:any, self:any) {
|
||||
@ -35,8 +39,11 @@ function installRule(markdownIt:any, mdOptions:any, ruleOptions:any, context:any
|
||||
};
|
||||
}
|
||||
|
||||
export default function(context:any, ruleOptions:any) {
|
||||
return function(md:any, mdOptions:any) {
|
||||
installRule(md, mdOptions, ruleOptions, context);
|
||||
};
|
||||
}
|
||||
export default {
|
||||
install: function(context:any, ruleOptions:any) {
|
||||
return function(md:any, mdOptions:any) {
|
||||
installRule(md, mdOptions, ruleOptions, context);
|
||||
};
|
||||
},
|
||||
style: style,
|
||||
};
|
||||
|
@ -13,6 +13,7 @@ module.exports = {
|
||||
raisedBackgroundColor: '#e5e5e5',
|
||||
htmlCodeColor: 'rgb(0,0,0)',
|
||||
htmlCodeFontSize: '.9em',
|
||||
bodyPaddingBottom: '0',
|
||||
|
||||
editorTheme: 'chrome',
|
||||
codeThemeCss: 'atom-one-light.css',
|
||||
|
@ -1,36 +1,34 @@
|
||||
module.exports = function(style, options) {
|
||||
style = style ? style : {};
|
||||
|
||||
// https://necolas.github.io/normalize.css/
|
||||
const normalizeCss = `
|
||||
html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||
article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}
|
||||
pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}
|
||||
b,strong{font-weight:bolder}small{font-size:80%}img{border-style:none}
|
||||
`;
|
||||
module.exports = function(theme) {
|
||||
theme = theme ? theme : {};
|
||||
|
||||
const fontFamily = '\'Avenir\', \'Arial\', sans-serif';
|
||||
|
||||
const css =
|
||||
`
|
||||
/* https://necolas.github.io/normalize.css/ */
|
||||
html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||
article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}
|
||||
pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}
|
||||
b,strong{font-weight:bolder}small{font-size:80%}img{border-style:none}
|
||||
|
||||
body {
|
||||
font-size: ${style.htmlFontSize};
|
||||
color: ${style.htmlColor};
|
||||
font-size: ${theme.htmlFontSize};
|
||||
color: ${theme.htmlColor};
|
||||
word-wrap: break-word;
|
||||
line-height: ${style.htmlLineHeight};
|
||||
background-color: ${style.htmlBackgroundColor};
|
||||
line-height: ${theme.htmlLineHeight};
|
||||
background-color: ${theme.htmlBackgroundColor};
|
||||
font-family: ${fontFamily};
|
||||
padding-bottom: ${options.paddingBottom};
|
||||
padding-bottom: ${theme.bodyPaddingBottom};
|
||||
}
|
||||
strong {
|
||||
color: ${style.colorBright};
|
||||
color: ${theme.colorBright};
|
||||
}
|
||||
kbd {
|
||||
border: 1px solid ${style.htmlCodeBorderColor};
|
||||
box-shadow: inset 0 -1px 0 ${style.htmlCodeBorderColor};
|
||||
border: 1px solid ${theme.htmlCodeBorderColor};
|
||||
box-shadow: inset 0 -1px 0 ${theme.htmlCodeBorderColor};
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
background-color: ${style.htmlCodeBackgroundColor};
|
||||
background-color: ${theme.htmlCodeBackgroundColor};
|
||||
}
|
||||
::-webkit-scrollbar {
|
||||
width: 7px;
|
||||
@ -79,7 +77,7 @@ module.exports = function(style, options) {
|
||||
h1 {
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid ${style.htmlDividerColor};
|
||||
border-bottom: 1px solid ${theme.htmlDividerColor};
|
||||
padding-bottom: .3em;
|
||||
}
|
||||
h2 {
|
||||
@ -95,7 +93,7 @@ module.exports = function(style, options) {
|
||||
font-weight: bold;
|
||||
}
|
||||
a {
|
||||
color: ${style.htmlLinkColor};
|
||||
color: ${theme.htmlLinkColor};
|
||||
}
|
||||
ul, ol {
|
||||
padding-left: 0;
|
||||
@ -116,7 +114,7 @@ module.exports = function(style, options) {
|
||||
width: 1.2em;
|
||||
height: 1.4em;
|
||||
margin-right: 0.4em;
|
||||
background-color: ${style.htmlLinkColor};
|
||||
background-color: ${theme.htmlLinkColor};
|
||||
}
|
||||
/* These icons are obtained from the wonderful ForkAwesome project by copying the src svgs
|
||||
* into the css classes below.
|
||||
@ -177,7 +175,7 @@ module.exports = function(style, options) {
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
blockquote {
|
||||
border-left: 4px solid ${style.htmlCodeBorderColor};
|
||||
border-left: 4px solid ${theme.htmlCodeBorderColor};
|
||||
padding-left: 1.2em;
|
||||
margin-left: 0;
|
||||
opacity: .7;
|
||||
@ -185,45 +183,45 @@ module.exports = function(style, options) {
|
||||
table {
|
||||
text-align: left-align;
|
||||
border-collapse: collapse;
|
||||
border: 1px solid ${style.htmlCodeBorderColor};
|
||||
background-color: ${style.htmlBackgroundColor};
|
||||
border: 1px solid ${theme.htmlCodeBorderColor};
|
||||
background-color: ${theme.htmlBackgroundColor};
|
||||
}
|
||||
td, th {
|
||||
padding: .5em 1em .5em 1em;
|
||||
font-size: ${style.htmlFontSize};
|
||||
color: ${style.htmlColor};
|
||||
font-size: ${theme.htmlFontSize};
|
||||
color: ${theme.htmlColor};
|
||||
font-family: ${fontFamily};
|
||||
}
|
||||
td {
|
||||
border: 1px solid ${style.htmlCodeBorderColor};
|
||||
border: 1px solid ${theme.htmlCodeBorderColor};
|
||||
}
|
||||
th {
|
||||
border: 1px solid ${style.htmlCodeBorderColor};
|
||||
border-bottom: 2px solid ${style.htmlCodeBorderColor};
|
||||
background-color: ${style.htmlTableBackgroundColor};
|
||||
border: 1px solid ${theme.htmlCodeBorderColor};
|
||||
border-bottom: 2px solid ${theme.htmlCodeBorderColor};
|
||||
background-color: ${theme.htmlTableBackgroundColor};
|
||||
}
|
||||
tr:nth-child(even) {
|
||||
background-color: ${style.htmlTableBackgroundColor};
|
||||
background-color: ${theme.htmlTableBackgroundColor};
|
||||
}
|
||||
tr:hover {
|
||||
background-color: ${style.raisedBackgroundColor};
|
||||
background-color: ${theme.raisedBackgroundColor};
|
||||
}
|
||||
hr {
|
||||
border: none;
|
||||
border-bottom: 2px solid ${style.htmlDividerColor};
|
||||
border-bottom: 2px solid ${theme.htmlDividerColor};
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.inline-code {
|
||||
border: 1px solid ${style.htmlCodeBorderColor};
|
||||
background-color: ${style.htmlCodeBackgroundColor};
|
||||
border: 1px solid ${theme.htmlCodeBorderColor};
|
||||
background-color: ${theme.htmlCodeBackgroundColor};
|
||||
padding-right: .2em;
|
||||
padding-left: .2em;
|
||||
border-radius: .25em;
|
||||
color: ${style.htmlCodeColor};
|
||||
font-size: ${style.htmlCodeFontSize};
|
||||
color: ${theme.htmlCodeColor};
|
||||
font-size: ${theme.htmlCodeFontSize};
|
||||
}
|
||||
|
||||
.highlighted-keyword {
|
||||
@ -316,5 +314,5 @@ module.exports = function(style, options) {
|
||||
}
|
||||
`;
|
||||
|
||||
return [normalizeCss, css];
|
||||
return [css];
|
||||
};
|
||||
|
Reference in New Issue
Block a user