import PropTypes from 'prop-types' import React from 'react' import CSSModules from 'browser/lib/CSSModules' import styles from './ConfigTab.styl' import ConfigManager from 'browser/main/lib/ConfigManager' import { store } from 'browser/main/store' import consts from 'browser/lib/consts' import ReactCodeMirror from 'react-codemirror' import CodeMirror from 'codemirror' import 'codemirror-mode-elixir' import _ from 'lodash' import i18n from 'browser/lib/i18n' import { getLanguages } from 'browser/lib/Languages' import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily' import uiThemes from 'browser/lib/ui-themes' import { chooseTheme, applyTheme } from 'browser/main/lib/ThemeManager' const OSX = global.process.platform === 'darwin' const electron = require('electron') const ipc = electron.ipcRenderer class UiTab extends React.Component { constructor(props) { super(props) this.state = { config: props.config, codemirrorTheme: props.config.editor.theme } } componentDidMount() { CodeMirror.autoLoadMode( this.codeMirrorInstance.getCodeMirror(), 'javascript' ) CodeMirror.autoLoadMode(this.customCSSCM.getCodeMirror(), 'css') CodeMirror.autoLoadMode( this.customMarkdownLintConfigCM.getCodeMirror(), 'javascript' ) CodeMirror.autoLoadMode(this.prettierConfigCM.getCodeMirror(), 'javascript') // Set CM editor Sizes this.customCSSCM.getCodeMirror().setSize('400px', '400px') this.prettierConfigCM.getCodeMirror().setSize('400px', '400px') this.customMarkdownLintConfigCM.getCodeMirror().setSize('400px', '200px') this.handleSettingDone = () => { this.setState({ UiAlert: { type: 'success', message: i18n.__('Successfully applied!') } }) } this.handleSettingError = err => { this.setState({ UiAlert: { type: 'error', message: err.message != null ? err.message : i18n.__('An error occurred!') } }) } ipc.addListener('APP_SETTING_DONE', this.handleSettingDone) ipc.addListener('APP_SETTING_ERROR', this.handleSettingError) } componentWillUnmount() { ipc.removeListener('APP_SETTING_DONE', this.handleSettingDone) ipc.removeListener('APP_SETTING_ERROR', this.handleSettingError) } handleUIChange(e) { const { codemirrorTheme } = this.state let checkHighLight = document.getElementById('checkHighLight') if (checkHighLight === null) { checkHighLight = document.createElement('link') checkHighLight.setAttribute('id', 'checkHighLight') checkHighLight.setAttribute('rel', 'stylesheet') document.head.appendChild(checkHighLight) } const newConfig = { ui: { theme: this.refs.uiTheme.value, defaultTheme: this.refs.uiTheme.value, enableScheduleTheme: this.refs.enableScheduleTheme.checked, scheduledTheme: this.refs.uiScheduledTheme.value, scheduleStart: this.refs.scheduleStart.value, scheduleEnd: this.refs.scheduleEnd.value, language: this.refs.uiLanguage.value, defaultNote: this.refs.defaultNote.value, tagNewNoteWithFilteringTags: this.refs.tagNewNoteWithFilteringTags .checked, showCopyNotification: this.refs.showCopyNotification.checked, confirmDeletion: this.refs.confirmDeletion.checked, showOnlyRelatedTags: this.refs.showOnlyRelatedTags.checked, showTagsAlphabetically: this.refs.showTagsAlphabetically.checked, saveTagsAlphabetically: this.refs.saveTagsAlphabetically.checked, enableLiveNoteCounts: this.refs.enableLiveNoteCounts.checked, showScrollBar: this.refs.showScrollBar.checked, showMenuBar: this.refs.showMenuBar.checked, disableDirectWrite: this.refs.uiD2w != null ? this.refs.uiD2w.checked : false }, editor: { theme: this.refs.editorTheme.value, fontSize: this.refs.editorFontSize.value, fontFamily: this.refs.editorFontFamily.value, indentType: this.refs.editorIndentType.value, indentSize: this.refs.editorIndentSize.value, enableRulers: this.refs.enableEditorRulers.value === 'true', rulers: this.refs.editorRulers.value.replace(/[^0-9,]/g, '').split(','), displayLineNumbers: this.refs.editorDisplayLineNumbers.checked, lineWrapping: this.refs.editorLineWrapping.checked, switchPreview: this.refs.editorSwitchPreview.value, keyMap: this.refs.editorKeyMap.value, snippetDefaultLanguage: this.refs.editorSnippetDefaultLanguage.value, scrollPastEnd: this.refs.scrollPastEnd.checked, fetchUrlTitle: this.refs.editorFetchUrlTitle.checked, enableTableEditor: this.refs.enableTableEditor.checked, enableFrontMatterTitle: this.refs.enableFrontMatterTitle.checked, frontMatterTitleField: this.refs.frontMatterTitleField.value, matchingPairs: this.refs.matchingPairs.value, matchingTriples: this.refs.matchingTriples.value, explodingPairs: this.refs.explodingPairs.value, spellcheck: this.refs.spellcheck.checked, enableSmartPaste: this.refs.enableSmartPaste.checked, enableMarkdownLint: this.refs.enableMarkdownLint.checked, customMarkdownLintConfig: this.customMarkdownLintConfigCM .getCodeMirror() .getValue(), prettierConfig: this.prettierConfigCM.getCodeMirror().getValue(), deleteUnusedAttachments: this.refs.deleteUnusedAttachments.checked }, preview: { fontSize: this.refs.previewFontSize.value, fontFamily: this.refs.previewFontFamily.value, codeBlockTheme: this.refs.previewCodeBlockTheme.value, lineNumber: this.refs.previewLineNumber.checked, latexInlineOpen: this.refs.previewLatexInlineOpen.value, latexInlineClose: this.refs.previewLatexInlineClose.value, latexBlockOpen: this.refs.previewLatexBlockOpen.value, latexBlockClose: this.refs.previewLatexBlockClose.value, plantUMLServerAddress: this.refs.previewPlantUMLServerAddress.value, scrollPastEnd: this.refs.previewScrollPastEnd.checked, scrollSync: this.refs.previewScrollSync.checked, smartQuotes: this.refs.previewSmartQuotes.checked, breaks: this.refs.previewBreaks.checked, smartArrows: this.refs.previewSmartArrows.checked, sanitize: this.refs.previewSanitize.value, mermaidHTMLLabel: this.refs.previewMermaidHTMLLabel.checked, allowCustomCSS: this.refs.previewAllowCustomCSS.checked, lineThroughCheckbox: this.refs.lineThroughCheckbox.checked, customCSS: this.customCSSCM.getCodeMirror().getValue() } } const newCodemirrorTheme = this.refs.editorTheme.value if (newCodemirrorTheme !== codemirrorTheme) { const theme = consts.THEMES.find( theme => theme.name === newCodemirrorTheme ) if (theme) { checkHighLight.setAttribute('href', theme.path) } } this.setState( { config: newConfig, codemirrorTheme: newCodemirrorTheme }, () => { const { ui, editor, preview } = this.props.config this.currentConfig = { ui, editor, preview } if (_.isEqual(this.currentConfig, this.state.config)) { this.props.haveToSave() } else { this.props.haveToSave({ tab: 'UI', type: 'warning', message: i18n.__('Unsaved Changes!') }) } } ) } handleSaveUIClick(e) { const newConfig = { ui: this.state.config.ui, editor: this.state.config.editor, preview: this.state.config.preview } chooseTheme(newConfig) applyTheme(newConfig.ui.theme) ConfigManager.set(newConfig) store.dispatch({ type: 'SET_UI', config: newConfig }) this.clearMessage() this.props.haveToSave() } clearMessage() { _.debounce(() => { this.setState({ UiAlert: null }) }, 2000)() } formatTime(time) { let hour = Math.floor(time / 60) let minute = time % 60 if (hour < 10) { hour = '0' + hour } if (minute < 10) { minute = '0' + minute } return `${hour}:${minute}` } render() { const UiAlert = this.state.UiAlert const UiAlertElement = UiAlert != null ? (

{UiAlert.message}

) : null const themes = consts.THEMES const { config, codemirrorTheme } = this.state const codemirrorSampleCode = 'function iamHappy (happy) {\n\tif (happy) {\n\t console.log("I am Happy!")\n\t} else {\n\t console.log("I am not Happy!")\n\t}\n};' const enableEditRulersStyle = config.editor.enableRulers ? 'block' : 'none' const fontFamily = normalizeEditorFontFamily(config.editor.fontFamily) return (
{i18n.__('Interface')}
{i18n.__('Interface Theme')}
{i18n.__('Theme Schedule')}
{i18n.__('Scheduled Theme')}
{`End: ${this.formatTime(config.ui.scheduleEnd)}`} this.handleUIChange(e)} />
{`Start: ${this.formatTime(config.ui.scheduleStart)}`} this.handleUIChange(e)} />
00:00 24:00
{i18n.__('Language')}
{i18n.__('Default New Note')}
{global.process.platform === 'win32' ? (
) : null}
Tags
Editor
{i18n.__('Editor Theme')}
(this.codeMirrorInstance = e)} value={codemirrorSampleCode} options={{ lineNumbers: true, readOnly: true, mode: 'javascript', theme: codemirrorTheme }} />
{i18n.__('Editor Font Size')}
this.handleUIChange(e)} type='text' />
{i18n.__('Editor Font Family')}
this.handleUIChange(e)} type='text' />
{i18n.__('Editor Indent Style')}
 
{i18n.__('Editor Rulers')}
this.handleUIChange(e)} type='text' />
{i18n.__('Switch to Preview')}
{i18n.__('Editor Keymap')}

{i18n.__( '⚠️ Please restart boostnote after you change the keymap' )}

{i18n.__('Snippet Default Language')}
{i18n.__('Front matter title field')}
this.handleUIChange(e)} type='text' />
{i18n.__('Matching character pairs')}
this.handleUIChange(e)} type='text' />
{i18n.__('Matching character triples')}
this.handleUIChange(e)} type='text' />
{i18n.__('Exploding character pairs')}
this.handleUIChange(e)} type='text' />
{i18n.__('Custom MarkdownLint Rules')}
this.handleUIChange(e)} checked={this.state.config.editor.enableMarkdownLint} ref='enableMarkdownLint' type='checkbox' />   {i18n.__('Enable MarkdownLint')}
this.handleUIChange(e)} ref={e => (this.customMarkdownLintConfigCM = e)} value={config.editor.customMarkdownLintConfig} options={{ lineNumbers: true, mode: 'application/json', theme: codemirrorTheme, lint: true, gutters: [ 'CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers' ] }} />
{i18n.__('Preview')}
{i18n.__('Preview Font Size')}
this.handleUIChange(e)} type='text' />
{i18n.__('Preview Font Family')}
this.handleUIChange(e)} type='text' />
{i18n.__('Code Block Theme')}
{i18n.__('Sanitization')}
{i18n.__('LaTeX Inline Open Delimiter')}
this.handleUIChange(e)} type='text' />
{i18n.__('LaTeX Inline Close Delimiter')}
this.handleUIChange(e)} type='text' />
{i18n.__('LaTeX Block Open Delimiter')}
this.handleUIChange(e)} type='text' />
{i18n.__('LaTeX Block Close Delimiter')}
this.handleUIChange(e)} type='text' />
{i18n.__('PlantUML Server')}
this.handleUIChange(e)} type='text' />
{i18n.__('Custom CSS')}
this.handleUIChange(e)} checked={config.preview.allowCustomCSS} ref='previewAllowCustomCSS' type='checkbox' />   {i18n.__('Allow custom CSS for preview')}
this.handleUIChange(e)} ref={e => (this.customCSSCM = e)} value={config.preview.customCSS} options={{ lineNumbers: true, mode: 'css', theme: codemirrorTheme }} />
{i18n.__('Prettier Config')}
this.handleUIChange(e)} ref={e => (this.prettierConfigCM = e)} value={config.editor.prettierConfig} options={{ lineNumbers: true, mode: 'application/json', lint: true, theme: codemirrorTheme }} />
{UiAlertElement}
) } } UiTab.propTypes = { user: PropTypes.shape({ name: PropTypes.string }), dispatch: PropTypes.func, haveToSave: PropTypes.func } export default CSSModules(UiTab, styles)