mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
Desktop: Allow setting a max width for the editor content
This commit is contained in:
parent
a9961ae3ec
commit
8063c94ff7
@ -33,10 +33,11 @@ const Menu = bridge().Menu;
|
||||
const MenuItem = bridge().MenuItem;
|
||||
import { reg } from '@joplin/lib/registry';
|
||||
import ErrorBoundary from '../../../ErrorBoundary';
|
||||
import { MarkupToHtmlOptions } from '../../utils/useMarkupToHtml';
|
||||
|
||||
const menuUtils = new MenuUtils(CommandService.instance());
|
||||
|
||||
function markupRenderOptions(override: any = null) {
|
||||
function markupRenderOptions(override: MarkupToHtmlOptions = null): MarkupToHtmlOptions {
|
||||
return { ...override };
|
||||
}
|
||||
|
||||
@ -384,6 +385,12 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
if (Setting.value('style.editor.monospaceFontFamily')) monospaceFonts.push(`"${Setting.value('style.editor.monospaceFontFamily')}"`);
|
||||
monospaceFonts.push('monospace');
|
||||
|
||||
const maxWidthCss = props.contentMaxWidth ? `
|
||||
margin-right: auto !important;
|
||||
margin-left: auto !important;
|
||||
max-width: ${props.contentMaxWidth}px !important;
|
||||
` : '';
|
||||
|
||||
const element = document.createElement('style');
|
||||
element.setAttribute('id', 'codemirrorStyle');
|
||||
document.head.appendChild(element);
|
||||
@ -418,6 +425,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
/* Add a fixed right padding to account for the appearance (and disappearance) */
|
||||
/* of the sidebar */
|
||||
padding-right: 10px !important;
|
||||
${maxWidthCss}
|
||||
}
|
||||
|
||||
/* This enforces monospace for certain elements (code, tables, etc.) */
|
||||
@ -533,7 +541,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
return () => {
|
||||
document.head.removeChild(element);
|
||||
};
|
||||
}, [props.themeId]);
|
||||
}, [props.themeId, props.contentMaxWidth]);
|
||||
|
||||
const webview_domReady = useCallback(() => {
|
||||
setWebviewReady(true);
|
||||
@ -572,7 +580,10 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
bodyToRender = `<i>${_('This note has no content. Click on "%s" to toggle the editor and edit the note.', _('Layout'))}</i>`;
|
||||
}
|
||||
|
||||
const result = await props.markupToHtml(props.contentMarkupLanguage, bodyToRender, markupRenderOptions({ resourceInfos: props.resourceInfos }));
|
||||
const result = await props.markupToHtml(props.contentMarkupLanguage, bodyToRender, markupRenderOptions({
|
||||
resourceInfos: props.resourceInfos,
|
||||
contentMaxWidth: props.contentMaxWidth,
|
||||
}));
|
||||
|
||||
if (cancelled) return;
|
||||
|
||||
@ -795,6 +806,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
viewerStyle={styles.viewer}
|
||||
onIpcMessage={webview_ipcMessage}
|
||||
onDomReady={webview_domReady}
|
||||
contentMaxWidth={props.contentMaxWidth}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -14,18 +14,18 @@ import { _, closestSupportedLocale } from '@joplin/lib/locale';
|
||||
import useContextMenu from './utils/useContextMenu';
|
||||
import { copyHtmlToClipboard } from '../../utils/clipboardUtils';
|
||||
import shim from '@joplin/lib/shim';
|
||||
|
||||
const { MarkupToHtml } = require('@joplin/renderer');
|
||||
import { MarkupToHtml } from '@joplin/renderer';
|
||||
import { reg } from '@joplin/lib/registry';
|
||||
import BaseItem from '@joplin/lib/models/BaseItem';
|
||||
import setupToolbarButtons from './utils/setupToolbarButtons';
|
||||
import { plainTextToHtml } from '@joplin/lib/htmlUtils';
|
||||
import openEditDialog from './utils/openEditDialog';
|
||||
const { themeStyle } = require('@joplin/lib/theme');
|
||||
import { MarkupToHtmlOptions } from '../../utils/useMarkupToHtml';
|
||||
import { themeStyle } from '@joplin/lib/theme';
|
||||
const { clipboard } = require('electron');
|
||||
const supportedLocales = require('./supportedLocales');
|
||||
|
||||
function markupRenderOptions(override: any = null) {
|
||||
function markupRenderOptions(override: MarkupToHtmlOptions = null): MarkupToHtmlOptions {
|
||||
return {
|
||||
plugins: {
|
||||
checkbox: {
|
||||
@ -148,8 +148,6 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
if (!resourceMd) return;
|
||||
const result = await props.markupToHtml(MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN, resourceMd, markupRenderOptions({ bodyOnly: true }));
|
||||
editor.insertContent(result.html);
|
||||
// editor.fire('joplinChange');
|
||||
// dispatchDidUpdate(editor);
|
||||
}, [props.markupToHtml, editor]);
|
||||
|
||||
const insertResourcesIntoContentRef = useRef(null);
|
||||
|
@ -159,8 +159,8 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
customCss: props.customCss,
|
||||
});
|
||||
|
||||
return markupToHtml.allAssets(markupLanguage, theme);
|
||||
}, [props.themeId, props.customCss]);
|
||||
return markupToHtml.allAssets(markupLanguage, theme, { contentMaxWidth: props.contentMaxWidth });
|
||||
}, [props.themeId, props.customCss, props.contentMaxWidth]);
|
||||
|
||||
const handleProvisionalFlag = useCallback(() => {
|
||||
if (props.isProvisional) {
|
||||
@ -400,6 +400,7 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
noteToolbarButtonInfos: props.toolbarButtonInfos,
|
||||
plugins: props.plugins,
|
||||
fontSize: Setting.value('style.editor.fontSize'),
|
||||
contentMaxWidth: props.contentMaxWidth,
|
||||
};
|
||||
|
||||
let editor = null;
|
||||
@ -601,6 +602,7 @@ const mapStateToProps = (state: AppState) => {
|
||||
setTagsToolbarButtonInfo: toolbarButtonUtils.commandsToToolbarButtons([
|
||||
'setTags',
|
||||
], whenClauseContext)[0],
|
||||
contentMaxWidth: state.settings['style.editor.contentMaxWidth'],
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -4,6 +4,7 @@ import { ToolbarButtonInfo } from '@joplin/lib/services/commands/ToolbarButtonUt
|
||||
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
|
||||
import { MarkupLanguage } from '@joplin/renderer';
|
||||
import { RenderResult, RenderResultPluginAsset } from '@joplin/renderer/MarkupToHtml';
|
||||
import { MarkupToHtmlOptions } from './useMarkupToHtml';
|
||||
|
||||
export interface ToolbarButtonInfos {
|
||||
[key: string]: ToolbarButtonInfo;
|
||||
@ -37,6 +38,7 @@ export interface NoteEditorProps {
|
||||
toolbarButtonInfos: ToolbarButtonInfo[];
|
||||
setTagsToolbarButtonInfo: ToolbarButtonInfo;
|
||||
richTextBannerDismissed: boolean;
|
||||
contentMaxWidth: number;
|
||||
}
|
||||
|
||||
export interface NoteBodyEditorProps {
|
||||
@ -51,7 +53,7 @@ export interface NoteBodyEditorProps {
|
||||
onWillChange(event: any): void;
|
||||
onMessage(event: any): void;
|
||||
onScroll(event: any): void;
|
||||
markupToHtml: (markupLanguage: MarkupLanguage, markup: string, options: any)=> Promise<RenderResult>;
|
||||
markupToHtml: (markupLanguage: MarkupLanguage, markup: string, options: MarkupToHtmlOptions)=> Promise<RenderResult>;
|
||||
htmlToMarkdown: Function;
|
||||
allAssets: (markupLanguage: MarkupLanguage)=> Promise<RenderResultPluginAsset[]>;
|
||||
disabled: boolean;
|
||||
@ -67,6 +69,7 @@ export interface NoteBodyEditorProps {
|
||||
noteToolbarButtonInfos: ToolbarButtonInfo[];
|
||||
plugins: PluginStates;
|
||||
fontSize: number;
|
||||
contentMaxWidth: number;
|
||||
}
|
||||
|
||||
export interface FormNote {
|
||||
|
@ -13,9 +13,12 @@ interface HookDependencies {
|
||||
plugins: PluginStates;
|
||||
}
|
||||
|
||||
interface MarkupToHtmlOptions {
|
||||
export interface MarkupToHtmlOptions {
|
||||
replaceResourceInternalToExternalLinks?: boolean;
|
||||
resourceInfos?: ResourceInfos;
|
||||
contentMaxWidth?: number;
|
||||
plugins?: Record<string, any>;
|
||||
bodyOnly?: boolean;
|
||||
}
|
||||
|
||||
export default function useMarkupToHtml(deps: HookDependencies) {
|
||||
|
@ -972,6 +972,8 @@ class Setting extends BaseModel {
|
||||
storage: SettingStorage.File,
|
||||
},
|
||||
|
||||
'style.editor.contentMaxWidth': { value: 600, type: SettingItemType.Int, public: true, storage: SettingStorage.File, appTypes: [AppType.Desktop], section: 'appearance', label: () => _('Editor maximum width'), description: () => _('Set it to 0 to make it take the complete available space.') },
|
||||
|
||||
'ui.layout': { value: {}, type: SettingItemType.Object, storage: SettingStorage.File, public: false, appTypes: [AppType.Desktop] },
|
||||
|
||||
// TODO: Is there a better way to do this? The goal here is to simply have
|
||||
|
@ -1,6 +1,7 @@
|
||||
import MdToHtml from './MdToHtml';
|
||||
import HtmlToHtml from './HtmlToHtml';
|
||||
import htmlUtils from './htmlUtils';
|
||||
import { Options as NoteStyleOptions } from './noteStyle';
|
||||
const MarkdownIt = require('markdown-it');
|
||||
|
||||
export enum MarkupLanguage {
|
||||
@ -48,7 +49,7 @@ export default class MarkupToHtml {
|
||||
private options_: Options;
|
||||
private rawMarkdownIt_: any;
|
||||
|
||||
public constructor(options: Options) {
|
||||
public constructor(options: Options = null) {
|
||||
this.options_ = {
|
||||
ResourceModel: {
|
||||
isResourceUrl: () => false,
|
||||
@ -119,7 +120,7 @@ export default class MarkupToHtml {
|
||||
return this.renderer(markupLanguage).render(markup, theme, options);
|
||||
}
|
||||
|
||||
public async allAssets(markupLanguage: MarkupLanguage, theme: any) {
|
||||
return this.renderer(markupLanguage).allAssets(theme);
|
||||
public async allAssets(markupLanguage: MarkupLanguage, theme: any, noteStyleOptions: NoteStyleOptions = null) {
|
||||
return this.renderer(markupLanguage).allAssets(theme, noteStyleOptions);
|
||||
}
|
||||
}
|
||||
|
@ -5,10 +5,28 @@ import setupLinkify from './MdToHtml/setupLinkify';
|
||||
import validateLinks from './MdToHtml/validateLinks';
|
||||
import { ItemIdToUrlHandler } from './utils';
|
||||
import { RenderResult, RenderResultPluginAsset } from './MarkupToHtml';
|
||||
import { Options as NoteStyleOptions } from './noteStyle';
|
||||
|
||||
const MarkdownIt = require('markdown-it');
|
||||
const md5 = require('md5');
|
||||
|
||||
export interface RenderOptions {
|
||||
contentMaxWidth?: number;
|
||||
bodyOnly?: boolean;
|
||||
splitted?: boolean;
|
||||
externalAssetsOnly?: boolean;
|
||||
postMessageSyntax?: string;
|
||||
highlightedKeywords?: string[];
|
||||
codeTheme?: string;
|
||||
theme?: any;
|
||||
plugins?: Record<string, any>;
|
||||
audioPlayerEnabled?: boolean;
|
||||
videoPlayerEnabled?: boolean;
|
||||
pdfViewerEnabled?: boolean;
|
||||
codeHighlightCacheKey?: string;
|
||||
plainResourceRendering?: boolean;
|
||||
}
|
||||
|
||||
interface RendererRule {
|
||||
install(context: any, ruleOptions: any): any;
|
||||
assets?(theme: any): any;
|
||||
@ -331,7 +349,7 @@ export default class MdToHtml {
|
||||
}
|
||||
|
||||
// This is similar to allProcessedAssets() but used only by the Rich Text editor
|
||||
public async allAssets(theme: any) {
|
||||
public async allAssets(theme: any, noteStyleOptions: NoteStyleOptions = null) {
|
||||
const assets: any = {};
|
||||
for (const key in rules) {
|
||||
if (!this.pluginEnabled(key)) continue;
|
||||
@ -343,7 +361,7 @@ export default class MdToHtml {
|
||||
}
|
||||
|
||||
const processedAssets = this.processPluginAssets(assets);
|
||||
processedAssets.cssStrings.splice(0, 0, noteStyle(theme).join('\n'));
|
||||
processedAssets.cssStrings.splice(0, 0, noteStyle(theme, noteStyleOptions).join('\n'));
|
||||
if (this.customCss_) processedAssets.cssStrings.push(this.customCss_);
|
||||
const output = await this.outputAssetsToExternalAssets_(processedAssets);
|
||||
return output.pluginAssets;
|
||||
@ -376,8 +394,9 @@ export default class MdToHtml {
|
||||
}
|
||||
|
||||
// "theme" is the theme as returned by themeStyle()
|
||||
public async render(body: string, theme: any = null, options: any = null): Promise<RenderResult> {
|
||||
options = Object.assign({}, {
|
||||
public async render(body: string, theme: any = null, options: RenderOptions = null): Promise<RenderResult> {
|
||||
|
||||
options = {
|
||||
// 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.
|
||||
@ -395,7 +414,10 @@ export default class MdToHtml {
|
||||
audioPlayerEnabled: this.pluginEnabled('audioPlayer'),
|
||||
videoPlayerEnabled: this.pluginEnabled('videoPlayer'),
|
||||
pdfViewerEnabled: this.pluginEnabled('pdfViewer'),
|
||||
}, options);
|
||||
|
||||
contentMaxWidth: 0,
|
||||
...options,
|
||||
};
|
||||
|
||||
// The "codeHighlightCacheKey" option indicates what set of cached object should be
|
||||
// associated with this particular Markdown body. It is only used to allow us to
|
||||
@ -525,7 +547,9 @@ export default class MdToHtml {
|
||||
|
||||
const renderedBody = markdownIt.render(body, context);
|
||||
|
||||
let cssStrings = noteStyle(options.theme);
|
||||
let cssStrings = noteStyle(options.theme, {
|
||||
contentMaxWidth: options.contentMaxWidth,
|
||||
});
|
||||
|
||||
let output = { ...this.allProcessedAssets(allRules, options.theme, options.codeTheme) };
|
||||
cssStrings = cssStrings.concat(output.cssStrings);
|
||||
|
@ -7,11 +7,28 @@ function formatCssSize(v: any): string {
|
||||
return `${v}px`;
|
||||
}
|
||||
|
||||
export default function(theme: any) {
|
||||
export interface Options {
|
||||
contentMaxWidth?: number;
|
||||
}
|
||||
|
||||
export default function(theme: any, options: Options = null) {
|
||||
options = {
|
||||
contentMaxWidth: 0,
|
||||
...options,
|
||||
};
|
||||
|
||||
theme = theme ? theme : {};
|
||||
|
||||
const fontFamily = '\'Avenir\', \'Arial\', sans-serif';
|
||||
|
||||
const maxWidthCss = options.contentMaxWidth ? `
|
||||
#rendered-md {
|
||||
max-width: ${options.contentMaxWidth}px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
` : '';
|
||||
|
||||
const css =
|
||||
`
|
||||
/* https://necolas.github.io/normalize.css/ */
|
||||
@ -61,6 +78,8 @@ export default function(theme: any) {
|
||||
background: rgba(100, 100, 100, 0.7);
|
||||
}
|
||||
|
||||
${maxWidthCss}
|
||||
|
||||
/* Remove top padding and margin from first child so that top of rendered text is aligned to top of text editor text */
|
||||
|
||||
#rendered-md > h1:first-child,
|
||||
|
Loading…
Reference in New Issue
Block a user