1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-11-27 08:21:03 +02:00

Desktop: Pass custom CSS property to all export handlers and renderers

This commit is contained in:
Laurent Cozic 2021-05-19 15:00:16 +02:00
parent 77b284f01f
commit bd08041f53
19 changed files with 52 additions and 26 deletions

View File

@ -166,6 +166,7 @@ export default class InteropServiceHelper {
exportOptions.format = module.format;
// exportOptions.modulePath = module.path;
if (options.plugins) exportOptions.plugins = options.plugins;
exportOptions.customCss = options.customCss;
exportOptions.target = module.target;
exportOptions.includeConflicts = !!options.includeConflicts;
if (options.sourceFolderIds) exportOptions.sourceFolderIds = options.sourceFolderIds;

View File

@ -90,6 +90,7 @@ interface Props {
['spellChecker.enabled']: boolean;
['spellChecker.language']: string;
plugins: PluginStates;
customCss: string;
}
const commandNames: string[] = menuCommandNames();
@ -313,7 +314,10 @@ function useMenu(props: Props) {
await InteropServiceHelper.export(
(action: any) => props.dispatch(action),
module,
{ plugins: props.plugins }
{
plugins: props.plugins,
customCss: props.customCss,
}
);
},
});
@ -860,7 +864,7 @@ function useMenu(props: Props) {
clearTimeout(timeoutId);
timeoutId = null;
};
}, [props.routeName, props.pluginMenuItems, props.pluginMenus, keymapLastChangeTime, modulesLastChangeTime, props['spellChecker.language'], props['spellChecker.enabled'], props.plugins]);
}, [props.routeName, props.pluginMenuItems, props.pluginMenus, keymapLastChangeTime, modulesLastChangeTime, props['spellChecker.language'], props['spellChecker.enabled'], props.plugins, props.customCss]);
useMenuStates(menu, props);
@ -917,6 +921,7 @@ const mapStateToProps = (state: AppState) => {
['spellChecker.language']: state.settings['spellChecker.language'],
['spellChecker.enabled']: state.settings['spellChecker.enabled'],
plugins: state.pluginService.plugins,
customCss: state.customCss,
};
};

View File

@ -13,6 +13,7 @@ interface MultiNoteActionsProps {
watchedNoteFiles: string[];
plugins: PluginStates;
inConflictFolder: boolean;
customCss: string;
}
function styles_(props: MultiNoteActionsProps) {
@ -53,6 +54,7 @@ export default function MultiNoteActions(props: MultiNoteActionsProps) {
watchedNoteFiles: props.watchedNoteFiles,
plugins: props.plugins,
inConflictFolder: props.inConflictFolder,
customCss: props.customCss,
});
const itemComps = [];

View File

@ -24,7 +24,7 @@ interface KeyToLabelMap {
let markupToHtml_: any = null;
function markupToHtml() {
if (markupToHtml_) return markupToHtml_;
markupToHtml_ = markupLanguageUtils.newMarkupToHtml({});
markupToHtml_ = markupLanguageUtils.newMarkupToHtml();
return markupToHtml_;
}

View File

@ -358,7 +358,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
element.id = script.id;
element.onload = () => {
resolve();
resolve(null);
};
document.getElementsByTagName('head')[0].appendChild(element);

View File

@ -156,10 +156,11 @@ function NoteEditor(props: NoteEditorProps) {
const markupToHtml = markupLanguageUtils.newMarkupToHtml({}, {
resourceBaseUrl: `file://${Setting.value('resourceDir')}/`,
customCss: props.customCss,
});
return markupToHtml.allAssets(markupLanguage, theme);
}, [props.themeId]);
}, [props.themeId, props.customCss]);
const handleProvisionalFlag = useCallback(() => {
if (props.isProvisional) {
@ -458,6 +459,7 @@ function NoteEditor(props: NoteEditorProps) {
watchedNoteFiles={props.watchedNoteFiles}
plugins={props.plugins}
inConflictFolder={props.selectedFolderId === Folder.conflictFolderId()}
customCss={props.customCss}
/>;
}

View File

@ -2,6 +2,8 @@
import AsyncActionQueue from '@joplin/lib/AsyncActionQueue';
import { ToolbarButtonInfo } from '@joplin/lib/services/commands/ToolbarButtonUtils';
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
import { MarkupLanguage } from '@joplin/renderer';
import { RenderResult, RenderResultPluginAsset } from '@joplin/renderer/MarkupToHtml';
export interface ToolbarButtonInfos {
[key: string]: ToolbarButtonInfo;
@ -49,9 +51,9 @@ export interface NoteBodyEditorProps {
onWillChange(event: any): void;
onMessage(event: any): void;
onScroll(event: any): void;
markupToHtml: Function;
markupToHtml: (markupLanguage: MarkupLanguage, markup: string, options: any)=> Promise<RenderResult>;
htmlToMarkdown: Function;
allAssets: Function;
allAssets: (markupLanguage: MarkupLanguage)=> Promise<RenderResultPluginAsset[]>;
disabled: boolean;
dispatch: Function;
noteToolbar: any;

View File

@ -24,6 +24,7 @@ export default function useMarkupToHtml(deps: HookDependencies) {
const markupToHtml = useMemo(() => {
return markupLanguageUtils.newMarkupToHtml(deps.plugins, {
resourceBaseUrl: `file://${Setting.value('resourceDir')}/`,
customCss: customCss || '',
});
}, [plugins]);
@ -49,7 +50,6 @@ export default function useMarkupToHtml(deps: HookDependencies) {
const result = await markupToHtml.render(markupLanguage, md, theme, Object.assign({}, {
codeTheme: theme.codeThemeCss,
userCss: customCss || '',
resources: resources,
postMessageSyntax: 'ipcProxySendToHost',
splitted: true,

View File

@ -125,6 +125,7 @@ class NoteListComponent extends React.Component {
watchedNoteFiles: this.props.watchedNoteFiles,
plugins: this.props.plugins,
inConflictFolder: this.props.selectedFolderId === Folder.conflictFolderId(),
customCss: this.props.customCss,
});
menu.popup(bridge().window());
@ -513,6 +514,7 @@ const mapStateToProps = (state: AppState) => {
noteSortOrder: state.settings['notes.sortOrder.field'],
highlightedWords: state.highlightedWords,
plugins: state.pluginService.plugins,
customCss: state.customCss,
};
};

View File

@ -118,11 +118,11 @@ class NoteRevisionViewerComponent extends React.PureComponent {
const markupToHtml = markupLanguageUtils.newMarkupToHtml({}, {
resourceBaseUrl: `file://${Setting.value('resourceDir')}/`,
customCss: this.props.customCss ? this.props.customCss : '',
});
const result = await markupToHtml.render(markupLanguage, noteBody, theme, {
codeTheme: theme.codeThemeCss,
userCss: this.props.customCss ? this.props.customCss : '',
resources: await shared.attachedResources(noteBody),
postMessageSyntax: 'ipcProxySendToHost',
});

View File

@ -22,6 +22,7 @@ interface ContextMenuProps {
watchedNoteFiles: string[];
plugins: PluginStates;
inConflictFolder: boolean;
customCss: string;
}
export default class NoteListUtils {
@ -158,6 +159,7 @@ export default class NoteListUtils {
sourceNoteIds: noteIds,
includeConflicts: props.inConflictFolder,
plugins: props.plugins,
customCss: props.customCss,
});
},
})

View File

@ -246,7 +246,7 @@ class Dialog extends React.PureComponent<Props, State> {
markupToHtml() {
if (this.markupToHtml_) return this.markupToHtml_;
this.markupToHtml_ = markupLanguageUtils.newMarkupToHtml({});
this.markupToHtml_ = markupLanguageUtils.newMarkupToHtml();
return this.markupToHtml_;
}

View File

@ -1,11 +1,14 @@
import { MarkupLanguageUtils as BaseMarkupLanguageUtils } from '@joplin/lib/markupLanguageUtils';
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
import { contentScriptsToRendererRules } from '@joplin/lib/services/plugins/utils/loadContentScripts';
import { Options } from '@joplin/renderer/MarkupToHtml';
class MarkupLanguageUtils extends BaseMarkupLanguageUtils {
public newMarkupToHtml(plugins: PluginStates, options: any = null) {
return super.newMarkupToHtml({
public newMarkupToHtml(plugins: PluginStates = null, options: Options = null) {
plugins = plugins || {};
return super.newMarkupToHtml(null, {
extraRendererRules: contentScriptsToRendererRules(plugins),
...options,
});

View File

@ -1,10 +1,11 @@
import markdownUtils from './markdownUtils';
import Setting from './models/Setting';
import shim from './shim';
import MarkupToHtml, { MarkupLanguage } from '@joplin/renderer/MarkupToHtml';
import MarkupToHtml, { MarkupLanguage, Options } from '@joplin/renderer/MarkupToHtml';
import htmlUtils from './htmlUtils';
import Resource from './models/Resource';
import { PluginStates } from './services/plugins/reducer';
export class MarkupLanguageUtils {
@ -20,7 +21,7 @@ export class MarkupLanguageUtils {
// Create a new MarkupToHtml instance while injecting options specific to Joplin
// desktop and mobile applications.
public newMarkupToHtml(options: any = null) {
public newMarkupToHtml(_plugins: PluginStates = null, options: Options = null) {
const subValues = Setting.subValues('markdown.plugin', Setting.toPlainObject());
const pluginOptions: any = {};
for (const n in subValues) {

View File

@ -39,8 +39,9 @@ export default class InteropService_Exporter_Html extends InteropService_Exporte
this.resourceDir_ = this.destDir_ ? `${this.destDir_}/_resources` : null;
await shim.fsDriver().mkdir(this.destDir_);
this.markupToHtml_ = markupLanguageUtils.newMarkupToHtml({
this.markupToHtml_ = markupLanguageUtils.newMarkupToHtml(null, {
extraRendererRules: contentScriptsToRendererRules(options.plugins),
customCss: this.customCss_ || '',
});
this.style_ = themeStyle(Setting.THEME_LIGHT);
}
@ -105,7 +106,6 @@ export default class InteropService_Exporter_Html extends InteropService_Exporte
const result = await this.markupToHtml_.render(item.markup_language, bodyMd, this.style_, {
resources: this.resources_,
plainResourceRendering: true,
userCss: this.customCss_,
});
const noteContent = [];
if (item.title) noteContent.push(`<div class="exported-note-title">${escapeHtml(item.title)}</div>`);

View File

@ -96,6 +96,7 @@ export interface ExportOptions {
target?: FileSystemItem;
includeConflicts?: boolean;
plugins?: PluginStates;
customCss?: string;
}
export interface ImportExportResult {

View File

@ -6,6 +6,7 @@ import utils, { ItemIdToUrlHandler } from './utils';
// import Setting from '@joplin/lib/models/Setting';
// const { themeStyle } = require('@joplin/lib/theme');
import InMemoryCache from './InMemoryCache';
import { RenderResult } from './MarkupToHtml';
const md5 = require('md5');
// Renderered notes can potentially be quite large (for example
@ -35,11 +36,6 @@ interface RenderOptions {
itemIdToUrl?: ItemIdToUrlHandler;
}
interface RenderResult {
html: string;
pluginAssets: any[];
}
// https://github.com/es-shims/String.prototype.trimStart/blob/main/implementation.js
function trimStart(s: string): string {
// eslint-disable-next-line no-control-regex

View File

@ -24,7 +24,7 @@ export interface RenderResultPluginAsset {
export interface RenderResult {
html: string;
pluginAssets: RenderResultPluginAsset[];
cssStrings: string[];
cssStrings?: string[];
}
export interface OptionsResourceModel {
@ -33,7 +33,10 @@ export interface OptionsResourceModel {
export interface Options {
isSafeMode?: boolean;
ResourceModel: OptionsResourceModel;
ResourceModel?: OptionsResourceModel;
customCss?: string;
extraRendererRules?: any[];
resourceBaseUrl?: string;
}
export default class MarkupToHtml {
@ -68,7 +71,7 @@ export default class MarkupToHtml {
throw new Error(`Invalid markup language: ${markupLanguage}`);
}
this.renderers_[markupLanguage] = new RendererClass(this.options_);
this.renderers_[markupLanguage] = new RendererClass(this.options_ as any);
return this.renderers_[markupLanguage];
}

View File

@ -86,6 +86,7 @@ export interface Options {
tempDir?: string;
fsDriver?: any;
extraRendererRules?: ExtraRendererRule[];
customCss?: string;
}
interface PluginAsset {
@ -167,6 +168,7 @@ export default class MdToHtml {
private pluginOptions_: any = {};
private extraRendererRules_: RendererRules = {};
private allProcessedAssets_: any = {};
private customCss_: string = '';
public constructor(options: Options = null) {
if (!options) options = {};
@ -195,6 +197,8 @@ export default class MdToHtml {
this.loadExtraRendererRule(rule.id, rule.assetPath, rule.module);
}
}
this.customCss_ = options.customCss || '';
}
private fsDriver() {
@ -340,13 +344,15 @@ export default class MdToHtml {
const processedAssets = this.processPluginAssets(assets);
processedAssets.cssStrings.splice(0, 0, noteStyle(theme).join('\n'));
if (this.customCss_) processedAssets.cssStrings.push(this.customCss_);
const output = await this.outputAssetsToExternalAssets_(processedAssets);
return output.pluginAssets;
}
private async outputAssetsToExternalAssets_(output: any) {
for (const cssString of output.cssStrings) {
output.pluginAssets.push(await this.fsDriver().cacheCssToFile(cssString));
const filePath = await this.fsDriver().cacheCssToFile(cssString);
output.pluginAssets.push(filePath);
}
delete output.cssStrings;
return output;
@ -524,7 +530,7 @@ export default class MdToHtml {
let output = { ...this.allProcessedAssets(allRules, options.theme, options.codeTheme) };
cssStrings = cssStrings.concat(output.cssStrings);
if (options.userCss) cssStrings.push(options.userCss);
if (this.customCss_) cssStrings.push(this.customCss_);
if (options.bodyOnly) {
// Markdown-it wraps any content in <p></p> by default. There's a function to parse without