1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-01-11 18:24:43 +02:00

Joplin desktop Dark Mode (#921)

* Added support for the dark mode on desktop

* Add dark highlighting to the code tags

* Update app/theme.js to be more clear and more easily support additional themes
Update more files to conform to theming
This commit is contained in:
Caleb John 2018-11-07 15:37:13 -07:00 committed by Laurent Cozic
parent 19f5a144e5
commit ee106105d8
18 changed files with 286 additions and 119 deletions

View File

@ -45,15 +45,21 @@ class ClipperConfigScreenComponent extends React.Component {
const style = this.props.style; const style = this.props.style;
const theme = themeStyle(this.props.theme); const theme = themeStyle(this.props.theme);
const headerStyle = { const headerStyle = Object.assign({}, theme.headerStyle, { width: style.width });
width: style.width,
}; const containerStyle = Object.assign({}, theme.containerStyle, {
overflowY: 'scroll',
height:style.height,
});
const buttonStyle = Object.assign({}, theme.buttonStyle, { marginRight: 10 });
const stepBoxStyle = { const stepBoxStyle = {
border: "1px solid #ccc", border: "1px solid #ccc",
padding: 15, padding: 15,
paddingTop: 0, paddingTop: 0,
marginBottom: 15, marginBottom: 15,
backgroundColor: theme.backgroundColor,
}; };
let webClipperStatusComps = []; let webClipperStatusComps = [];
@ -68,7 +74,7 @@ class ClipperConfigScreenComponent extends React.Component {
webClipperStatusComps.push(<button key="disable_button" onClick={this.disableClipperServer_click}>{_('Disable Web Clipper Service')}</button>) webClipperStatusComps.push(<button key="disable_button" onClick={this.disableClipperServer_click}>{_('Disable Web Clipper Service')}</button>)
} else { } else {
webClipperStatusComps.push(<p key="text_4" style={theme.textStyle}>{_('The web clipper service is not enabled.')}</p>) webClipperStatusComps.push(<p key="text_4" style={theme.textStyle}>{_('The web clipper service is not enabled.')}</p>)
webClipperStatusComps.push(<button key="enable_button" onClick={this.enableClipperServer_click}>{_('Enable Web Clipper Service')}</button>) webClipperStatusComps.push(<button key="enable_button" style={buttonStyle} onClick={this.enableClipperServer_click}>{_('Enable Web Clipper Service')}</button>)
} }
const apiTokenStyle = Object.assign({}, theme.textStyle, { const apiTokenStyle = Object.assign({}, theme.textStyle, {
@ -81,7 +87,7 @@ class ClipperConfigScreenComponent extends React.Component {
return ( return (
<div> <div>
<Header style={headerStyle} /> <Header style={headerStyle} />
<div style={{overflowY:'scroll', height:style.height}}> <div style={containerStyle}>
<div style={{padding: theme.margin}}> <div style={{padding: theme.margin}}>
<p style={theme.textStyle}>{_('Joplin Web Clipper allows saving web pages and screenshots from your browser to Joplin.')}</p> <p style={theme.textStyle}>{_('Joplin Web Clipper allows saving web pages and screenshots from your browser to Joplin.')}</p>
<p style={theme.textStyle}>{_('In order to use the web clipper, you need to do the following:')}</p> <p style={theme.textStyle}>{_('In order to use the web clipper, you need to do the following:')}</p>
@ -106,7 +112,7 @@ class ClipperConfigScreenComponent extends React.Component {
<div style={stepBoxStyle}> <div style={stepBoxStyle}>
<p style={theme.h1Style}>{_('Advanced options')}</p> <p style={theme.h1Style}>{_('Advanced options')}</p>
<p style={theme.textStyle}>{_('Authorisation token:')}</p> <p style={theme.textStyle}>{_('Authorisation token:')}</p>
<p style={apiTokenStyle}>{this.props.apiToken} <a href="#" onClick={this.copyToken_click}>{_('Copy token')}</a></p> <p style={apiTokenStyle}>{this.props.apiToken} <a style={theme.urlStyle} href="#" onClick={this.copyToken_click}>{_('Copy token')}</a></p>
<p style={theme.textStyle}>{_('This authorisation token is only needed to allow third-party applications to access Joplin.')}</p> <p style={theme.textStyle}>{_('This authorisation token is only needed to allow third-party applications to access Joplin.')}</p>
</div> </div>
</div> </div>
@ -128,4 +134,4 @@ const mapStateToProps = (state) => {
const ClipperConfigScreen = connect(mapStateToProps)(ClipperConfigScreenComponent); const ClipperConfigScreen = connect(mapStateToProps)(ClipperConfigScreenComponent);
module.exports = { ClipperConfigScreen }; module.exports = { ClipperConfigScreen };

View File

@ -53,10 +53,13 @@ class ConfigScreenComponent extends React.Component {
const labelStyle = Object.assign({}, theme.textStyle, { const labelStyle = Object.assign({}, theme.textStyle, {
display: 'inline-block', display: 'inline-block',
marginRight: 10, marginRight: 10,
color: theme.color,
}); });
const controlStyle = { const controlStyle = {
display: 'inline-block', display: 'inline-block',
color: theme.color,
backgroundColor: theme.backgroundColor,
}; };
const descriptionStyle = Object.assign({}, theme.textStyle, { const descriptionStyle = Object.assign({}, theme.textStyle, {
@ -120,7 +123,10 @@ class ConfigScreenComponent extends React.Component {
updateSettingValue(key, event.target.value); updateSettingValue(key, event.target.value);
} }
const inputStyle = Object.assign({}, controlStyle, { width: '50%', minWidth: '20em' }); const inputStyle = Object.assign({}, controlStyle, {
width: '50%',
minWidth: '20em',
border: '1px solid' });
const inputType = md.secure === true ? 'password' : 'text'; const inputType = md.secure === true ? 'password' : 'text';
return ( return (
@ -164,21 +170,19 @@ class ConfigScreenComponent extends React.Component {
render() { render() {
const theme = themeStyle(this.props.theme); const theme = themeStyle(this.props.theme);
const style = Object.assign({}, this.props.style, { overflow: 'auto' }); const style = Object.assign({
backgroundColor: theme.backgroundColor
}, this.props.style, { overflow: 'auto' });
const settings = this.state.settings; const settings = this.state.settings;
const headerStyle = { const headerStyle = Object.assign({}, theme.headerStyle, { width: style.width });
width: style.width,
};
const containerStyle = { const containerStyle = Object.assign({}, theme.containerStyle, { padding: 10 });
padding: 10,
};
const buttonStyle = { const buttonStyle = Object.assign({}, theme.buttonStyle, {
display: this.state.changedSettingKeys.length ? 'inline-block' : 'none', display: this.state.changedSettingKeys.length ? 'inline-block' : 'none',
marginRight: 10, marginRight: 10,
} });
const settingComps = shared.settingsToComponents(this, 'desktop', settings); const settingComps = shared.settingsToComponents(this, 'desktop', settings);
@ -195,7 +199,7 @@ class ConfigScreenComponent extends React.Component {
settingComps.push( settingComps.push(
<div key="check_sync_config_button" style={this.rowStyle_}> <div key="check_sync_config_button" style={this.rowStyle_}>
<button disabled={this.state.checkSyncConfigResult === 'checking'} onClick={this.checkSyncConfig_}>{_('Check synchronisation configuration')}</button> <button style={buttonStyle} disabled={this.state.checkSyncConfigResult === 'checking'} onClick={this.checkSyncConfig_}>{_('Check synchronisation configuration')}</button>
{ statusComp } { statusComp }
</div>); </div>);
} }
@ -228,4 +232,4 @@ const mapStateToProps = (state) => {
const ConfigScreen = connect(mapStateToProps)(ConfigScreenComponent); const ConfigScreen = connect(mapStateToProps)(ConfigScreenComponent);
module.exports = { ConfigScreen }; module.exports = { ConfigScreen };

View File

@ -28,16 +28,18 @@ class DropboxLoginScreenComponent extends React.Component {
const style = this.props.style; const style = this.props.style;
const theme = themeStyle(this.props.theme); const theme = themeStyle(this.props.theme);
const headerStyle = { const headerStyle = Object.assign({}, theme.headerStyle, { width: style.width });
width: style.width, const containerStyle = Object.assign({}, theme.containerStyle, {
}; padding: theme.margin,
height: style.height - theme.headerHeight - theme.margin * 2,
});
const inputStyle = Object.assign({}, theme.inputStyle, { width: 500 }); const inputStyle = Object.assign({}, theme.inputStyle, { width: 500 });
return ( return (
<div> <div>
<Header style={headerStyle} /> <Header style={headerStyle} />
<div style={{padding: theme.margin}}> <div style={containerStyle}>
<p style={theme.textStyle}>{_('To allow Joplin to synchronise with Dropbox, please follow the steps below:')}</p> <p style={theme.textStyle}>{_('To allow Joplin to synchronise with Dropbox, please follow the steps below:')}</p>
<p style={theme.textStyle}>{_('Step 1: Open this URL in your browser to authorise the application:')}</p> <p style={theme.textStyle}>{_('Step 1: Open this URL in your browser to authorise the application:')}</p>
<a style={theme.textStyle} href="#" onClick={this.shared_.loginUrl_click}>{this.state.loginUrl}</a> <a style={theme.textStyle} href="#" onClick={this.shared_.loginUrl_click}>{this.state.loginUrl}</a>
@ -59,4 +61,4 @@ const mapStateToProps = (state) => {
const DropboxLoginScreen = connect(mapStateToProps)(DropboxLoginScreenComponent); const DropboxLoginScreen = connect(mapStateToProps)(DropboxLoginScreenComponent);
module.exports = { DropboxLoginScreen }; module.exports = { DropboxLoginScreen };

View File

@ -50,6 +50,13 @@ class EncryptionConfigScreenComponent extends React.Component {
renderMasterKey(mk) { renderMasterKey(mk) {
const theme = themeStyle(this.props.theme); const theme = themeStyle(this.props.theme);
const passwordStyle = {
color: theme.color,
backgroundColor: theme.backgroundColor,
border: '1px solid',
borderColor: theme.dividerColor,
}
const onSaveClick = () => { const onSaveClick = () => {
return shared.onSavePasswordClick(this, mk); return shared.onSavePasswordClick(this, mk);
} }
@ -69,7 +76,7 @@ class EncryptionConfigScreenComponent extends React.Component {
<td style={theme.textStyle}>{mk.source_application}</td> <td style={theme.textStyle}>{mk.source_application}</td>
<td style={theme.textStyle}>{time.formatMsToLocal(mk.created_time)}</td> <td style={theme.textStyle}>{time.formatMsToLocal(mk.created_time)}</td>
<td style={theme.textStyle}>{time.formatMsToLocal(mk.updated_time)}</td> <td style={theme.textStyle}>{time.formatMsToLocal(mk.updated_time)}</td>
<td style={theme.textStyle}><input type="password" value={password} onChange={(event) => onPasswordChange(event)}/> <button onClick={() => onSaveClick()}>{_('Save')}</button></td> <td style={theme.textStyle}><input type="password" style={passwordStyle} value={password} onChange={(event) => onPasswordChange(event)}/> <button style={theme.buttonStyle} onClick={() => onSaveClick()}>{_('Save')}</button></td>
<td style={theme.textStyle}>{passwordOk}</td> <td style={theme.textStyle}>{passwordOk}</td>
</tr> </tr>
); );
@ -81,15 +88,13 @@ class EncryptionConfigScreenComponent extends React.Component {
const masterKeys = this.state.masterKeys; const masterKeys = this.state.masterKeys;
const containerPadding = 10; const containerPadding = 10;
const headerStyle = { const headerStyle = Object.assign({}, theme.headerStyle, { width: style.width });
width: style.width,
};
const containerStyle = { const containerStyle = Object.assign({}, theme.containerStyle, {
padding: containerPadding, padding: containerPadding,
overflow: 'auto', overflow: 'auto',
height: style.height - theme.headerHeight - containerPadding * 2, height: style.height - theme.headerHeight - containerPadding * 2,
}; });
const mkComps = []; const mkComps = [];
let nonExistingMasterKeyIds = this.props.notLoadedMasterKeys.slice(); let nonExistingMasterKeyIds = this.props.notLoadedMasterKeys.slice();
@ -126,7 +131,7 @@ class EncryptionConfigScreenComponent extends React.Component {
} }
const decryptedItemsInfo = <p style={theme.textStyle}>{shared.decryptedStatText(this)}</p>; const decryptedItemsInfo = <p style={theme.textStyle}>{shared.decryptedStatText(this)}</p>;
const toggleButton = <button onClick={() => { onToggleButtonClick() }}>{this.props.encryptionEnabled ? _('Disable encryption') : _('Enable encryption')}</button> const toggleButton = <button style={theme.buttonStyle} onClick={() => { onToggleButtonClick() }}>{this.props.encryptionEnabled ? _('Disable encryption') : _('Enable encryption')}</button>
let masterKeySection = null; let masterKeySection = null;
@ -213,4 +218,4 @@ const mapStateToProps = (state) => {
const EncryptionConfigScreen = connect(mapStateToProps)(EncryptionConfigScreenComponent); const EncryptionConfigScreen = connect(mapStateToProps)(EncryptionConfigScreenComponent);
module.exports = { EncryptionConfigScreen }; module.exports = { EncryptionConfigScreen };

View File

@ -108,6 +108,9 @@ class HeaderComponent extends React.Component {
color: style.color, color: style.color,
fontSize: style.fontSize, fontSize: style.fontSize,
fontFamily: style.fontFamily, fontFamily: style.fontFamily,
backgroundColor: style.searchColor,
border: '1px solid',
borderColor: style.dividerColor,
}; };
const searchButton = { const searchButton = {
@ -171,6 +174,8 @@ class HeaderComponent extends React.Component {
paddingLeft: theme.headerButtonHPadding, paddingLeft: theme.headerButtonHPadding,
paddingRight: theme.headerButtonHPadding, paddingRight: theme.headerButtonHPadding,
color: theme.color, color: theme.color,
searchColor: theme.backgroundColor,
dividerColor: theme.dividerColor,
textDecoration: 'none', textDecoration: 'none',
fontFamily: theme.fontFamily, fontFamily: theme.fontFamily,
fontSize: theme.fontSize, fontSize: theme.fontSize,
@ -212,4 +217,4 @@ const mapStateToProps = (state) => {
const Header = connect(mapStateToProps)(HeaderComponent); const Header = connect(mapStateToProps)(HeaderComponent);
module.exports = { Header }; module.exports = { Header };

View File

@ -35,4 +35,4 @@ class IconButton extends React.Component {
} }
module.exports = { IconButton }; module.exports = { IconButton };

View File

@ -336,14 +336,17 @@ class MainScreenComponent extends React.Component {
} }
render() { render() {
const style = this.props.style; const theme = themeStyle(this.props.theme);
const style = Object.assign({
color: theme.color,
backgroundColor: theme.backgroundColor,
}, this.props.style);
const promptOptions = this.state.promptOptions; const promptOptions = this.state.promptOptions;
const folders = this.props.folders; const folders = this.props.folders;
const notes = this.props.notes; const notes = this.props.notes;
const messageBoxVisible = this.props.hasDisabledSyncItems || this.props.showMissingMasterKeyMessage; const messageBoxVisible = this.props.hasDisabledSyncItems || this.props.showMissingMasterKeyMessage;
const sidebarVisibility = this.props.sidebarVisibility; const sidebarVisibility = this.props.sidebarVisibility;
const styles = this.styles(this.props.theme, style.width, style.height, messageBoxVisible, sidebarVisibility); const styles = this.styles(this.props.theme, style.width, style.height, messageBoxVisible, sidebarVisibility);
const theme = themeStyle(this.props.theme);
const selectedFolderId = this.props.selectedFolderId; const selectedFolderId = this.props.selectedFolderId;
const onConflictFolder = this.props.selectedFolderId === Folder.conflictFolderId(); const onConflictFolder = this.props.selectedFolderId === Folder.conflictFolderId();

View File

@ -37,6 +37,7 @@ require('brace/mode/markdown');
// https://ace.c9.io/build/kitchen-sink.html // https://ace.c9.io/build/kitchen-sink.html
// https://highlightjs.org/static/demo/ // https://highlightjs.org/static/demo/
require('brace/theme/chrome'); require('brace/theme/chrome');
require('brace/theme/twilight');
class NoteTextComponent extends React.Component { class NoteTextComponent extends React.Component {
@ -1401,6 +1402,10 @@ class NoteTextComponent extends React.Component {
paddingLeft: 8, paddingLeft: 8,
paddingRight: 8, paddingRight: 8,
marginRight: rootStyle.paddingLeft, marginRight: rootStyle.paddingLeft,
color: theme.color,
backgroundColor: theme.backgroundColor,
border: '1px solid',
borderColor: theme.dividerColor,
}; };
const toolbarStyle = { const toolbarStyle = {
@ -1432,6 +1437,9 @@ class NoteTextComponent extends React.Component {
paddingTop: paddingTop + 'px', paddingTop: paddingTop + 'px',
lineHeight: theme.textAreaLineHeight + 'px', lineHeight: theme.textAreaLineHeight + 'px',
fontSize: theme.fontSize + 'px', fontSize: theme.fontSize + 'px',
color: theme.color,
backgroundColor: theme.backgroundColor,
editorTheme: theme.editorTheme,
}; };
if (visiblePanes.indexOf('viewer') < 0) { if (visiblePanes.indexOf('viewer') < 0) {
@ -1462,7 +1470,8 @@ class NoteTextComponent extends React.Component {
const htmlHasChanged = this.lastSetHtml_ !== html; const htmlHasChanged = this.lastSetHtml_ !== html;
if (htmlHasChanged) { if (htmlHasChanged) {
this.webview_.send('setHtml', html); let options = {codeTheme: theme.codeThemeCss};
this.webview_.send('setHtml', html, options);
this.lastSetHtml_ = html; this.lastSetHtml_ = html;
} }
@ -1535,7 +1544,7 @@ class NoteTextComponent extends React.Component {
const editor = <AceEditor const editor = <AceEditor
value={body} value={body}
mode="markdown" mode="markdown"
theme="chrome" theme={editorRootStyle.editorTheme}
style={editorRootStyle} style={editorRootStyle}
width={editorStyle.width + 'px'} width={editorStyle.width + 'px'}
height={editorStyle.height + 'px'} height={editorStyle.height + 'px'}
@ -1597,4 +1606,4 @@ const mapStateToProps = (state) => {
const NoteText = connect(mapStateToProps)(NoteTextComponent); const NoteText = connect(mapStateToProps)(NoteTextComponent);
module.exports = { NoteText }; module.exports = { NoteText };

View File

@ -73,14 +73,14 @@ class OneDriveLoginScreenComponent extends React.Component {
const style = this.props.style; const style = this.props.style;
const theme = themeStyle(this.props.theme); const theme = themeStyle(this.props.theme);
const headerStyle = { const headerStyle = Object.assign({}, theme.headerStyle, { width: style.width });
width: style.width,
};
const webviewStyle = { const webviewStyle = {
width: this.props.style.width, width: this.props.style.width,
height: this.props.style.height - theme.headerHeight, height: this.props.style.height - theme.headerHeight,
overflow: 'hidden', overflow: 'hidden',
color: theme.color,
backgroundColor: theme.backgroundColor,
}; };
const headerButtons = [ const headerButtons = [
@ -109,4 +109,4 @@ const mapStateToProps = (state) => {
const OneDriveLoginScreen = connect(mapStateToProps)(OneDriveLoginScreenComponent); const OneDriveLoginScreen = connect(mapStateToProps)(OneDriveLoginScreenComponent);
module.exports = { OneDriveLoginScreen }; module.exports = { OneDriveLoginScreen };

View File

@ -59,7 +59,7 @@ class PromptDialog extends React.Component {
}; };
this.styles_.promptDialog = { this.styles_.promptDialog = {
backgroundColor: 'white', backgroundColor: theme.backgroundColor,
padding: 16, padding: 16,
display: 'inline-block', display: 'inline-block',
boxShadow: '6px 6px 20px rgba(0,0,0,0.5)', boxShadow: '6px 6px 20px rgba(0,0,0,0.5)',
@ -69,6 +69,10 @@ class PromptDialog extends React.Component {
minWidth: theme.buttonMinWidth, minWidth: theme.buttonMinWidth,
minHeight: theme.buttonMinHeight, minHeight: theme.buttonMinHeight,
marginLeft: 5, marginLeft: 5,
color: theme.color,
backgroundColor: theme.backgroundColor,
border: '1px solid',
borderColor: theme.dividerColor,
}; };
this.styles_.label = { this.styles_.label = {
@ -82,6 +86,10 @@ class PromptDialog extends React.Component {
this.styles_.input = { this.styles_.input = {
width: 0.5 * width, width: 0.5 * width,
maxWidth: 400, maxWidth: 400,
color: theme.color,
backgroundColor: theme.backgroundColor,
border: '1px solid',
borderColor: theme.dividerColor,
}; };
this.styles_.desc = Object.assign({}, theme.textStyle, { this.styles_.desc = Object.assign({}, theme.textStyle, {
@ -142,6 +150,7 @@ class PromptDialog extends React.Component {
if (this.props.inputType === 'datetime') { if (this.props.inputType === 'datetime') {
inputComp = <Datetime inputComp = <Datetime
value={this.state.answer} value={this.state.answer}
inputProps={{style: styles.input}}
dateFormat={time.dateFormat()} dateFormat={time.dateFormat()}
timeFormat={time.timeFormat()} timeFormat={time.timeFormat()}
onChange={(momentObject) => onDateTimeChange(momentObject)} onChange={(momentObject) => onDateTimeChange(momentObject)}
@ -166,7 +175,7 @@ class PromptDialog extends React.Component {
<div style={styles.modalLayer}> <div style={styles.modalLayer}>
<div style={styles.promptDialog}> <div style={styles.promptDialog}>
<label style={styles.label}>{this.props.label ? this.props.label : ''}</label> <label style={styles.label}>{this.props.label ? this.props.label : ''}</label>
<div style={{display: 'inline-block'}}> <div style={{display: 'inline-block', color: 'black', backgroundColor: theme.backgroundColor}}>
{inputComp} {inputComp}
{descComp} {descComp}
</div> </div>
@ -180,4 +189,4 @@ class PromptDialog extends React.Component {
} }
module.exports = { PromptDialog }; module.exports = { PromptDialog };

View File

@ -47,17 +47,14 @@ class StatusScreenComponent extends React.Component {
const theme = themeStyle(this.props.theme); const theme = themeStyle(this.props.theme);
const style = this.props.style; const style = this.props.style;
const headerStyle = { const headerStyle = Object.assign({}, theme.headerStyle, { width: style.width });
width: style.width,
};
const containerPadding = 10; const containerPadding = 10;
const containerStyle = { const containerStyle = Object.assign({}, theme.containerStyle, {
padding: containerPadding, padding: containerPadding,
overflowY: 'auto',
height: style.height - theme.headerHeight - containerPadding * 2, height: style.height - theme.headerHeight - containerPadding * 2,
}; });
function renderSectionTitleHtml(key, title) { function renderSectionTitleHtml(key, title) {
return <h2 key={'section_' + key} style={theme.h2Style}>{title}</h2> return <h2 key={'section_' + key} style={theme.h2Style}>{title}</h2>
@ -134,4 +131,4 @@ const mapStateToProps = (state) => {
const StatusScreen = connect(mapStateToProps)(StatusScreenComponent); const StatusScreen = connect(mapStateToProps)(StatusScreenComponent);
module.exports = { StatusScreen }; module.exports = { StatusScreen };

View File

@ -30,4 +30,4 @@ class Dialogs {
const dialogs = new Dialogs(); const dialogs = new Dialogs();
module.exports = dialogs; module.exports = dialogs;

View File

@ -59,7 +59,7 @@
let hljsScriptAdded = false; let hljsScriptAdded = false;
let hljsLoaded = false; let hljsLoaded = false;
function loadHljs(callback) { function loadHljs(options) {
hljsScriptAdded = true; hljsScriptAdded = true;
const script = document.createElement('script'); const script = document.createElement('script');
@ -74,16 +74,16 @@
link.rel = 'stylesheet'; link.rel = 'stylesheet';
// https://ace.c9.io/build/kitchen-sink.html // https://ace.c9.io/build/kitchen-sink.html
// https://highlightjs.org/static/demo/ // https://highlightjs.org/static/demo/
link.href = 'highlight/styles/atom-one-light.css'; link.href = 'highlight/styles/' + options.codeTheme;
document.getElementById('hlScriptContainer').appendChild(link); document.getElementById('hlScriptContainer').appendChild(link);
} }
function loadAndApplyHljs() { function loadAndApplyHljs(options) {
var codeElements = document.getElementsByClassName('code'); var codeElements = document.getElementsByClassName('code');
if (!codeElements.length) return; if (!codeElements.length) return;
if (!hljsScriptAdded) { if (!hljsScriptAdded) {
this.loadHljs(); this.loadHljs(options);
return; return;
} }
@ -142,7 +142,7 @@
contentElement.innerHTML = html; contentElement.innerHTML = html;
loadAndApplyHljs(); loadAndApplyHljs(event.options);
// Remove the bullet from "ul" for checkbox lists and extra padding // Remove the bullet from "ul" for checkbox lists and extra padding
// const checkboxes = document.getElementsByClassName('checkbox'); // const checkboxes = document.getElementsByClassName('checkbox');
@ -319,4 +319,4 @@
</script> </script>
</body> </body>
</html> </html>

View File

@ -5,8 +5,8 @@
const ipcRenderer = require('electron').ipcRenderer; const ipcRenderer = require('electron').ipcRenderer;
ipcRenderer.on('setHtml', (event, html) => { ipcRenderer.on('setHtml', (event, html, options) => {
window.postMessage({ target: 'webview', name: 'setHtml', data: { html: html } }, '*'); window.postMessage({ target: 'webview', name: 'setHtml', data: { html: html, options: options } }, '*');
}); });
ipcRenderer.on('setPercentScroll', (event, percent) => { ipcRenderer.on('setPercentScroll', (event, percent) => {
@ -33,4 +33,4 @@ window.addEventListener('message', (event) => {
} else { } else {
throw new Error('Unsupported number of args'); throw new Error('Unsupported number of args');
} }
}); });

View File

@ -81,4 +81,4 @@ table td, table th {
.note-property-box .rdt { .note-property-box .rdt {
display: inline-block; display: inline-block;
} }

View File

@ -2,55 +2,30 @@ const Setting = require('lib/models/Setting.js');
const zoomRatio = Setting.value('style.zoom') / 100; const zoomRatio = Setting.value('style.zoom') / 100;
// globalStyle should be used for properties that do not change across themes
// i.e. should not be used for colors
const globalStyle = { const globalStyle = {
fontSize: Math.round(12 * zoomRatio), fontSize: Math.round(12 * zoomRatio),
fontFamily: 'sans-serif', fontFamily: 'sans-serif',
margin: 15, // No text and no interactive component should be within this margin margin: 15, // No text and no interactive component should be within this margin
itemMarginTop: 10, itemMarginTop: 10,
itemMarginBottom: 10, itemMarginBottom: 10,
backgroundColor: "#ffffff",
backgroundColorTransparent: 'rgba(255,255,255,0.9)',
oddBackgroundColor: "#dddddd",
color: "#222222", // For regular text
colorError: "red",
colorWarn: "#9A5B00",
colorFaded: "#777777", // For less important text
fontSizeSmaller: 14, fontSizeSmaller: 14,
dividerColor: "#dddddd",
selectedColor: '#e5e5e5',
disabledOpacity: 0.3, disabledOpacity: 0.3,
buttonMinWidth: 50, buttonMinWidth: 50,
buttonMinHeight: 30, buttonMinHeight: 30,
textAreaLineHeight: 17, textAreaLineHeight: 17,
//backgroundColor2: "#2B2634",
backgroundColor2: "#162B3D",
color2: "#ffffff",
//selectedColor2: "#5A4D70",
selectedColor2: "#0269C2",
colorError2: "#ff6c6c",
warningBackgroundColor: "#FFD08D",
headerHeight: 35, headerHeight: 35,
headerButtonHPadding: 6, headerButtonHPadding: 6,
toolbarHeight: 35, toolbarHeight: 35,
raisedBackgroundColor: "#0080EF",
raisedColor: "#003363",
raisedHighlightedColor: "#ffffff",
tagItemPadding: 3, tagItemPadding: 3,
tagBackgroundColor: '#e5e5e5' tagBackgroundColor: '#e5e5e5',
}; };
// For WebView - must correspond to the properties above // For WebView - must correspond to the properties above
globalStyle.htmlFontSize = globalStyle.fontSize + 'px'; globalStyle.htmlFontSize = globalStyle.fontSize + 'px';
globalStyle.htmlColor ='black'; // Note: CSS in WebView component only supports named colors or rgb() notation
globalStyle.htmlBackgroundColor ='white';
globalStyle.htmlDividerColor = 'rgb(150,150,150)';
globalStyle.htmlLinkColor ='blue';
globalStyle.htmlLineHeight = Math.round(20 * zoomRatio) + 'px'; globalStyle.htmlLineHeight = Math.round(20 * zoomRatio) + 'px';
globalStyle.marginRight = globalStyle.margin; globalStyle.marginRight = globalStyle.margin;
@ -60,28 +35,22 @@ globalStyle.marginBottom = globalStyle.margin;
globalStyle.htmlMarginLeft = ((globalStyle.marginLeft / 10) * 0.6).toFixed(2) + 'em'; globalStyle.htmlMarginLeft = ((globalStyle.marginLeft / 10) * 0.6).toFixed(2) + 'em';
globalStyle.icon = { globalStyle.icon = {
color: globalStyle.color,
fontSize: 30, fontSize: 30,
}; };
globalStyle.lineInput = { globalStyle.lineInput = {
color: globalStyle.color,
backgroundColor: globalStyle.backgroundColor,
fontFamily: globalStyle.fontFamily, fontFamily: globalStyle.fontFamily,
}; };
globalStyle.textStyle = { globalStyle.textStyle = {
color: globalStyle.color,
fontFamily: globalStyle.fontFamily, fontFamily: globalStyle.fontFamily,
fontSize: globalStyle.fontSize, fontSize: globalStyle.fontSize,
lineHeight: '1.6em', lineHeight: '1.6em',
}; };
globalStyle.textStyle2 = Object.assign({}, globalStyle.textStyle, { globalStyle.textStyle2 = Object.assign({}, globalStyle.textStyle, {});
color: globalStyle.color2,
});
globalStyle.urlStyle = Object.assign({}, globalStyle.textStyle, { color: "#155BDA", textDecoration: 'underline' }); globalStyle.urlStyle = Object.assign({}, globalStyle.textStyle, { textDecoration: 'underline' });
globalStyle.h1Style = Object.assign({}, globalStyle.textStyle); globalStyle.h1Style = Object.assign({}, globalStyle.textStyle);
globalStyle.h1Style.fontSize *= 1.5; globalStyle.h1Style.fontSize *= 1.5;
@ -98,7 +67,6 @@ globalStyle.toolbarStyle = {
alignItems: 'center', alignItems: 'center',
paddingLeft: globalStyle.headerButtonHPadding, paddingLeft: globalStyle.headerButtonHPadding,
paddingRight: globalStyle.headerButtonHPadding, paddingRight: globalStyle.headerButtonHPadding,
color: globalStyle.color,
textDecoration: 'none', textDecoration: 'none',
fontFamily: globalStyle.fontFamily, fontFamily: globalStyle.fontFamily,
fontSize: globalStyle.fontSize, fontSize: globalStyle.fontSize,
@ -107,6 +75,97 @@ globalStyle.toolbarStyle = {
justifyContent: 'center', justifyContent: 'center',
}; };
globalStyle.headerStyle = {};
globalStyle.inputStyle = {
border: '1px solid',
};
globalStyle.containerStyle = {
overflow: 'auto',
overflowY: 'auto',
};
globalStyle.buttonStyle = {
marginRight: 10,
border: '1px solid',
};
const lightStyle = {
backgroundColor: "#ffffff",
backgroundColorTransparent: 'rgba(255,255,255,0.9)',
oddBackgroundColor: "#dddddd",
color: "#222222", // For regular text
colorError: "red",
colorWarn: "#9A5B00",
colorFaded: "#777777", // For less important text
dividerColor: "#dddddd",
selectedColor: '#e5e5e5',
urlColor: '#155BDA',
backgroundColor2: "#162B3D",
color2: "#ffffff",
selectedColor2: "#0269C2",
colorError2: "#ff6c6c",
raisedBackgroundColor: "#0080EF",
raisedColor: "#003363",
raisedHighlightedColor: "#ffffff",
warningBackgroundColor: "#FFD08D",
codeColor: "#EFF0F1",
codeBorderColor: '#CBCBCB',
htmlColor:'black', // Note: CSS in WebView component only supports named colors or rgb() notation
htmlBackgroundColor: 'white',
htmlDividerColor: 'rgb(150,150,150)',
htmlLinkColor: 'blue',
htmlCodeColor: 'rgb(239, 240, 241)',
htmlCodeBorderColor: 'rgb(203, 203, 203)',
editorTheme: "chrome",
codeThemeCss: "atom-one-light.css",
};
const darkStyle = {
backgroundColor: '#1D2024',
backgroundColorTransparent: 'rgba(255,255,255,0.9)',
oddBackgroundColor: "#dddddd",
color: '#dddddd',
colorFaded: '#777777',
colorError: "red",
colorWarn: "#9A5B00",
colorFaded: "#777777", // For less important text
dividerColor: '#555555',
selectedColor: '#333333',
urlColor: '#4E87EE',
backgroundColor2: "#162B3D",
color2: "#ffffff",
selectedColor2: "#0269C2",
colorError2: "#ff6c6c",
raisedBackgroundColor: "#0F2051",
raisedColor: "#788BC3",
raisedHighlightedColor: "#ffffff",
warningBackgroundColor: "#CC6600",
codeColor: "#2F3031",
codeBorderColor: '#464646',
htmlColor: 'rgb(220,220,220)', // Note: CSS in WebView component only supports named colors or rgb() notation
htmlBackgroundColor: 'rgb(29,32,36)',
htmlDividerColor: 'rgb(150,150,150)',
htmlLinkColor: 'rgb(166,166,255)',
htmlCodeColor: 'rgb(47, 48, 49)',
htmlCodeBorderColor: 'rgb(70, 70, 70)',
editorTheme: 'twilight',
codeThemeCss: "atom-one-dark-reasonable.css",
};
globalStyle.tagStyle = { globalStyle.tagStyle = {
fontSize: globalStyle.fontSize, fontSize: globalStyle.fontSize,
fontFamily: globalStyle.fontFamily, fontFamily: globalStyle.fontFamily,
@ -126,25 +185,93 @@ function themeStyle(theme) {
if (!theme) throw new Error('Theme must be specified'); if (!theme) throw new Error('Theme must be specified');
if (themeCache_[theme]) return themeCache_[theme]; if (themeCache_[theme]) return themeCache_[theme];
let output = Object.assign({}, globalStyle); let output = {};
if (theme == Setting.THEME_LIGHT) return output; if (theme == Setting.THEME_LIGHT) {
output = Object.assign({}, globalStyle, lightStyle);
}
else if (theme == Setting.THEME_DARK) {
output = Object.assign({}, globalStyle, darkStyle);
}
output.backgroundColor = '#1D2024'; output.textStyle = Object.assign({},
output.color = '#dddddd'; output.textStyle,
output.colorFaded = '#777777'; { color: output.color }
output.dividerColor = '#555555'; );
output.selectedColor = '#333333';
output.raisedBackgroundColor = "#0F2051"; output.icon = Object.assign({},
output.raisedColor = "#788BC3"; output.icon,
output.raisedHighlightedColor = "#ffffff"; { color: output.color }
);
output.htmlColor = 'rgb(220,220,220)'; output.lineInput = Object.assign({},
output.htmlBackgroundColor = 'rgb(29,32,36)'; output.lineInput,
output.htmlLinkColor = 'rgb(166,166,255)'; {
color: output.color,
backgroundColor: output.backgroundColor,
}
);
output.textStyle2 = Object.assign({},
output.textStyle2,
{ color: output.color2, }
);
output.urlStyle = Object.assign({},
output.urlStyle,
{ color: output.urlColor }
);
output.h1Style = Object.assign({},
output.h1Style,
{ color: output.color }
);
output.h2Style = Object.assign({},
output.h2Style,
{ color: output.color }
);
output.toolbarStyle = Object.assign({},
output.toolbarStyle,
{ color: output.color }
);
output.headerStyle = Object.assign({},
output.headerStyle,
{
color: output.color,
backgroundColor: output.backgroundColor,
}
);
output.inputStyle = Object.assign({},
output.inputStyle,
{
color: output.color,
backgroundColor: output.backgroundColor,
borderColor: output.dividerColor,
}
);
output.containerStyle = Object.assign({},
output.containerStyle,
{
color: output.color,
backgroundColor: output.backgroundColor,
}
);
output.buttonStyle = Object.assign({},
output.buttonStyle,
{
color: output.color,
backgroundColor: output.backgroundColor,
borderColor: output.dividerColor,
}
);
themeCache_[theme] = output; themeCache_[theme] = output;
return themeCache_[theme]; return themeCache_[theme];
} }
module.exports = { themeStyle }; module.exports = { themeStyle };

View File

@ -599,8 +599,8 @@ class MdToHtml {
max-width: 100%; max-width: 100%;
} }
.inline-code { .inline-code {
border: 1px solid #CBCBCB; border: 1px solid ` + style.htmlCodeBorderColor + `;
background-color: #eff0f1; background-color: ` + style.htmlCodeColor + `;
padding-right: .2em; padding-right: .2em;
padding-left: .2em; padding-left: .2em;
} }

View File

@ -53,7 +53,7 @@ class Setting extends BaseModel {
options[Setting.TIME_FORMAT_2] = time.formatMsToLocal(now, Setting.TIME_FORMAT_2); options[Setting.TIME_FORMAT_2] = time.formatMsToLocal(now, Setting.TIME_FORMAT_2);
return options; return options;
}}, }},
'theme': { value: Setting.THEME_LIGHT, type: Setting.TYPE_INT, public: true, appTypes: ['mobile'], isEnum: true, label: () => _('Theme'), options: () => { 'theme': { value: Setting.THEME_LIGHT, type: Setting.TYPE_INT, public: true, appTypes: ['mobile', 'desktop'], isEnum: true, label: () => _('Theme'), options: () => {
let output = {}; let output = {};
output[Setting.THEME_LIGHT] = _('Light'); output[Setting.THEME_LIGHT] = _('Light');
output[Setting.THEME_DARK] = _('Dark'); output[Setting.THEME_DARK] = _('Dark');
@ -576,4 +576,4 @@ Setting.constants_ = {
Setting.autoSaveEnabled = true; Setting.autoSaveEnabled = true;
module.exports = Setting; module.exports = Setting;