diff --git a/app/editor/Pipe.js b/app/editor/Pipe.js index 330d50e..f289d54 100644 --- a/app/editor/Pipe.js +++ b/app/editor/Pipe.js @@ -2,7 +2,7 @@ import { ipcRenderer as ipc } from 'electron'; import { saveToFile } from '../main/utils/file'; // Actions -import { exportPdf, exportPdfComplete } from './actions'; +import { exportPdf, exportPdfComplete, setTotalPages } from './actions'; export default class Pipe { constructor({ dispatch, uuid }) { @@ -14,6 +14,7 @@ export default class Pipe { return { publishPdf: this.publishPdf, responsePdfOptions: this.responsePdfOptions, + pageData: this.pageData, }; } @@ -41,6 +42,10 @@ export default class Pipe { setTimeout(startPublish, 1000); } + pageData = (event, rulers) => { + const pages = rulers.length + 1; + this.dispatch(setTotalPages(this.uuid, pages, rulers)); + } // Connect and disconnect helpers @@ -58,6 +63,6 @@ export default class Pipe { } disconnect() { - // TODO + this.webview.removeEventListener('ipc-message'); } } diff --git a/app/editor/actions.js b/app/editor/actions.js index c3d3061..bd4ae6c 100644 --- a/app/editor/actions.js +++ b/app/editor/actions.js @@ -5,6 +5,10 @@ import { EDITOR_LOADING, EXPORT_PDF, EXPORT_PDF_COMPLETE, + PRESENTATION_MODE, + SET_CURRENT_PAGE, + SET_TOTAL_PAGES, + VIEW_MODE, } from '../store/actionTypes'; import { loadFromFile, saveToFile } from '../main/utils/file'; @@ -51,6 +55,39 @@ export function exportPdf(uuid) { ]); } +export function presentationMode(uuid, presenting) { + return { + type: PRESENTATION_MODE, + uuid, + presenting, + }; +} + +export function setCurrentPage(uuid, page) { + return { + type: SET_CURRENT_PAGE, + uuid, + page, + }; +} + +export function setTotalPages(uuid, pages, rulers = []) { + return { + type: SET_TOTAL_PAGES, + uuid, + pages, + rulers, + }; +} + +export function setViewMode(uuid, mode) { + return { + type: VIEW_MODE, + uuid, + mode, + }; +} + export function exportPdfComplete(uuid, file) { return (dispatch) => Promise.all([ dispatch(isLoading(uuid, false)), diff --git a/app/editor/containers/Editor.js b/app/editor/containers/Editor.js index 8422aa4..f14930d 100644 --- a/app/editor/containers/Editor.js +++ b/app/editor/containers/Editor.js @@ -10,7 +10,7 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; // Actions -import { updateCode } from '../actions'; +import { updateCode, setCurrentPage } from '../actions'; // Selectors import { getEditor } from '../selectors'; @@ -22,7 +22,10 @@ class Editor extends Component { code: PropTypes.string, file: PropTypes.string, saved: PropTypes.bool, + presenting: PropTypes.bool, + rulers: PropTypes.array, updateCode: PropTypes.func, + setCurrentPage: PropTypes.func, } constructor(props) { @@ -35,6 +38,13 @@ class Editor extends Component { }; } + componentDidMount() { + this.codeMirror.on( + 'cursorActivity', + () => setTimeout(this.refreshPage, 5) + ); + } + componentWillReceiveProps(nextProps) { if (this.props.file !== nextProps.file) { this.setState({ @@ -58,9 +68,24 @@ class Editor extends Component { this.props.updateCode(UUID, code); } + refreshPage = () => { + if (this.props.presenting || !this.codeMirror) { + return; + } + + const lineNumber = this.codeMirror.getCursor().line || 0; + const line = this.props.rulers.filter((ln) => ln <= lineNumber); + const page = line.length + 1; + + this.props.setCurrentPage(UUID, page); + + // TODO: Update page indicator `Page ${currentPage} / ${totalPages}` + } + render() { return ( code && (this.codeMirror = code.getCodeMirror())} value={this.state.code} onChange={this.onCodeChange} options={{ @@ -85,9 +110,12 @@ export default connect( file: editor.file, code: editor.code, saved: editor.saved, + rulers: editor.pageRulers, + presenting: editor.presenting, }; }, dispatch => bindActionCreators({ updateCode, + setCurrentPage, }, dispatch) )(Editor); diff --git a/app/editor/containers/MainPanel.js b/app/editor/containers/MainPanel.js index 440b865..665393b 100644 --- a/app/editor/containers/MainPanel.js +++ b/app/editor/containers/MainPanel.js @@ -1,11 +1,16 @@ import { remote } from 'electron'; import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; import SplitPane from 'react-split-pane'; import Editor from '../../editor/containers/Editor'; import Pipe from '../Pipe'; +// Actions +import { setCurrentPage, presentationMode, setViewMode } from '../actions'; + +// Selectors import { getEditor } from '../selectors'; import loaderSvg from '../../../assets/loading.svg'; @@ -14,13 +19,17 @@ class MainPanel extends Component { static propTypes = { pipe: PropTypes.object, exporting: PropTypes.bool, + presenting: PropTypes.bool, + currentPage: PropTypes.number, + totalPages: PropTypes.number, + viewMode: PropTypes.string, + presentationMode: PropTypes.func, + setCurrentPage: PropTypes.func, + setViewMode: PropTypes.func, } componentDidMount() { - this.webview = this.div.childNodes[0]; - global.webview = this.webview; - - this.props.pipe.connect(this.webview); + document.addEventListener('keydown', this.keyPress); } componentWillReceiveProps(newProps) { @@ -31,6 +40,49 @@ class MainPanel extends Component { componentWillUnmount() { this.props.pipe.disconnect(); + document.removeEventListener('keydown', this.keyPress); + } + + keyPress = (event) => { + if (!this.props.presenting) { + return; + } + const code = event.keyCode; + if (code === 27) { + this.props.presentationMode(false); + } else if (code === 37 || code === 39) { + const diff = code === 37 ? -1 : 1; + const page = Math.max(1, Math.min(this.props.totalPages, this.props.currentPage + diff)); + this.props.setCurrentPage(page); + } + } + + handleWebview = (div) => { + if (div) { + this.webview = div.childNodes[0]; + global.webview = this.webview; + + this.props.pipe.connect(this.webview); + } + } + + toolbarMenu() { + const { Menu, MenuItem } = remote; + + const menu = new Menu(); + menu.append(new MenuItem({ + label: 'Continue Presentation', + click: () => { + this.props.presentationMode(true); + } + })); + + menu.popup(remote.getCurrentWindow()); + } + + beginPresentation() { + this.props.setCurrentPage(1); + this.props.presentationMode(true); } renderWebview() { @@ -45,7 +97,7 @@ class MainPanel extends Component { return (
(this.div = d)} + ref={this.handleWebview} style={{ height: '100%' }} dangerouslySetInnerHTML={{ __html: webview }} /> @@ -60,13 +112,75 @@ class MainPanel extends Component { ); } + renderPanels() { + const presenting = this.props.presenting ? { + pane1Style: { display: 'none' }, + resizerStyle: { display: 'none' }, + allowResize: false, + } : {}; + + const style = { + position: 'static', + }; + + return ( + + + { this.renderWebview() } + + ); + } + + renderToolbar() { + if (this.props.presenting) { + return null; + } + + const modes = [ + { + icon: 'icon-monitor', + key: 'screen', + }, + { + icon: 'icon-doc-text', + key: 'list', + } + ].map(({ icon, key }) => { + const active = key === this.props.viewMode ? ' active' : ''; + return ( + + ); + }); + + return ( +
+
+
+ + +
+
+ { modes } +
+
+
+ ); + } + render() { return ( -
- - - { this.renderWebview() } - +
+ { this.renderPanels() } + { this.renderToolbar() } { this.props.exporting ? this.renderLoading() : null }
); @@ -79,11 +193,18 @@ export default connect( const editor = getEditor(uuid)(state); return { exporting: (editor.export || {}).loading || false, + presenting: editor.presenting || false, + viewMode: editor.viewMode || 'screen', + currentPage: editor.currentPage || 1, + totalPages: editor.totalPages || 1, }; }, - dispatch => { - return { - pipe: new Pipe({ dispatch, uuid }), - }; - } + (dispatch) => ({ + pipe: new Pipe({ dispatch, uuid }), + ...bindActionCreators({ + setCurrentPage: setCurrentPage.bind(null, uuid), + presentationMode: presentationMode.bind(null, uuid), + setViewMode: setViewMode.bind(null, uuid), + }, dispatch) + }) )(MainPanel); diff --git a/app/editor/reducer.js b/app/editor/reducer.js index f653481..1f075f1 100644 --- a/app/editor/reducer.js +++ b/app/editor/reducer.js @@ -6,6 +6,10 @@ import { EDITOR_LOADING, EXPORT_PDF, EXPORT_PDF_COMPLETE, + PRESENTATION_MODE, + SET_CURRENT_PAGE, + SET_TOTAL_PAGES, + VIEW_MODE, } from '../store/actionTypes'; const saveUuidState = (state, uuid) => (data) => ({ @@ -49,6 +53,23 @@ export default function editor(state = {}, action) { file: action.file, } }); + case PRESENTATION_MODE: + return save({ + presenting: action.presenting, + }); + case SET_CURRENT_PAGE: + return save({ + currentPage: action.page, + }); + case SET_TOTAL_PAGES: + return save({ + totalPages: action.pages, + pageRulers: action.rulers, + }); + case VIEW_MODE: + return save({ + viewMode: action.mode, + }); case WINDOW_CLOSED: { const copy = { ...state }; delete copy[uuid]; diff --git a/app/main/Window.js b/app/main/Window.js index 3432983..ad623f2 100644 --- a/app/main/Window.js +++ b/app/main/Window.js @@ -1,11 +1,11 @@ import { BrowserWindow, Menu, dialog, app, ipcRenderer as ipc } from 'electron'; import path from 'path'; -import { getAppPath, saveToFile } from './utils/file'; +import { getAppPath } from './utils/file'; import guid from './utils/guid'; import createMenu from './createMenu'; -import { loadFile, saveFile } from '../editor/actions'; +import { loadFile, saveFile, presentationMode } from '../editor/actions'; import { windowClosed } from './actions'; import { getEditor } from '../editor/selectors'; @@ -194,6 +194,29 @@ export default class Window { )); } + exportPdfDialog = () => { + dialog.showSaveDialog( + this.window, + { + title: 'Export to PDF...', + filters: [{ name: 'PDF file', extensions: ['pdf'] }], + }, + (filename) => { + if (!filename) return; + // @freeze = true + this.send('publishPdf', { filename }); + } + ); + } + + presentationMode = () => { + const editor = this.getEditorState(); + this.dispatch(presentationMode( + this.uuid, + !editor.presenting + )); + } + static loadFile(currentWindow) { const onOpen = (fnames) => { if (!fnames) return; @@ -226,19 +249,4 @@ export default class Window { ); } - exportPdfDialog = () => { - dialog.showSaveDialog( - this.window, - { - title: 'Export to PDF...', - filters: [{ name: 'PDF file', extensions: ['pdf'] }], - }, - (filename) => { - if (!filename) return; - // @freeze = true - this.send('publishPdf', { filename }); - } - ); - }; - } diff --git a/app/main/createMenu.js b/app/main/createMenu.js index cbf98ca..04549e4 100644 --- a/app/main/createMenu.js +++ b/app/main/createMenu.js @@ -43,6 +43,12 @@ export default function createMenu({ focusedWindow && focusedWindow.getClass().saveFile(true), }, { type: 'separator' }, + { + label: 'Presentation Mode', + accelerator: 'CmdOrCtrl+P', + click: (item, focusedWindow) => + focusedWindow && focusedWindow.getClass().presentationMode(), + }, { label: 'Export to PDF...', accelerator: 'Shift+CmdOrCtrl+E', diff --git a/app/main/utils/markdown.js b/app/main/utils/markdown.js index 234c617..2e49a88 100644 --- a/app/main/utils/markdown.js +++ b/app/main/utils/markdown.js @@ -61,6 +61,10 @@ export default class Markdown { return this.settings; } + getRulers() { + return this.rulers; + } + render(code) { this.rulers = []; return this.postRender([ diff --git a/app/store/actionTypes.js b/app/store/actionTypes.js index a409383..6043956 100644 --- a/app/store/actionTypes.js +++ b/app/store/actionTypes.js @@ -8,6 +8,11 @@ export const EXPORT_PDF = 'EXPORT_PDF'; export const EXPORT_PDF_COMPLETE = 'EXPORT_PDF_COMPLETE'; export const EXPORT_PDF_FAILED = 'EXPORT_PDF_FAILED'; +export const PRESENTATION_MODE = 'PRESENTATION_MODE'; +export const SET_CURRENT_PAGE = 'SET_CURRENT_PAGE'; +export const SET_TOTAL_PAGES = 'SET_TOTAL_PAGES'; +export const VIEW_MODE = 'VIEW_MODE'; + export const WILL_QUIT = 'WILL_QUIT'; export const QUIT_CANCELLED = 'QUIT_CANCELLED'; diff --git a/app/styles/_layout.css b/app/styles/_layout.css index 97f8c51..1e2603b 100644 --- a/app/styles/_layout.css +++ b/app/styles/_layout.css @@ -14,6 +14,21 @@ } } +.window-app-box { + display: flex; + align-items: stretch; + flex-direction: column; + height: 100vh; + + .toolbar-footer { + + } +} + +.SplitPane.vertical { + position: static !important; +} + .Resizer { background: #000; opacity: .2; diff --git a/app/styles/app.css b/app/styles/app.css index 8055139..1d83944 100644 --- a/app/styles/app.css +++ b/app/styles/app.css @@ -7,5 +7,6 @@ body { overflow-y: hidden; } -@import 'layout'; -@import 'editor'; +@import "ui/photon.css"; +@import "layout"; +@import "editor"; diff --git a/app/styles/fonts/photon-entypo.eot b/app/styles/fonts/photon-entypo.eot new file mode 100644 index 0000000..f70d126 Binary files /dev/null and b/app/styles/fonts/photon-entypo.eot differ diff --git a/app/styles/fonts/photon-entypo.svg b/app/styles/fonts/photon-entypo.svg new file mode 100644 index 0000000..9256ea8 --- /dev/null +++ b/app/styles/fonts/photon-entypo.svg @@ -0,0 +1,295 @@ + + + +Copyright (C) 2015 by original authors @ fontello.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/styles/fonts/photon-entypo.ttf b/app/styles/fonts/photon-entypo.ttf new file mode 100644 index 0000000..7f765b3 Binary files /dev/null and b/app/styles/fonts/photon-entypo.ttf differ diff --git a/app/styles/fonts/photon-entypo.woff b/app/styles/fonts/photon-entypo.woff new file mode 100644 index 0000000..87860e5 Binary files /dev/null and b/app/styles/fonts/photon-entypo.woff differ diff --git a/app/styles/ui/bars.css b/app/styles/ui/bars.css new file mode 100644 index 0000000..a9918c9 --- /dev/null +++ b/app/styles/ui/bars.css @@ -0,0 +1,55 @@ +/* Bars.css */ +/* -------------------------------------------------- */ + +.toolbar { + min-height: 22px; + box-shadow: inset 0 1px 0 #f5f4f5; + @mixin linear-gradient #e8e6e8, #d1cfd1; + @mixin clearfix; +} + +.toolbar-header { + border-bottom: 1px solid $dark-border-color; + + .title { + margin-top: 1px; + } +} + +.toolbar-footer { + border-top: 1px solid $dark-border-color; + -webkit-app-region: drag; +} + +/* Simple centered title to go in the toolbar */ +.title { + margin: 0; + font-size: 12px; + font-weight: 400; + text-align: center; + color: #555; + cursor: default; +} + +/* Borderless toolbar for the clean look */ +.toolbar-borderless { + border-top: 0; + border-bottom: 0; +} + +/* Buttons in toolbars */ +.toolbar-actions { + margin-top: 4px; + margin-bottom: 3px; + padding-right: 3px; + padding-left: 3px; + padding-bottom: 3px; + -webkit-app-region: drag; + @include clearfix; + + > .btn, + > .btn-group { + margin-left: 4px; + margin-right: 4px; + } +} diff --git a/app/styles/ui/button-groups.css b/app/styles/ui/button-groups.css new file mode 100644 index 0000000..647d2c8 --- /dev/null +++ b/app/styles/ui/button-groups.css @@ -0,0 +1,71 @@ +/* Button-groups.css */ +/* Adapted from Bootstrap's button-groups.less (https://github.com/twbs/bootstrap/blob/master/less/button-groups.less) */ +/* -------------------------------------------------- */ + +/* Button groups */ +.btn-group { + position: relative; + display: inline-block; + vertical-align: middle; /* match .btn alignment given font-size hack above */ + -webkit-app-region: no-drag; + + .btn { + position: relative; + float: left; + + /* Bring the "active" button to the front */ + &:focus, + &:active{ + z-index: 2; + } + + &.active { + z-index: 3; + } + } +} + +/* Prevent double borders when buttons are next to each other */ +.btn-group { + .btn + .btn, + .btn + .btn-group, + .btn-group + .btn, + .btn-group + .btn-group { + margin-left: -1px; + } + + > .btn:first-child { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + + > .btn:last-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + + > .btn:not(:first-child):not(:last-child) { + border-radius: 0; + } + + .btn + .btn { + border-left: 1px solid $dark-border-color; + } + + .btn + .btn.active { + border-left: 0; + } + + /* Selected state */ + .active { + color: #fff; + border: 1px solid transparent; + background-color: #6d6c6d; + background-image: none; + } + + /* Invert the icon in the active button */ + .active .icon { + color: #fff; + } +} diff --git a/app/styles/ui/buttons.css b/app/styles/ui/buttons.css new file mode 100644 index 0000000..28177cd --- /dev/null +++ b/app/styles/ui/buttons.css @@ -0,0 +1,135 @@ +/* */ +/* Buttons.css */ +/* -------------------------------------------------- */ + +.btn { + display: inline-block; + padding: 3px 8px; + margin-bottom: 0; + font-size: 12px; + line-height: 1.4; + text-align: center; + white-space: nowrap; + vertical-align: middle; + cursor: default; + background-image: none; + border: 1px solid transparent; + border-radius: $default-border-radius; + box-shadow: 0 1px 1px rgba(0,0,0,.06); + -webkit-app-region: no-drag; + + &:focus { + outline: none; + box-shadow: none; + } +} + +.btn-mini { + padding: 2px 6px; +} + +.btn-large { + padding: 6px 12px; +} + +.btn-form { + padding-right: 20px; + padding-left: 20px; +} + +/* Normal buttons */ +.btn-default { + color: $gray-color; + border-top-color: $dark-border-color; + border-right-color: $dark-border-color; + border-bottom-color: $darker-bottom-border-color; + border-left-color: $dark-border-color; + @mixin linear-gradient #fcfcfc, #f1f1f1; + + &:active { + background-color: #ddd; + background-image: none; + } +} + +/* Button variations */ +.btn-primary, +.btn-positive, +.btn-negative, +.btn-warning { + color: #fff; + text-shadow: 0 1px 1px rgba(0,0,0,.1); +} + +/* For primary buttons */ +.btn-primary { + border-color: #388df8; + border-bottom-color: darken(#388df8, 15%); + @mixin linear-gradient #6eb4f7, #1a82fb; + + &:active { + @mixin linear-gradient darken(#6eb4f7, 10%), darken(#1a82fb, 10%); + } +} + +/* For positive buttons */ +.btn-positive { + border-color: darken($positive-color, 10%); + border-bottom-color: darken($positive-color, 15%); + @mixin linear-gradient lighten($positive-color, 10%), darken($positive-color, 10%); + + &:active { + @mixin linear-gradient $positive-color, darken($positive-color, 15%); + } +} + +/* For negative actions */ +.btn-negative { + border-color: darken($negative-color, 10%); + border-bottom-color: darken($negative-color, 15%); + @mixin linear-gradient lighten($negative-color, 10%), darken($negative-color, 10%); + + &:active { + @mixin linear-gradient $negative-color, darken($negative-color, 15%); + } +} + +/* For warning actions */ +.btn-warning { + border-color: darken($warning-color, 10%); + border-bottom-color: darken($warning-color, 15%); + @mixin linear-gradient lighten($warning-color, 10%), darken($warning-color, 10%); + + &:active { + @mixin linear-gradient $warning-color, darken($warning-color, 15%); + } +} + +/* Icons in buttons */ +.btn .icon { + float: left; + width: 14px; + height: 14px; + margin-top: 1px; + margin-bottom: 1px; + color: #737475; + font-size: 14px; + line-height: 1; +} + +/* Add the margin next to the icon if there is text in the button too */ +.btn .icon-text { + margin-right: 5px; +} + +.btn-drop-segment { + padding-left: 1px; + padding-right: 2px; +} + +/* This utility class add a down arrow icon to the button */ +.btn-dropdown:after { + font-family: "photon-entypo"; + margin-left: 5px; + content: '\e873'; +} diff --git a/app/styles/ui/icons.css b/app/styles/ui/icons.css new file mode 100644 index 0000000..1926895 --- /dev/null +++ b/app/styles/ui/icons.css @@ -0,0 +1,309 @@ +@font-face { + font-family: "photon-entypo"; + src: url('$(font-path)photon-entypo.eot'); + src: url('$(font-path)photon-entypo.eot?#iefix') format('eot'), + url('$(font-path)photon-entypo.woff') format('woff'), + url('$(font-path)photon-entypo.ttf') format('truetype'); + font-weight: normal; + font-style: normal; +} + +.icon:before { + position: relative; + display: inline-block; + font-family: "photon-entypo"; + speak: none; + font-size: 100%; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-note:before { content: '\e800'; } /* '' */ +.icon-note-beamed:before { content: '\e801'; } /* '' */ +.icon-music:before { content: '\e802'; } /* '' */ +.icon-search:before { content: '\e803'; } /* '' */ +.icon-flashlight:before { content: '\e804'; } /* '' */ +.icon-mail:before { content: '\e805'; } /* '' */ +.icon-heart:before { content: '\e806'; } /* '' */ +.icon-heart-empty:before { content: '\e807'; } /* '' */ +.icon-star:before { content: '\e808'; } /* '' */ +.icon-star-empty:before { content: '\e809'; } /* '' */ +.icon-user:before { content: '\e80a'; } /* '' */ +.icon-users:before { content: '\e80b'; } /* '' */ +.icon-user-add:before { content: '\e80c'; } /* '' */ +.icon-video:before { content: '\e80d'; } /* '' */ +.icon-picture:before { content: '\e80e'; } /* '' */ +.icon-camera:before { content: '\e80f'; } /* '' */ +.icon-layout:before { content: '\e810'; } /* '' */ +.icon-menu:before { content: '\e811'; } /* '' */ +.icon-check:before { content: '\e812'; } /* '' */ +.icon-cancel:before { content: '\e813'; } /* '' */ +.icon-cancel-circled:before { content: '\e814'; } /* '' */ +.icon-cancel-squared:before { content: '\e815'; } /* '' */ +.icon-plus:before { content: '\e816'; } /* '' */ +.icon-plus-circled:before { content: '\e817'; } /* '' */ +.icon-plus-squared:before { content: '\e818'; } /* '' */ +.icon-minus:before { content: '\e819'; } /* '' */ +.icon-minus-circled:before { content: '\e81a'; } /* '' */ +.icon-minus-squared:before { content: '\e81b'; } /* '' */ +.icon-help:before { content: '\e81c'; } /* '' */ +.icon-help-circled:before { content: '\e81d'; } /* '' */ +.icon-info:before { content: '\e81e'; } /* '' */ +.icon-info-circled:before { content: '\e81f'; } /* '' */ +.icon-back:before { content: '\e820'; } /* '' */ +.icon-home:before { content: '\e821'; } /* '' */ +.icon-link:before { content: '\e822'; } /* '' */ +.icon-attach:before { content: '\e823'; } /* '' */ +.icon-lock:before { content: '\e824'; } /* '' */ +.icon-lock-open:before { content: '\e825'; } /* '' */ +.icon-eye:before { content: '\e826'; } /* '' */ +.icon-tag:before { content: '\e827'; } /* '' */ +.icon-bookmark:before { content: '\e828'; } /* '' */ +.icon-bookmarks:before { content: '\e829'; } /* '' */ +.icon-flag:before { content: '\e82a'; } /* '' */ +.icon-thumbs-up:before { content: '\e82b'; } /* '' */ +.icon-thumbs-down:before { content: '\e82c'; } /* '' */ +.icon-download:before { content: '\e82d'; } /* '' */ +.icon-upload:before { content: '\e82e'; } /* '' */ +.icon-upload-cloud:before { content: '\e82f'; } /* '' */ +.icon-reply:before { content: '\e830'; } /* '' */ +.icon-reply-all:before { content: '\e831'; } /* '' */ +.icon-forward:before { content: '\e832'; } /* '' */ +.icon-quote:before { content: '\e833'; } /* '' */ +.icon-code:before { content: '\e834'; } /* '' */ +.icon-export:before { content: '\e835'; } /* '' */ +.icon-pencil:before { content: '\e836'; } /* '' */ +.icon-feather:before { content: '\e837'; } /* '' */ +.icon-print:before { content: '\e838'; } /* '' */ +.icon-retweet:before { content: '\e839'; } /* '' */ +.icon-keyboard:before { content: '\e83a'; } /* '' */ +.icon-comment:before { content: '\e83b'; } /* '' */ +.icon-chat:before { content: '\e83c'; } /* '' */ +.icon-bell:before { content: '\e83d'; } /* '' */ +.icon-attention:before { content: '\e83e'; } /* '' */ +.icon-alert:before { content: '\e83f'; } /* '' */ +.icon-vcard:before { content: '\e840'; } /* '' */ +.icon-address:before { content: '\e841'; } /* '' */ +.icon-location:before { content: '\e842'; } /* '' */ +.icon-map:before { content: '\e843'; } /* '' */ +.icon-direction:before { content: '\e844'; } /* '' */ +.icon-compass:before { content: '\e845'; } /* '' */ +.icon-cup:before { content: '\e846'; } /* '' */ +.icon-trash:before { content: '\e847'; } /* '' */ +.icon-doc:before { content: '\e848'; } /* '' */ +.icon-docs:before { content: '\e849'; } /* '' */ +.icon-doc-landscape:before { content: '\e84a'; } /* '' */ +.icon-doc-text:before { content: '\e84b'; } /* '' */ +.icon-doc-text-inv:before { content: '\e84c'; } /* '' */ +.icon-newspaper:before { content: '\e84d'; } /* '' */ +.icon-book-open:before { content: '\e84e'; } /* '' */ +.icon-book:before { content: '\e84f'; } /* '' */ +.icon-folder:before { content: '\e850'; } /* '' */ +.icon-archive:before { content: '\e851'; } /* '' */ +.icon-box:before { content: '\e852'; } /* '' */ +.icon-rss:before { content: '\e853'; } /* '' */ +.icon-phone:before { content: '\e854'; } /* '' */ +.icon-cog:before { content: '\e855'; } /* '' */ +.icon-tools:before { content: '\e856'; } /* '' */ +.icon-share:before { content: '\e857'; } /* '' */ +.icon-shareable:before { content: '\e858'; } /* '' */ +.icon-basket:before { content: '\e859'; } /* '' */ +.icon-bag:before { content: '\e85a'; } /* '' */ +.icon-calendar:before { content: '\e85b'; } /* '' */ +.icon-login:before { content: '\e85c'; } /* '' */ +.icon-logout:before { content: '\e85d'; } /* '' */ +.icon-mic:before { content: '\e85e'; } /* '' */ +.icon-mute:before { content: '\e85f'; } /* '' */ +.icon-sound:before { content: '\e860'; } /* '' */ +.icon-volume:before { content: '\e861'; } /* '' */ +.icon-clock:before { content: '\e862'; } /* '' */ +.icon-hourglass:before { content: '\e863'; } /* '' */ +.icon-lamp:before { content: '\e864'; } /* '' */ +.icon-light-down:before { content: '\e865'; } /* '' */ +.icon-light-up:before { content: '\e866'; } /* '' */ +.icon-adjust:before { content: '\e867'; } /* '' */ +.icon-block:before { content: '\e868'; } /* '' */ +.icon-resize-full:before { content: '\e869'; } /* '' */ +.icon-resize-small:before { content: '\e86a'; } /* '' */ +.icon-popup:before { content: '\e86b'; } /* '' */ +.icon-publish:before { content: '\e86c'; } /* '' */ +.icon-window:before { content: '\e86d'; } /* '' */ +.icon-arrow-combo:before { content: '\e86e'; } /* '' */ +.icon-down-circled:before { content: '\e86f'; } /* '' */ +.icon-left-circled:before { content: '\e870'; } /* '' */ +.icon-right-circled:before { content: '\e871'; } /* '' */ +.icon-up-circled:before { content: '\e872'; } /* '' */ +.icon-down-open:before { content: '\e873'; } /* '' */ +.icon-left-open:before { content: '\e874'; } /* '' */ +.icon-right-open:before { content: '\e875'; } /* '' */ +.icon-up-open:before { content: '\e876'; } /* '' */ +.icon-down-open-mini:before { content: '\e877'; } /* '' */ +.icon-left-open-mini:before { content: '\e878'; } /* '' */ +.icon-right-open-mini:before { content: '\e879'; } /* '' */ +.icon-up-open-mini:before { content: '\e87a'; } /* '' */ +.icon-down-open-big:before { content: '\e87b'; } /* '' */ +.icon-left-open-big:before { content: '\e87c'; } /* '' */ +.icon-right-open-big:before { content: '\e87d'; } /* '' */ +.icon-up-open-big:before { content: '\e87e'; } /* '' */ +.icon-down:before { content: '\e87f'; } /* '' */ +.icon-left:before { content: '\e880'; } /* '' */ +.icon-right:before { content: '\e881'; } /* '' */ +.icon-up:before { content: '\e882'; } /* '' */ +.icon-down-dir:before { content: '\e883'; } /* '' */ +.icon-left-dir:before { content: '\e884'; } /* '' */ +.icon-right-dir:before { content: '\e885'; } /* '' */ +.icon-up-dir:before { content: '\e886'; } /* '' */ +.icon-down-bold:before { content: '\e887'; } /* '' */ +.icon-left-bold:before { content: '\e888'; } /* '' */ +.icon-right-bold:before { content: '\e889'; } /* '' */ +.icon-up-bold:before { content: '\e88a'; } /* '' */ +.icon-down-thin:before { content: '\e88b'; } /* '' */ +.icon-left-thin:before { content: '\e88c'; } /* '' */ +.icon-right-thin:before { content: '\e88d'; } /* '' */ +.icon-up-thin:before { content: '\e88e'; } /* '' */ +.icon-ccw:before { content: '\e88f'; } /* '' */ +.icon-cw:before { content: '\e890'; } /* '' */ +.icon-arrows-ccw:before { content: '\e891'; } /* '' */ +.icon-level-down:before { content: '\e892'; } /* '' */ +.icon-level-up:before { content: '\e893'; } /* '' */ +.icon-shuffle:before { content: '\e894'; } /* '' */ +.icon-loop:before { content: '\e895'; } /* '' */ +.icon-switch:before { content: '\e896'; } /* '' */ +.icon-play:before { content: '\e897'; } /* '' */ +.icon-stop:before { content: '\e898'; } /* '' */ +.icon-pause:before { content: '\e899'; } /* '' */ +.icon-record:before { content: '\e89a'; } /* '' */ +.icon-to-end:before { content: '\e89b'; } /* '' */ +.icon-to-start:before { content: '\e89c'; } /* '' */ +.icon-fast-forward:before { content: '\e89d'; } /* '' */ +.icon-fast-backward:before { content: '\e89e'; } /* '' */ +.icon-progress-0:before { content: '\e89f'; } /* '' */ +.icon-progress-1:before { content: '\e8a0'; } /* '' */ +.icon-progress-2:before { content: '\e8a1'; } /* '' */ +.icon-progress-3:before { content: '\e8a2'; } /* '' */ +.icon-target:before { content: '\e8a3'; } /* '' */ +.icon-palette:before { content: '\e8a4'; } /* '' */ +.icon-list:before { content: '\e8a5'; } /* '' */ +.icon-list-add:before { content: '\e8a6'; } /* '' */ +.icon-signal:before { content: '\e8a7'; } /* '' */ +.icon-trophy:before { content: '\e8a8'; } /* '' */ +.icon-battery:before { content: '\e8a9'; } /* '' */ +.icon-back-in-time:before { content: '\e8aa'; } /* '' */ +.icon-monitor:before { content: '\e8ab'; } /* '' */ +.icon-mobile:before { content: '\e8ac'; } /* '' */ +.icon-network:before { content: '\e8ad'; } /* '' */ +.icon-cd:before { content: '\e8ae'; } /* '' */ +.icon-inbox:before { content: '\e8af'; } /* '' */ +.icon-install:before { content: '\e8b0'; } /* '' */ +.icon-globe:before { content: '\e8b1'; } /* '' */ +.icon-cloud:before { content: '\e8b2'; } /* '' */ +.icon-cloud-thunder:before { content: '\e8b3'; } /* '' */ +.icon-flash:before { content: '\e8b4'; } /* '' */ +.icon-moon:before { content: '\e8b5'; } /* '' */ +.icon-flight:before { content: '\e8b6'; } /* '' */ +.icon-paper-plane:before { content: '\e8b7'; } /* '' */ +.icon-leaf:before { content: '\e8b8'; } /* '' */ +.icon-lifebuoy:before { content: '\e8b9'; } /* '' */ +.icon-mouse:before { content: '\e8ba'; } /* '' */ +.icon-briefcase:before { content: '\e8bb'; } /* '' */ +.icon-suitcase:before { content: '\e8bc'; } /* '' */ +.icon-dot:before { content: '\e8bd'; } /* '' */ +.icon-dot-2:before { content: '\e8be'; } /* '' */ +.icon-dot-3:before { content: '\e8bf'; } /* '' */ +.icon-brush:before { content: '\e8c0'; } /* '' */ +.icon-magnet:before { content: '\e8c1'; } /* '' */ +.icon-infinity:before { content: '\e8c2'; } /* '' */ +.icon-erase:before { content: '\e8c3'; } /* '' */ +.icon-chart-pie:before { content: '\e8c4'; } /* '' */ +.icon-chart-line:before { content: '\e8c5'; } /* '' */ +.icon-chart-bar:before { content: '\e8c6'; } /* '' */ +.icon-chart-area:before { content: '\e8c7'; } /* '' */ +.icon-tape:before { content: '\e8c8'; } /* '' */ +.icon-graduation-cap:before { content: '\e8c9'; } /* '' */ +.icon-language:before { content: '\e8ca'; } /* '' */ +.icon-ticket:before { content: '\e8cb'; } /* '' */ +.icon-water:before { content: '\e8cc'; } /* '' */ +.icon-droplet:before { content: '\e8cd'; } /* '' */ +.icon-air:before { content: '\e8ce'; } /* '' */ +.icon-credit-card:before { content: '\e8cf'; } /* '' */ +.icon-floppy:before { content: '\e8d0'; } /* '' */ +.icon-clipboard:before { content: '\e8d1'; } /* '' */ +.icon-megaphone:before { content: '\e8d2'; } /* '' */ +.icon-database:before { content: '\e8d3'; } /* '' */ +.icon-drive:before { content: '\e8d4'; } /* '' */ +.icon-bucket:before { content: '\e8d5'; } /* '' */ +.icon-thermometer:before { content: '\e8d6'; } /* '' */ +.icon-key:before { content: '\e8d7'; } /* '' */ +.icon-flow-cascade:before { content: '\e8d8'; } /* '' */ +.icon-flow-branch:before { content: '\e8d9'; } /* '' */ +.icon-flow-tree:before { content: '\e8da'; } /* '' */ +.icon-flow-line:before { content: '\e8db'; } /* '' */ +.icon-flow-parallel:before { content: '\e8dc'; } /* '' */ +.icon-rocket:before { content: '\e8dd'; } /* '' */ +.icon-gauge:before { content: '\e8de'; } /* '' */ +.icon-traffic-cone:before { content: '\e8df'; } /* '' */ +.icon-cc:before { content: '\e8e0'; } /* '' */ +.icon-cc-by:before { content: '\e8e1'; } /* '' */ +.icon-cc-nc:before { content: '\e8e2'; } /* '' */ +.icon-cc-nc-eu:before { content: '\e8e3'; } /* '' */ +.icon-cc-nc-jp:before { content: '\e8e4'; } /* '' */ +.icon-cc-sa:before { content: '\e8e5'; } /* '' */ +.icon-cc-nd:before { content: '\e8e6'; } /* '' */ +.icon-cc-pd:before { content: '\e8e7'; } /* '' */ +.icon-cc-zero:before { content: '\e8e8'; } /* '' */ +.icon-cc-share:before { content: '\e8e9'; } /* '' */ +.icon-cc-remix:before { content: '\e8ea'; } /* '' */ +.icon-github:before { content: '\e8eb'; } /* '' */ +.icon-github-circled:before { content: '\e8ec'; } /* '' */ +.icon-flickr:before { content: '\e8ed'; } /* '' */ +.icon-flickr-circled:before { content: '\e8ee'; } /* '' */ +.icon-vimeo:before { content: '\e8ef'; } /* '' */ +.icon-vimeo-circled:before { content: '\e8f0'; } /* '' */ +.icon-twitter:before { content: '\e8f1'; } /* '' */ +.icon-twitter-circled:before { content: '\e8f2'; } /* '' */ +.icon-facebook:before { content: '\e8f3'; } /* '' */ +.icon-facebook-circled:before { content: '\e8f4'; } /* '' */ +.icon-facebook-squared:before { content: '\e8f5'; } /* '' */ +.icon-gplus:before { content: '\e8f6'; } /* '' */ +.icon-gplus-circled:before { content: '\e8f7'; } /* '' */ +.icon-pinterest:before { content: '\e8f8'; } /* '' */ +.icon-pinterest-circled:before { content: '\e8f9'; } /* '' */ +.icon-tumblr:before { content: '\e8fa'; } /* '' */ +.icon-tumblr-circled:before { content: '\e8fb'; } /* '' */ +.icon-linkedin:before { content: '\e8fc'; } /* '' */ +.icon-linkedin-circled:before { content: '\e8fd'; } /* '' */ +.icon-dribbble:before { content: '\e8fe'; } /* '' */ +.icon-dribbble-circled:before { content: '\e8ff'; } /* '' */ +.icon-stumbleupon:before { content: '\e900'; } /* '' */ +.icon-stumbleupon-circled:before { content: '\e901'; } /* '' */ +.icon-lastfm:before { content: '\e902'; } /* '' */ +.icon-lastfm-circled:before { content: '\e903'; } /* '' */ +.icon-rdio:before { content: '\e904'; } /* '' */ +.icon-rdio-circled:before { content: '\e905'; } /* '' */ +.icon-spotify:before { content: '\e906'; } /* '' */ +.icon-spotify-circled:before { content: '\e907'; } /* '' */ +.icon-qq:before { content: '\e908'; } /* '' */ +.icon-instagram:before { content: '\e909'; } /* '' */ +.icon-dropbox:before { content: '\e90a'; } /* '' */ +.icon-evernote:before { content: '\e90b'; } /* '' */ +.icon-flattr:before { content: '\e90c'; } /* '' */ +.icon-skype:before { content: '\e90d'; } /* '' */ +.icon-skype-circled:before { content: '\e90e'; } /* '' */ +.icon-renren:before { content: '\e90f'; } /* '' */ +.icon-sina-weibo:before { content: '\e910'; } /* '' */ +.icon-paypal:before { content: '\e911'; } /* '' */ +.icon-picasa:before { content: '\e912'; } /* '' */ +.icon-soundcloud:before { content: '\e913'; } /* '' */ +.icon-mixi:before { content: '\e914'; } /* '' */ +.icon-behance:before { content: '\e915'; } /* '' */ +.icon-google-circles:before { content: '\e916'; } /* '' */ +.icon-vkontakte:before { content: '\e917'; } /* '' */ +.icon-smashing:before { content: '\e918'; } /* '' */ +.icon-sweden:before { content: '\e919'; } /* '' */ +.icon-db-shape:before { content: '\e91a'; } /* '' */ +.icon-logo-db:before { content: '\e91b'; } /* '' */ diff --git a/app/styles/ui/mixins.css b/app/styles/ui/mixins.css new file mode 100644 index 0000000..8b1928a --- /dev/null +++ b/app/styles/ui/mixins.css @@ -0,0 +1,16 @@ +@define-mixin clearfix { + &:before, + &:after { + display: table; /* 2 */ + content: " "; /* 1 */ + } + &:after { + clear: both; + } +} +@define-mixin linear-gradient $color-from, $color-to { + background-color: $color-from; /* Old browsers */ + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%,$color-from), color-stop(100%,$color-to)); /* Chrome, Safari4+ */ + background-image: -webkit-linear-gradient(top, $color-from 0%, $color-to 100%); /* Chrome10+, Safari5.1+ */ + background-image: linear-gradient(to bottom, $color-from 0%, $color-to 100%); /* W3C */ +} diff --git a/app/styles/ui/photon.css b/app/styles/ui/photon.css new file mode 100644 index 0000000..88a480c --- /dev/null +++ b/app/styles/ui/photon.css @@ -0,0 +1,24 @@ +@import "variables.css"; + +/* Mixins */ +@import "mixins.css"; + +/* Normalize, Appify, Base, & Utilities CSS */ +/*@import "normalize.scss"; +@import "appify.scss"; +@import "base.scss"; +@import "utilities.scss";*/ + +/* Components */ +@import "buttons.css"; +@import "button-groups.css"; +@import "bars.css"; +@import "icons.css"; +/*@import "forms.scss"; +@import "grid.scss"; +@import "images.scss"; +@import "lists.scss"; +@import "navs.scss"; + +@import "tables.scss"; +@import "tabs.scss";*/ diff --git a/app/styles/ui/variables.css b/app/styles/ui/variables.css new file mode 100644 index 0000000..9b90390 --- /dev/null +++ b/app/styles/ui/variables.css @@ -0,0 +1,57 @@ +/* */ +/* Variables */ +/* -------------------------------------------------- */ + +/* Type */ +/* -------------------------------------------------- */ + +$font-path: fonts/ !default; +/* Try to use the system's font on whatever platform the user is on. */ +$font-family-default: system, -apple-system, ".SFNSDisplay-Regular", "Helvetica Neue", Helvetica, "Segoe UI", sans-serif !default; +$font-size-default: 13px !default; +$font-weight: 500 !default; +$font-weight-bold: 700 !default; +$font-weight-light: 300 !default; +$line-height-default: 1.6 !default; + + +/* Colors */ +/* -------------------------------------------------- */ + +/* Main colors */ +$primary-color: #3b99fc !default; +$chrome-color: #fff !default; + +/* Copy */ +$gray-color: #333 !default; + +/* Borders */ +$border-color: #ddd !default; +$dark-border-color: #c2c0c2 !default; +$darker-bottom-border-color: #a19fa1 !default; +$toolbar-border-color: #939293 !default; + +/* Action colors */ +$default-color: #fff !default; +$positive-color: #34c84a !default; +$negative-color: #fc605b !default; +$warning-color: #fdbc40 !default; + +/* Shades */ +$dark-color: #57acf5 !default; + +/* Focus and active colors */ +$active-color: #116cd6; +$focus-input-color: lighten($primary-color, 10%) !default; + +/* Other */ +/* -------------------------------------------------- */ + +/* Border radius */ +$default-border-radius: 4px; + +/* Padding */ +$padding: 10px; +$padding-mini: 3px; +$padding-less: 5px; +$padding-more: 20px; diff --git a/app/viewer/containers/Viewer.js b/app/viewer/containers/Viewer.js index 41b25ff..4ea6ba1 100644 --- a/app/viewer/containers/Viewer.js +++ b/app/viewer/containers/Viewer.js @@ -1,5 +1,5 @@ import React, { Component, PropTypes } from 'react'; -import { remote } from 'electron'; +import { remote, ipcRenderer as ipc } from 'electron'; import path from 'path'; import { connect } from 'react-redux'; @@ -8,7 +8,7 @@ import { bindActionCreators } from 'redux'; import Markdown from '../../main/utils/markdown'; // Utilities -import { setStyle, getScreenSize } from '../utils'; +import { setStyle, getScreenSize, getSlideSize } from '../utils'; import { renderTheme } from '../theme'; // Selectors @@ -16,12 +16,15 @@ import { getEditor } from '../../editor/selectors'; const UUID = remote.getCurrentWindow().uuid; -class Editor extends Component { +class Viewer extends Component { static propTypes = { code: PropTypes.string, workingFile: PropTypes.string, clearSettings: PropTypes.func, addSetting: PropTypes.func, + presenting: PropTypes.bool, + currentPage: PropTypes.number, + viewMode: PropTypes.string, } constructor(props) { @@ -32,12 +35,46 @@ class Editor extends Component { } componentDidMount() { - this.applyScreenSize(); + this.setPresenting(this.props.presenting); + setTimeout(() => this.applyScreenSize(), 1000); window.addEventListener('resize', this.handleResize); + this.applyCurrentPage(this.props.currentPage); + } + + componentWillReceiveProps(nextProps) { + if (this.props.currentPage !== nextProps.currentPage) { + this.applyCurrentPage(nextProps.currentPage); + } + if (this.props.presenting !== nextProps.presenting) { + this.setPresenting(nextProps.presenting); + } + if (this.props.viewMode !== nextProps.viewMode) { + this.setViewMode(nextProps.viewMode); + } } componentWillUnmount() { window.removeEventListener('resize', this.handleResize); + this.setPresenting(false); + } + + setViewMode = (mode) => { + document.body.classList.toggle('list', mode === 'list'); + document.body.classList.toggle('screen', mode === 'screen'); + } + + setPresenting = (presenting) => { + const mode = presenting ? 'screen' : this.props.viewMode; + this.setViewMode(mode); + + if (presenting) { + setStyle('presentationMode', `body { + --preview-margin: 0; + }`); + this.applyCurrentPage(this.props.currentPage); + } else { + setStyle('presentationMode', ''); + } } handleResize = () => { @@ -50,7 +87,23 @@ class Editor extends Component { --screen-width: ${size.w}; --screen-height: ${size.h}; }`); - // $('#container').toggleClass 'height-base', size.ratio > getSlideSize().ratio + const container = document.getElementById('container'); + if (container) { + container.classList.toggle('height-base', size.ratio > getSlideSize().ratio); + } + } + + applyCurrentPage(page) { + setStyle('currentPage', ` + @media not print { + body.slide-view.screen .slide_wrapper:not(:nth-of-type(${page})) { + width: 0 !important; + height: 0 !important; + border: none !important; + box-shadow: none !important; + } + }` + ); } applySlideSize(width, height) { @@ -82,6 +135,8 @@ class Editor extends Component { const render = this.markdown.render(this.props.code || ''); const settings = this.markdown.getSettings(); + ipc.sendToHost('pageData', this.markdown.getRulers()); + this.applySlideSize(settings.getGlobal('width'), settings.getGlobal('height')); const newTheme = settings.getGlobal('theme') || 'default'; @@ -121,7 +176,10 @@ export default connect( return { code: editor.code || '', workingFile: editor.file, + presenting: editor.presenting, + viewMode: editor.viewMode || 'screen', + currentPage: editor.currentPage || 1, }; }, dispatch => bindActionCreators({}, dispatch) -)(Editor); +)(Viewer); diff --git a/package.json b/package.json index 87bdb43..74946e5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "markdeck", "productName": "Markdeck", - "version": "0.1.1", + "version": "0.2.0-rc.1", "description": "Presentation software based on React, Markdown, PostCSS for rapid presentation development", "main": "main.js", "scripts": { @@ -65,6 +65,7 @@ "express": "^4.14.0", "extract-text-webpack-plugin": "^1.0.1", "fbjs-scripts": "^0.7.1", + "file-loader": "^0.9.0", "jsdom": "^9.4.2", "json-loader": "^0.5.4", "minimist": "^1.2.0", diff --git a/package.sh b/package.sh index 5e5ee50..d76a59b 100755 --- a/package.sh +++ b/package.sh @@ -1,6 +1,15 @@ #!/bin/sh -: "${VERSION:?Need to set VERSION}" +PACKAGE_VERSION=$(cat package.json \ + | grep version \ + | head -1 \ + | awk -F: '{ print $2 }' \ + | sed 's/[",]//g' \ + | tr -d '[[:space:]]') + +VERSION="${VERSION:-$PACKAGE_VERSION}" + +echo "Packaging version $VERSION" RELEASE_PATH="$(pwd)/release" IMAGE_PATH="$(pwd)/release/images" diff --git a/webpack.config.base.js b/webpack.config.base.js index 94d1e87..8dbae04 100644 --- a/webpack.config.base.js +++ b/webpack.config.base.js @@ -18,6 +18,10 @@ export default { { test: /\.svg$/, loader: 'raw-loader', + }, + { + test: /\.(eot|ttf|woff|woff2)$/, + loader: 'file-loader?publicPath=./&name=fonts/[name].[ext]', } ] },