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:
parent
77b284f01f
commit
bd08041f53
@ -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;
|
||||
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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 = [];
|
||||
|
@ -24,7 +24,7 @@ interface KeyToLabelMap {
|
||||
let markupToHtml_: any = null;
|
||||
function markupToHtml() {
|
||||
if (markupToHtml_) return markupToHtml_;
|
||||
markupToHtml_ = markupLanguageUtils.newMarkupToHtml({});
|
||||
markupToHtml_ = markupLanguageUtils.newMarkupToHtml();
|
||||
return markupToHtml_;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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}
|
||||
/>;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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',
|
||||
});
|
||||
|
@ -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,
|
||||
});
|
||||
},
|
||||
})
|
||||
|
@ -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_;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -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) {
|
||||
|
@ -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>`);
|
||||
|
@ -96,6 +96,7 @@ export interface ExportOptions {
|
||||
target?: FileSystemItem;
|
||||
includeConflicts?: boolean;
|
||||
plugins?: PluginStates;
|
||||
customCss?: string;
|
||||
}
|
||||
|
||||
export interface ImportExportResult {
|
||||
|
@ -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
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user