diff --git a/TODO.md b/TODO.md index f5cd8cd..415e409 100644 --- a/TODO.md +++ b/TODO.md @@ -2,11 +2,16 @@ - [ ] add dictionary - [ ] add search in home -- [ ] set up vue-router with animation -- [ ] fix and improve highlight -- [ ] reduce build size -- [ ] add last card in home grid for book add button -- [ ] create theme +- [ ] swip transtition +- [ ] fix app icon +- [x] fix : highlight not loading +- [x] fix : if cover not found replace it with default cover +- [x] feature : Themes ( dark, tan and light theme added) +- [x] fix : click lisnter in rendition for navigation (buttons removed) +- [x] fix : add threshold value for wheel event +- [x] fix : bubble goes out of page +- [x] fix : bookmark titles +- [x] feature : sliderbar tooltip now show cuurent toc insted of percentage # v0.3.0 diff --git a/package.json b/package.json index 11d4a10..d5571a0 100644 --- a/package.json +++ b/package.json @@ -104,11 +104,9 @@ "file-loader": "^3.0.1", "html-webpack-plugin": "^3.2.0", "inject-loader": "^4.0.1", - "less": "^3.9.0", - "less-loader": "^4.1.0", "mini-css-extract-plugin": "^0.5.0", "node-loader": "^0.6.0", - "node-sass": "^4.11.0", + "node-sass": "^4.12.0", "prettier": "^1.17.0", "purgecss-webpack-plugin": "^1.5.0", "sass-loader": "^7.1.0", diff --git a/src/renderer/App.vue b/src/renderer/App.vue index b326490..f896336 100644 --- a/src/renderer/App.vue +++ b/src/renderer/App.vue @@ -1,5 +1,5 @@ @@ -13,6 +13,7 @@ export default { data() { return { isReady: false, + theme: 'light' }; }, @@ -32,11 +33,30 @@ export default { else{ this.isReady = true; } + + this.$bus.on('theme-change',(theme)=>{ + this.$refs.app.className = ""; + this.$refs.app.classList.add(theme); + + this.$store.commit('setTheme', theme); + }); }, }; - + + + diff --git a/src/renderer/assets/default-cover.jpg b/src/renderer/assets/default-cover.jpg deleted file mode 100644 index d62d4b3..0000000 Binary files a/src/renderer/assets/default-cover.jpg and /dev/null differ diff --git a/src/renderer/assets/default-cover.png b/src/renderer/assets/default-cover.png new file mode 100644 index 0000000..918169c Binary files /dev/null and b/src/renderer/assets/default-cover.png differ diff --git a/src/renderer/assets/logo-layout.png b/src/renderer/assets/logo-layout.png new file mode 100644 index 0000000..97c82e0 Binary files /dev/null and b/src/renderer/assets/logo-layout.png differ diff --git a/src/renderer/assets/logo.png b/src/renderer/assets/logo.png deleted file mode 100644 index c6afc22..0000000 Binary files a/src/renderer/assets/logo.png and /dev/null differ diff --git a/src/renderer/assets/style.less b/src/renderer/assets/style.less deleted file mode 100644 index 0d11c78..0000000 --- a/src/renderer/assets/style.less +++ /dev/null @@ -1,3 +0,0 @@ -@border-radius: 4px; -@margin: 4px; -@padding: 4px; \ No newline at end of file diff --git a/src/renderer/assets/style.scss b/src/renderer/assets/style.scss new file mode 100644 index 0000000..544353a --- /dev/null +++ b/src/renderer/assets/style.scss @@ -0,0 +1,3 @@ +$border-radius: 4px; +$margin: 4px; +$padding: 4px; \ No newline at end of file diff --git a/src/renderer/components/Home/Grid.vue b/src/renderer/components/Home/Grid.vue index 6c7d6db..e679fb7 100644 --- a/src/renderer/components/Home/Grid.vue +++ b/src/renderer/components/Home/Grid.vue @@ -3,7 +3,12 @@
- + +
+ + +
+
{{ trunc(book.title,30) }}
@@ -19,6 +24,7 @@ */ export default { + name: 'Grid', props: { @@ -50,6 +56,10 @@ export default { type: Array, default: () => {}, }, + defaultCover: { + type: String, + default: 'src/renderer/assets/default-cover.jpg' + } }, data() { @@ -194,5 +204,7 @@ export default { align-content: center; text-align: center; color: #ffffff; + position: relative; + top: -3px; } diff --git a/src/renderer/components/Reader.vue b/src/renderer/components/Reader.vue deleted file mode 100644 index 6c81fc2..0000000 --- a/src/renderer/components/Reader.vue +++ /dev/null @@ -1,509 +0,0 @@ - - - - - - - - diff --git a/src/renderer/components/Reader/BookmarkMenu.vue b/src/renderer/components/Reader/BookmarkMenu.vue new file mode 100644 index 0000000..660978c --- /dev/null +++ b/src/renderer/components/Reader/BookmarkMenu.vue @@ -0,0 +1,48 @@ + + + + + diff --git a/src/renderer/components/Reader/BubleMenu.vue b/src/renderer/components/Reader/BubleMenu.vue new file mode 100644 index 0000000..32994e8 --- /dev/null +++ b/src/renderer/components/Reader/BubleMenu.vue @@ -0,0 +1,116 @@ + + + + + + + + diff --git a/src/renderer/components/Reader/SearchMenu.vue b/src/renderer/components/Reader/SearchMenu.vue new file mode 100644 index 0000000..f5fe94b --- /dev/null +++ b/src/renderer/components/Reader/SearchMenu.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/src/renderer/components/Reader/ThemeMenu.vue b/src/renderer/components/Reader/ThemeMenu.vue new file mode 100644 index 0000000..1716cd3 --- /dev/null +++ b/src/renderer/components/Reader/ThemeMenu.vue @@ -0,0 +1,133 @@ + + + + + + + diff --git a/src/renderer/components/Reader/TocMenu.vue b/src/renderer/components/Reader/TocMenu.vue new file mode 100644 index 0000000..b53326d --- /dev/null +++ b/src/renderer/components/Reader/TocMenu.vue @@ -0,0 +1,34 @@ + + + + + diff --git a/src/renderer/components/Reader/listener/click.js b/src/renderer/components/Reader/listener/click.js new file mode 100644 index 0000000..62e93f0 --- /dev/null +++ b/src/renderer/components/Reader/listener/click.js @@ -0,0 +1,37 @@ +/** + * Thanks to Xyfir + * https://github.com/Xyfir/xyfir-reader + */ + +/** + * Listen for clicks and convert them to actions based on the location of the + * click on the page. + * @param {Document} document - The document to add event listeners to. + * @param {Object} rendition - EPUBJS rendition + * @param {function} fn - The listener function. + */ + +export default function mouseListener(document, rendition, fn) { + + document.addEventListener('click', event => { + if (event.ignore) return; + event.ignore = true; + + // User selected text + if (document.getSelection().toString()) return; + + // Get book iframe window's size + const wX = document.body.clientWidth; + // const wY = document.body.clientHeight; + + // Get click location + const cX = event.clientX - (rendition.manager.scrollLeft || 0); + // const cY = event.clientY; + + // Click was in left 20% of page + if (cX < wX * 0.2) fn('prev'); + // Click was in right 20% of page + else if (cX > wX - wX * 0.2) fn('next'); + + }, false); +} \ No newline at end of file diff --git a/src/renderer/components/Reader/listener/key.js b/src/renderer/components/Reader/listener/key.js new file mode 100644 index 0000000..510c523 --- /dev/null +++ b/src/renderer/components/Reader/listener/key.js @@ -0,0 +1,21 @@ + +/** +* Listen for key press +* @param {HTMLElement} el - The element to add event listeners to. +* @param {function} fn - The listener function. +*/ + +export default function keyListener(el, fn) { + el.addEventListener('keyup', e => { + + // Right or up arrow key indicates next + if (e.keyCode === 39 || e.keyCode === 38) { + fn('next'); + } + // left or down arrow key indicates next + else if (e.keyCode === 37 || e.keyCode === 40 ){ + fn('prev') + } + }, false); + +} diff --git a/src/renderer/components/Reader/listener/listener.js b/src/renderer/components/Reader/listener/listener.js new file mode 100644 index 0000000..e7b147c --- /dev/null +++ b/src/renderer/components/Reader/listener/listener.js @@ -0,0 +1,6 @@ +import clickListener from './click'; +import keyListener from './key'; +import wheelListener from './wheel'; +import swipListener from './swip'; +import selectListener from './select'; +export { clickListener, keyListener, wheelListener, swipListener, selectListener }; \ No newline at end of file diff --git a/src/renderer/components/Reader/listener/select.js b/src/renderer/components/Reader/listener/select.js new file mode 100644 index 0000000..77e64f1 --- /dev/null +++ b/src/renderer/components/Reader/listener/select.js @@ -0,0 +1,40 @@ +/** + * @param {Document} document - The document object to add event + * @param {Object} rendition - The EPUBJS rendition + * @param {Function} fb - The listener function + */ +export default function selectListener(document, rendition, fn){ + + + document.addEventListener('mousedown', () => { + document.getSelection().removeAllRanges(); + fn('cleared'); + }); + + document.addEventListener('mouseup', e => { + + if (e.ignore) return; + e.ignore = true; + + const selection = document.getSelection(); + const text = selection.toString(); + + if (text === '') return; + const range = selection.getRangeAt(0); + + const [contents] = rendition.getContents(); + const cfiRange = contents.cfiFromRange(range); + + const SelectionReact = range.getBoundingClientRect(); + const viewRect = rendition.manager.container.getBoundingClientRect(); + + let react = { + left: `${viewRect.x + SelectionReact.x - (rendition.manager.scrollLeft || 0)}px`, + top: `${viewRect.y + SelectionReact.y}px`, + width: `${SelectionReact.width}px`, + height: `${SelectionReact.height}px`, + } + fn('selected', react, text, cfiRange); + }); + +} \ No newline at end of file diff --git a/src/renderer/components/Reader/listener/swip.js b/src/renderer/components/Reader/listener/swip.js new file mode 100644 index 0000000..0d50a9b --- /dev/null +++ b/src/renderer/components/Reader/listener/swip.js @@ -0,0 +1,79 @@ +/** + * Thanks to Xyfir + * https://github.com/Xyfir/xyfir-reader + */ + + /** + * Listen for swipes convert them to actions. + * @param {Document} document - The document to add event listeners to. + * @param {function} fn - The listener function. + */ +export default function swipListener(document, fn) { + // Defaults: 100, 350, 100 + // Required min distance traveled to be considered swipe + const threshold = 50; + // Maximum time allowed to travel that distance + const allowedTime = 500; + // Maximum distance allowed at the same time in perpendicular direction + const restraint = 200; + + let startX; + let startY; + let startTime; + + document.addEventListener( + 'touchstart', + e => { + if (e.ignore) return; + e.ignore = true; + + startX = e.changedTouches[0].pageX; + startY = e.changedTouches[0].pageY; + startTime = Date.now(); + }, + false + ); + + document.addEventListener( + 'touchend', + e => { + if (e.ignore) return; + e.ignore = true; + + // Get distance traveled by finger while in contact with surface + const distX = e.changedTouches[0].pageX - startX; + const distY = e.changedTouches[0].pageY - startY; + + // Time elapsed since touchstart + const elapsedTime = Date.now() - startTime; + + if (elapsedTime <= allowedTime) { + // Horizontal swipe + if (Math.abs(distX) >= threshold && Math.abs(distY) <= restraint) + // If dist traveled is negative, it indicates left swipe + fn(distX < 0 ? 'prev' : 'next'); + // Vertical swipe + else if (Math.abs(distY) >= threshold && Math.abs(distX) <= restraint) + // If dist traveled is negative, it indicates up swipe + fn(distY < 0 ? 'up' : 'down'); + // Tap + else { + + document.defaultView.getSelection().removeAllRanges(); + + // Convert tap to click + document.dispatchEvent( + new MouseEvent('click', { + clientX: startX, + clientY: startY + }) + ); + + // !! Needed to prevent double 'clicks' in certain environments + e.preventDefault(); + } + } + }, + false + ); +} \ No newline at end of file diff --git a/src/renderer/components/Reader/listener/wheel.js b/src/renderer/components/Reader/listener/wheel.js new file mode 100644 index 0000000..807cdb2 --- /dev/null +++ b/src/renderer/components/Reader/listener/wheel.js @@ -0,0 +1,41 @@ + +/** +* Listen for wheel and convert them to next or prev action based on direction. +* @param {HTMLElement} el - The element to add event listeners to. +* @param {function} fn - The listener function. +*/ + +export default function wheelListener(el, fn) { + + // Required min distance traveled to be considered swipe + const threshold = 750; + // Maximum time allowed to travel that distance + const allowedTime = 50; + + let dist = 0; + let isScrolling; + + el.addEventListener('wheel', e => { + if (e.ignore) return; + e.ignore = true; + + clearTimeout(isScrolling); + + dist += e.deltaY; + + + isScrolling = setTimeout(() => { + if (Math.abs(dist) >= threshold) { + + // If wheel scrolled down it indicates left + let direction = Math.sign(dist) > 0 ? 'next' : 'prev'; + fn(direction); + dist = 0; + } + + dist = 0; + }, allowedTime); + + }); + +} diff --git a/src/renderer/components/Titlebar.vue b/src/renderer/components/Titlebar.vue index 21c28dd..d796203 100644 --- a/src/renderer/components/Titlebar.vue +++ b/src/renderer/components/Titlebar.vue @@ -45,23 +45,21 @@ export default { }; - - - diff --git a/src/renderer/router.js b/src/renderer/router.js index f80d342..da74050 100644 --- a/src/renderer/router.js +++ b/src/renderer/router.js @@ -1,7 +1,7 @@ import Vue from 'vue'; import Router from 'vue-router'; -import Home from '@/renderer/components/Home'; -import Reader from '@/renderer/components/Reader'; +import Home from './views/Home'; +import Reader from './views/Reader'; Vue.use(Router); export default new Router({ diff --git a/src/renderer/store.js b/src/renderer/store.js index 3239aa2..3df03f7 100644 --- a/src/renderer/store.js +++ b/src/renderer/store.js @@ -1,11 +1,19 @@ import Vue from 'vue'; import Vuex from 'vuex'; -// import createPersistedState from 'vuex-persistedstate' Vue.use(Vuex); export default new Vuex.Store({ strict: process.env.NODE_ENV !== 'production', - // TODO: Enable when deploy - // plugins: [createPersistedState()] + state: { + theme: 'default', + }, + getters: { + theme: state => state.theme, + }, + mutations: { + setTheme: (state, theme) => { + state.theme = theme; + }, + }, }); diff --git a/src/renderer/components/Home.vue b/src/renderer/views/Home.vue similarity index 65% rename from src/renderer/components/Home.vue rename to src/renderer/views/Home.vue index bdcb4a2..abcac5a 100644 --- a/src/renderer/components/Home.vue +++ b/src/renderer/views/Home.vue @@ -4,14 +4,14 @@ - + - diff --git a/src/renderer/views/Reader.vue b/src/renderer/views/Reader.vue new file mode 100644 index 0000000..36d9bb7 --- /dev/null +++ b/src/renderer/views/Reader.vue @@ -0,0 +1,374 @@ + + + + + + + + diff --git a/src/shared/db.js b/src/shared/db.js index b1a454b..ef16376 100644 --- a/src/shared/db.js +++ b/src/shared/db.js @@ -189,9 +189,10 @@ class Database { if (this.storage[id]) { this.storage[id] = newValue; + this.hasChanges = true; + this.async(); return true; } - this.async(); return false; } diff --git a/src/shared/dbUtilis.js b/src/shared/dbUtilis.js index 8102a5a..6dee457 100644 --- a/src/shared/dbUtilis.js +++ b/src/shared/dbUtilis.js @@ -52,11 +52,15 @@ function genrateKey(filePath) { } /** - * parsh the toc provided by epubjs to required form by element-ui tree component - * @param {Object} toc The object of book + * Parsh the toc provided by epubjs to required form by element-ui tree component + * and store inforamtion related to toc item such as lable, href, cfi and percent. + * @param {Object} Book - EPUBJS Book needed to be ready * @returns {Array} returns array of toc tree that easily adopted by el-tree */ -function parshToc(toc) { +function parshToc(book) { + const { toc } = book.navigation; + const { spine } = book; + /** * some epubs not uese standerd href or epubjs fails to process them * @param {String} href The href to validate @@ -72,6 +76,30 @@ function parshToc(toc) { return href; }; + /** + * Return spin part from href + * + * TL;DR + * Toc item points exact postion of chapter or subChapter by using hase ID + * in href. In more genrale href looks like ch001#title. + * The ch001 is spine item and title is element id for which tocitem is. + * We can get cfi of toc from this two item. + * + * @param {String} href - The herf to get spine component + * @returns {String} - The Spine item href + */ + const getSpineComponent = href => { + return href.split('#')[0]; + }; + + /** + * Returns elementId part of href + * @param {String} href + */ + const getPositonComponent = href => { + return href.split('#')[1]; + }; + const tocTree = []; /** @@ -81,15 +109,38 @@ function parshToc(toc) { */ const createTree = (toc, parrent) => { for (let i = 0; i < toc.length; i += 1) { - parrent[i] = { - label: toc[i].label.trim(), - children: [], - href: validateHref(toc[i].href), - }; - - if (toc[i].subitems) { - createTree(toc[i].subitems, parrent[i].children); - } + // get clean href + const href = validateHref(toc[i].href); + + // get spin and elementId part from href + const spineComponent = getSpineComponent(href); + const positonComponent = getPositonComponent(href); + + // get spinItem from href + const spineItem = spine.get(spineComponent); + + // load spin item + spineItem.load(book.load.bind(book)).then(() => { + // get element by positionComponent which is basically elementId + const el = spineItem.document.getElementById(positonComponent); + // get cfi from element + const cfi = spineItem.cfiFromElement(el); + // get percent from cfi + const percentage = book.locations.percentageFromCfi(cfi); + // toc item which has + parrent[i] = { + label: toc[i].label.trim(), + children: [], + href, + cfi, + percentage, + }; + + // if toc has subitems recursively parsh it + if (toc[i].subitems) { + createTree(toc[i].subitems, parrent[i].children); + } + }); } }; @@ -132,9 +183,10 @@ function getInfo(filePath, callback) { bookmarks: [], highlights: [], bgColorFromCover: '', - toc: parshToc(book.navigation.toc), + toc: parshToc(book), locations, }; + if (callback) { callback(info, book); } @@ -197,4 +249,4 @@ function addToDB(file, db, cb) { }); } -export { addToDB, storeCover, genrateKey, getInfo }; +export { addToDB, storeCover, genrateKey, getInfo, parshToc }; diff --git a/src/shared/themes.js b/src/shared/themes.js new file mode 100644 index 0000000..b2aed09 --- /dev/null +++ b/src/shared/themes.js @@ -0,0 +1,44 @@ +const dark = { + body: { + background: `#444 !important`, + color: `#fff !important`, + }, + '*': { + color: 'inherit !important', + background: 'inherit !important', + }, + 'a:link': { + color: `#1e83d2 !important`, + 'text-decoration': 'none !important', + }, + 'a:link:hover': { + background: 'rgba(0, 0, 0, 0.1) !important', + }, +}; + +const light = { + body: {}, + '*': {}, + 'a:link': {}, + 'a:link:hover': {}, +}; + +const tan = { + body: { + background: `#fdf6e3 !important`, + color: `#002b36 !important`, + }, + '*': { + color: 'inherit !important', + background: 'inherit !important', + }, + 'a:link': { + color: `#268bd2 !important`, + 'text-decoration': 'none !important', + }, + 'a:link:hover': { + background: 'rgba(0, 0, 0, 0.1) !important', + }, +}; + +export { dark, light, tan }; diff --git a/yarn.lock b/yarn.lock index c3c241b..2839ade 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1474,11 +1474,6 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= -asap@~2.0.3: - version "2.0.6" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" - integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= - asn1.js@^4.0.0: version "4.10.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" @@ -2392,11 +2387,6 @@ clone-deep@^2.0.1: kind-of "^6.0.0" shallow-clone "^1.0.0" -clone@^2.1.1, clone@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" - integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= - code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" @@ -3376,7 +3366,7 @@ epubjs@^0.3.84: url-polyfill "^1.1.3" xmldom "^0.1.27" -errno@^0.1.1, errno@^0.1.3, errno@~0.1.7: +errno@^0.1.3, errno@~0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg== @@ -4742,11 +4732,6 @@ ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -image-size@~0.5.0: - version "0.5.5" - resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" - integrity sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w= - immediate@~3.0.5: version "3.0.6" resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" @@ -5436,31 +5421,6 @@ lcid@^2.0.0: dependencies: invert-kv "^2.0.0" -less-loader@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-4.1.0.tgz#2c1352c5b09a4f84101490274fd51674de41363e" - integrity sha512-KNTsgCE9tMOM70+ddxp9yyt9iHqgmSs0yTZc5XH5Wo+g80RWRIYNqE58QJKm/yMud5wZEvz50ugRDuzVIkyahg== - dependencies: - clone "^2.1.1" - loader-utils "^1.1.0" - pify "^3.0.0" - -less@^3.9.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/less/-/less-3.9.0.tgz#b7511c43f37cf57dc87dffd9883ec121289b1474" - integrity sha512-31CmtPEZraNUtuUREYjSqRkeETFdyEHSEPAGq4erDlUXtda7pzNmctdljdIagSb589d/qXGWiiP31R5JVf+v0w== - dependencies: - clone "^2.1.2" - optionalDependencies: - errno "^0.1.1" - graceful-fs "^4.1.2" - image-size "~0.5.0" - mime "^1.4.1" - mkdirp "^0.5.0" - promise "^7.1.1" - request "^2.83.0" - source-map "~0.6.0" - levn@^0.3.0, levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" @@ -5802,7 +5762,7 @@ mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24: dependencies: mime-db "1.40.0" -mime@1.6.0, mime@^1.3.4, mime@^1.4.1: +mime@1.6.0, mime@^1.3.4: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== @@ -6109,7 +6069,7 @@ node-releases@^1.1.21: dependencies: semver "^5.3.0" -node-sass@^4.11.0: +node-sass@^4.12.0: version "4.12.0" resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.12.0.tgz#0914f531932380114a30cc5fa4fa63233a25f017" integrity sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ== @@ -6919,13 +6879,6 @@ promise-inflight@^1.0.1: resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= -promise@^7.1.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" - integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== - dependencies: - asap "~2.0.3" - proxy-addr@~2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34" @@ -7316,7 +7269,7 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -request@^2.45.0, request@^2.83.0, request@^2.87.0, request@^2.88.0: +request@^2.45.0, request@^2.87.0, request@^2.88.0: version "2.88.0" resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==