diff --git a/Splittermond/SL_Frust.png b/packs/Splittermond/SL_Frust.png similarity index 100% rename from Splittermond/SL_Frust.png rename to packs/Splittermond/SL_Frust.png diff --git a/packs/Splittermond/pack.json b/packs/Splittermond/pack.json new file mode 100644 index 0000000..c690836 --- /dev/null +++ b/packs/Splittermond/pack.json @@ -0,0 +1 @@ +{"title": "Splittermond", "id": "Splittermond", "stickers": [{"body": "SL_Frust", "url": "mxc://matrix.org/HbIcgjOqyyXxaIxXHSuWjkos", "info": {"w": 154, "h": 146, "size": 30700, "mimetype": "image/png", "thumbnail_url": "mxc://matrix.org/HbIcgjOqyyXxaIxXHSuWjkos", "thumbnail_info": {"w": 154, "h": 146, "size": 30700, "mimetype": "image/png"}}, "msgtype": "m.sticker", "id": "sha256:647fab3645fe41f5c07f79410a04a1b7695484742db84dd06b63b4a43ca5fcb7"}]} \ No newline at end of file diff --git a/web/esinstall.js b/web/esinstall.js new file mode 100644 index 0000000..8cb5682 --- /dev/null +++ b/web/esinstall.js @@ -0,0 +1,23 @@ +const { install, printStats } = require("esinstall") + +install( + [{ + specifier: "htm/preact", + all: false, + default: false, + namespace: false, + named: ["html", "render", "Component"], + }], + { + dest: "./lib", + sourceMap: false, + treeshake: true, + verbose: true, + } +).then(data => { + const oldPrefix = "web_modules/" + const newPrefix = "lib/" + const spaces = " ".repeat(oldPrefix.length - newPrefix.length) + console.log("Installation complete") + console.log(printStats(data.stats).replace(oldPrefix, newPrefix + spaces)) +}) diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..6312814 --- /dev/null +++ b/web/index.html @@ -0,0 +1,22 @@ + + + + + + Maunium sticker picker + + + + + + + + + + + + + + + + diff --git a/web/lib/htm/preact.js b/web/lib/htm/preact.js new file mode 100644 index 0000000..4213116 --- /dev/null +++ b/web/lib/htm/preact.js @@ -0,0 +1,7 @@ +var n,l,u,t,o,r,e={},c=[],s=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;function a(n,l){for(var u in l)n[u]=l[u];return n}function h(n){var l=n.parentNode;l&&l.removeChild(n);}function v(l,u,i){var t,o,r,f={};for(r in u)"key"==r?t=u[r]:"ref"==r?o=u[r]:f[r]=u[r];if(arguments.length>2&&(f.children=arguments.length>3?n.call(arguments,2):i),"function"==typeof l&&null!=l.defaultProps)for(r in l.defaultProps)void 0===f[r]&&(f[r]=l.defaultProps[r]);return y(l,f,t,o,null)}function y(n,i,t,o,r){var f={type:n,props:i,key:t,ref:o,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,__h:null,constructor:void 0,__v:null==r?++u:r};return null!=l.vnode&&l.vnode(f),f}function d(n){return n.children}function _(n,l){this.props=n,this.context=l;}function k(n,l){if(null==l)return n.__?k(n.__,n.__.__k.indexOf(n)+1):null;for(var u;l0?y(_.type,_.props,_.key,null,_.__v):_)){if(_.__=u,_.__b=u.__b+1,null===(p=w[h])||p&&_.key==p.key&&_.type===p.type)w[h]=void 0;else for(v=0;v=5&&((e||!n&&5===r)&&(h.push(r,0,e,s),r=6),n&&(h.push(r,n,0,s),r=6)),e="";},a=0;a"===t?(r=1,e=""):e=t+e[0]:u?t===u?u="":e+=t:'"'===t||"'"===t?u=t:">"===t?(p(),r=1):r&&("="===t?(r=5,s=e,e=""):"/"===t&&(r<5||">"===n[a][l+1])?(p(),3===r&&(h=h[0]),r=h,(h=h[0]).push(2,0,r),r=0):" "===t||"\t"===t||"\n"===t||"\r"===t?(p(),r=2):e+=t),3===r&&"!--"===e&&(r=4,h=h[0]);}return p(),h}(s)),r),arguments,[])).length>1?r:r[0]} + +var m$1=e$1.bind(v); + +export { _ as Component, m$1 as html, S as render }; diff --git a/web/package.json b/web/package.json new file mode 100644 index 0000000..b2b7f90 --- /dev/null +++ b/web/package.json @@ -0,0 +1,19 @@ +{ + "name": "maunium-stickerpicker", + "version": "0.1.0", + "description": "A fast and simple Matrix sticker picker widget", + "repository": "https://github.com/maunium/stickerpicker", + "author": "Tulir Asokan ", + "license": "AGPL-3.0-or-later", + "private": true, + "scripts": { + "esinstall": "node ./esinstall.js", + "sass": "sass --no-source-map --style=compressed style/" + }, + "dependencies": { + "htm": "^3.1.0", + "preact": "^10.5.14", + "esinstall": "^1.1.7", + "sass": "^1.42.1" + } +} diff --git a/web/packs/.gitkeep b/web/packs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/web/packs/Splittermond.json b/web/packs/Splittermond.json new file mode 100644 index 0000000..c690836 --- /dev/null +++ b/web/packs/Splittermond.json @@ -0,0 +1 @@ +{"title": "Splittermond", "id": "Splittermond", "stickers": [{"body": "SL_Frust", "url": "mxc://matrix.org/HbIcgjOqyyXxaIxXHSuWjkos", "info": {"w": 154, "h": 146, "size": 30700, "mimetype": "image/png", "thumbnail_url": "mxc://matrix.org/HbIcgjOqyyXxaIxXHSuWjkos", "thumbnail_info": {"w": 154, "h": 146, "size": 30700, "mimetype": "image/png"}}, "msgtype": "m.sticker", "id": "sha256:647fab3645fe41f5c07f79410a04a1b7695484742db84dd06b63b4a43ca5fcb7"}]} \ No newline at end of file diff --git a/web/packs/index.json b/web/packs/index.json new file mode 100644 index 0000000..069aac5 --- /dev/null +++ b/web/packs/index.json @@ -0,0 +1,6 @@ +{ + "packs": [ + "Splittermond.json" + ], + "homeserver_url": "https://matrix-client.matrix.org" +} \ No newline at end of file diff --git a/web/res/favorite.svg b/web/res/favorite.svg new file mode 100644 index 0000000..4af2b49 --- /dev/null +++ b/web/res/favorite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/res/recent.svg b/web/res/recent.svg new file mode 100644 index 0000000..59be87d --- /dev/null +++ b/web/res/recent.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/res/search.svg b/web/res/search.svg new file mode 100644 index 0000000..7be3396 --- /dev/null +++ b/web/res/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/res/settings.svg b/web/res/settings.svg new file mode 100644 index 0000000..51f966f --- /dev/null +++ b/web/res/settings.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/src/frequently-used.js b/web/src/frequently-used.js new file mode 100644 index 0000000..a754e9a --- /dev/null +++ b/web/src/frequently-used.js @@ -0,0 +1,34 @@ +// maunium-stickerpicker - A fast and simple Matrix sticker picker widget. +// Copyright (C) 2020 Tulir Asokan +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +const FREQUENTLY_USED = JSON.parse(window.localStorage.mauFrequentlyUsedStickerIDs || "{}") +let FREQUENTLY_USED_SORTED = null + +export const add = id => { + const [count] = FREQUENTLY_USED[id] || [0] + FREQUENTLY_USED[id] = [count + 1, Date.now()] + window.localStorage.mauFrequentlyUsedStickerIDs = JSON.stringify(FREQUENTLY_USED) + FREQUENTLY_USED_SORTED = null +} + +export const get = (limit = 16) => { + if (FREQUENTLY_USED_SORTED === null) { + FREQUENTLY_USED_SORTED = Object.entries(FREQUENTLY_USED) + .sort(([, [count1, date1]], [, [count2, date2]]) => + count2 === count1 ? date2 - date1 : count2 - count1) + .map(([emoji]) => emoji) + } + return FREQUENTLY_USED_SORTED.slice(0, limit) +} diff --git a/web/src/index.js b/web/src/index.js new file mode 100644 index 0000000..a215c46 --- /dev/null +++ b/web/src/index.js @@ -0,0 +1,363 @@ +// maunium-stickerpicker - A fast and simple Matrix sticker picker widget. +// Copyright (C) 2020 Tulir Asokan +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +import { html, render, Component } from "../lib/htm/preact.js" +import { Spinner } from "./spinner.js" +import { SearchBox } from "./search-box.js" +import * as widgetAPI from "./widget-api.js" +import * as frequent from "./frequently-used.js" + +// The base URL for fetching packs. The app will first fetch ${PACK_BASE_URL}/index.json, +// then ${PACK_BASE_URL}/${packFile} for each packFile in the packs object of the index.json file. +const PACKS_BASE_URL = "packs" + +let INDEX = `${PACKS_BASE_URL}/index.json` +const params = new URLSearchParams(document.location.search) +if (params.has('config')) { + INDEX = params.get("config") +} +// This is updated from packs/index.json +let HOMESERVER_URL = "https://matrix-client.matrix.org" + +const makeThumbnailURL = mxc => `${HOMESERVER_URL}/_matrix/media/r0/thumbnail/${mxc.substr(6)}?height=128&width=128&method=scale` + +// We need to detect iOS webkit because it has a bug related to scrolling non-fixed divs +// This is also used to fix scrolling to sections on Element iOS +const isMobileSafari = navigator.userAgent.match(/(iPod|iPhone|iPad)/) && navigator.userAgent.match(/AppleWebKit/) + +const supportedThemes = ["light", "dark", "black"] + +const defaultState = { + packs: [], + filtering: { + searchTerm: "", + packs: [], + }, +} + +class App extends Component { + constructor(props) { + super(props) + this.defaultTheme = params.get("theme") + this.state = { + packs: defaultState.packs, + loading: true, + error: null, + stickersPerRow: parseInt(localStorage.mauStickersPerRow || "4"), + theme: localStorage.mauStickerThemeOverride || this.defaultTheme, + frequentlyUsed: { + id: "frequently-used", + title: "Frequently used", + stickerIDs: frequent.get(), + stickers: [], + }, + filtering: defaultState.filtering, + } + if (!supportedThemes.includes(this.state.theme)) { + this.state.theme = "light" + } + if (!supportedThemes.includes(this.defaultTheme)) { + this.defaultTheme = "light" + } + this.stickersByID = new Map(JSON.parse(localStorage.mauFrequentlyUsedStickerCache || "[]")) + this.state.frequentlyUsed.stickers = this._getStickersByID(this.state.frequentlyUsed.stickerIDs) + this.imageObserver = null + this.packListRef = null + this.navRef = null + this.searchStickers = this.searchStickers.bind(this) + this.sendSticker = this.sendSticker.bind(this) + this.navScroll = this.navScroll.bind(this) + this.reloadPacks = this.reloadPacks.bind(this) + this.observeSectionIntersections = this.observeSectionIntersections.bind(this) + this.observeImageIntersections = this.observeImageIntersections.bind(this) + } + + _getStickersByID(ids) { + return ids.map(id => this.stickersByID.get(id)).filter(sticker => !!sticker) + } + + updateFrequentlyUsed() { + const stickerIDs = frequent.get() + const stickers = this._getStickersByID(stickerIDs) + this.setState({ + frequentlyUsed: { + ...this.state.frequentlyUsed, + stickerIDs, + stickers, + }, + }) + localStorage.mauFrequentlyUsedStickerCache = JSON.stringify(stickers.map(sticker => [sticker.id, sticker])) + } + + searchStickers(e) { + const sanitizeString = s => s.toLowerCase().trim() + const searchTerm = sanitizeString(e.target.value) + + const allPacks = [this.state.frequentlyUsed, ...this.state.packs] + const packsWithFilteredStickers = allPacks.map(pack => ({ + ...pack, + stickers: pack.stickers.filter(sticker => + sanitizeString(sticker.body).includes(searchTerm) || + sanitizeString(sticker.id).includes(searchTerm) + ), + })) + + this.setState({ + filtering: { + ...this.state.filtering, + searchTerm, + packs: packsWithFilteredStickers.filter(({ stickers }) => !!stickers.length), + }, + }) + } + + setStickersPerRow(val) { + localStorage.mauStickersPerRow = val + document.documentElement.style.setProperty("--stickers-per-row", localStorage.mauStickersPerRow) + this.setState({ + stickersPerRow: val, + }) + this.packListRef.scrollTop = this.packListRef.scrollHeight + } + + setTheme(theme) { + if (theme === "default") { + delete localStorage.mauStickerThemeOverride + this.setState({ theme: this.defaultTheme }) + } else { + localStorage.mauStickerThemeOverride = theme + this.setState({ theme: theme }) + } + } + + reloadPacks() { + this.imageObserver.disconnect() + this.sectionObserver.disconnect() + this.setState({ + packs: defaultState.packs, + filtering: defaultState.filtering, + }) + this._loadPacks(true) + } + + _loadPacks(disableCache = false) { + const cache = disableCache ? "no-cache" : undefined + fetch(INDEX, { cache }).then(async indexRes => { + if (indexRes.status >= 400) { + this.setState({ + loading: false, + error: indexRes.status !== 404 ? indexRes.statusText : null, + }) + return + } + const indexData = await indexRes.json() + HOMESERVER_URL = indexData.homeserver_url || HOMESERVER_URL + // TODO only load pack metadata when scrolled into view? + for (const packFile of indexData.packs) { + let packRes + if (packFile.startsWith("https://") || packFile.startsWith("http://")) { + packRes = await fetch(packFile, { cache }) + } else { + packRes = await fetch(`${PACKS_BASE_URL}/${packFile}`, { cache }) + } + const packData = await packRes.json() + for (const sticker of packData.stickers) { + this.stickersByID.set(sticker.id, sticker) + } + this.setState({ + packs: [...this.state.packs, packData], + loading: false, + }) + } + this.updateFrequentlyUsed() + }, error => this.setState({ loading: false, error })) + } + + componentDidMount() { + document.documentElement.style.setProperty("--stickers-per-row", this.state.stickersPerRow.toString()) + this._loadPacks() + this.imageObserver = new IntersectionObserver(this.observeImageIntersections, { + rootMargin: "100px", + }) + this.sectionObserver = new IntersectionObserver(this.observeSectionIntersections) + } + + observeImageIntersections(intersections) { + for (const entry of intersections) { + const img = entry.target.children.item(0) + if (entry.isIntersecting) { + img.setAttribute("src", img.getAttribute("data-src")) + img.classList.add("visible") + } else { + img.removeAttribute("src") + img.classList.remove("visible") + } + } + } + + observeSectionIntersections(intersections) { + const navWidth = this.navRef.getBoundingClientRect().width + let minX = 0, maxX = navWidth + let minXElem = null + let maxXElem = null + for (const entry of intersections) { + const packID = entry.target.getAttribute("data-pack-id") + const navElement = document.getElementById(`nav-${packID}`) + if (entry.isIntersecting) { + navElement.classList.add("visible") + const bb = navElement.getBoundingClientRect() + if (bb.x < minX) { + minX = bb.x + minXElem = navElement + } else if (bb.right > maxX) { + maxX = bb.right + maxXElem = navElement + } + } else { + navElement.classList.remove("visible") + } + } + if (minXElem !== null) { + minXElem.scrollIntoView({ inline: "start" }) + } else if (maxXElem !== null) { + maxXElem.scrollIntoView({ inline: "end" }) + } + } + + componentDidUpdate() { + if (this.packListRef === null) { + return + } + for (const elem of this.packListRef.getElementsByClassName("sticker")) { + this.imageObserver.observe(elem) + } + for (const elem of this.packListRef.children) { + this.sectionObserver.observe(elem) + } + } + + componentWillUnmount() { + this.imageObserver.disconnect() + this.sectionObserver.disconnect() + } + + sendSticker(evt) { + const id = evt.currentTarget.getAttribute("data-sticker-id") + const sticker = this.stickersByID.get(id) + frequent.add(id) + this.updateFrequentlyUsed() + widgetAPI.sendSticker(sticker) + } + + navScroll(evt) { + this.navRef.scrollLeft += evt.deltaY + } + + render() { + const theme = `theme-${this.state.theme}` + const filterActive = !!this.state.filtering.searchTerm + const packs = filterActive ? this.state.filtering.packs : [this.state.frequentlyUsed, ...this.state.packs] + + if (this.state.loading) { + return html`
<${Spinner} size=${80} green />
` + } else if (this.state.error) { + return html`
+

Failed to load packs

+

${this.state.error}

+
` + } else if (this.state.packs.length === 0) { + return html`

No packs found 😿

` + } + + return html`
+ + <${SearchBox} onKeyUp=${this.searchStickers} /> +
this.packListRef = elem}> + ${filterActive && packs.length === 0 ? html`

No stickers match your search

` : null} + ${packs.map(pack => html`<${Pack} id=${pack.id} pack=${pack} send=${this.sendSticker} />`)} + <${Settings} app=${this}/> +
+
` + } +} + +const Settings = ({ app }) => html` +
+

Settings

+
+ +
+ + app.setStickersPerRow(evt.target.value)} /> +
+
+ + +
+
+
+` + +// By default we just let the browser handle scrolling to sections, but webviews on Element iOS +// open the link in the browser instead of just scrolling there, so we need to scroll manually: +const scrollToSection = (evt, id) => { + const pack = document.getElementById(`pack-${id}`) + pack.scrollIntoView({ block: "start", behavior: "instant" }) + evt.preventDefault() +} + +const NavBarItem = ({ pack, iconOverride = null }) => html` + scrollToSection(evt, pack.id)) : undefined}> +
+ ${iconOverride ? html` + + ` : html` + ${pack.stickers[0].body} + `} +
+
+` + +const Pack = ({ pack, send }) => html` +
+

${pack.title}

+
+ ${pack.stickers.map(sticker => html` + <${Sticker} key=${sticker.id} content=${sticker} send=${send}/> + `)} +
+
+` + +const Sticker = ({ content, send }) => html` +
+ ${content.body} +
+` + +render(html`<${App} />`, document.body) diff --git a/web/src/search-box.js b/web/src/search-box.js new file mode 100644 index 0000000..ba2ed5d --- /dev/null +++ b/web/src/search-box.js @@ -0,0 +1,26 @@ +// maunium-stickerpicker - A fast and simple Matrix sticker picker widget. +// Copyright (C) 2020 Tulir Asokan +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +import { html } from "../lib/htm/preact.js" + +export const SearchBox = ({ onKeyUp, placeholder = 'Find stickers' }) => { + const component = html` + + ` + return component +} diff --git a/web/src/spinner.js b/web/src/spinner.js new file mode 100644 index 0000000..e89dd02 --- /dev/null +++ b/web/src/spinner.js @@ -0,0 +1,41 @@ +// maunium-stickerpicker - A fast and simple Matrix sticker picker widget. +// Copyright (C) 2020 Tulir Asokan +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +import { html } from "../lib/htm/preact.js" + +export const Spinner = ({ size = 40, noCenter = false, noMargin = false, green = false }) => { + let margin = 0 + if (!isNaN(+size)) { + size = +size + margin = noMargin ? 0 : `${Math.round(size / 6)}px` + size = `${size}px` + } + const noInnerMargin = !noCenter || !margin + const comp = html` +
+
+
+
+
+
+
+
+ ` + if (!noCenter) { + return html`
${comp}
` + } + return comp +} diff --git a/web/src/widget-api.js b/web/src/widget-api.js new file mode 100644 index 0000000..fa72165 --- /dev/null +++ b/web/src/widget-api.js @@ -0,0 +1,76 @@ +// maunium-stickerpicker - A fast and simple Matrix sticker picker widget. +// Copyright (C) 2020 Tulir Asokan +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +let widgetId = null + +window.onmessage = event => { + if (!window.parent || !event.data) { + return + } + + const request = event.data + if (!request.requestId || !request.widgetId || !request.action || request.api !== "toWidget") { + return + } + + if (widgetId) { + if (widgetId !== request.widgetId) { + return + } + } else { + widgetId = request.widgetId + } + + let response + + if (request.action === "visibility") { + response = {} + } else if (request.action === "capabilities") { + response = { capabilities: ["m.sticker"] } + } else { + response = { error: { message: "Action not supported" } } + } + + window.parent.postMessage({ ...request, response }, event.origin) +} + +export function sendSticker(content) { + const data = { + content: { ...content }, + // `name` is for Element Web (and also the spec) + // Element Android uses content -> body as the name + name: content.body, + } + // Custom field that stores the ID even for non-telegram stickers + delete data.content.id + + // This is for Element iOS + const widgetData = { + ...data, + description: content.body, + file: `${content.id}.png`, + } + // Element iOS explodes if there are extra fields present + delete widgetData.content["net.maunium.telegram.sticker"] + + window.parent.postMessage({ + api: "fromWidget", + action: "m.sticker", + requestId: `sticker-${Date.now()}`, + widgetId, + data, + widgetData, + }, "*") +} diff --git a/web/style/index.css b/web/style/index.css new file mode 100644 index 0000000..e33e450 --- /dev/null +++ b/web/style/index.css @@ -0,0 +1 @@ +*{font-family:sans-serif}body{margin:0}h1{font-size:1rem}:root{--stickers-per-row: 4;--sticker-size: calc(100vw / var(--stickers-per-row))}main{color:var(--text-color)}main.spinner{margin-top:5rem}main.error,main.empty{margin:2rem}main.empty{text-align:center}main.has-content{position:fixed;top:0;left:0;right:0;bottom:0;display:grid;grid-template-rows:calc(12vw + 2px) min-content auto}main.theme-light{--highlight-color: #eee;--search-box-color: var(--highlight-color);--text-color: black;background-color:#fff}main.theme-dark{--highlight-color: #444;--search-box-color: #383e4b;--text-color: white;background-color:#22262e}main.theme-black{--highlight-color: #222;--search-box-color: var(--highlight-color);--text-color: white;background-color:#000}.icon{width:100%;height:100%;background-color:var(--text-color);mask-size:contain;-webkit-mask-size:contain;mask-image:var(--icon-image);-webkit-mask-image:var(--icon-image)}.icon.icon-settings{--icon-image: url(../res/settings.svg)}.icon.icon-recent{--icon-image: url(../res/recent.svg)}.icon.icon.icon-search{--icon-image: url(../res/search.svg)}nav{display:flex;overflow-x:auto}nav>a{border-bottom:2px solid transparent}nav>a.visible{border-bottom-color:green}nav>a>div.sticker{width:12vw;height:12vw}div.pack-list,nav{scrollbar-width:none}div.pack-list::-webkit-scrollbar,nav::-webkit-scrollbar{display:none}div.pack-list{overflow-y:auto}div.pack-list.ios-safari-hack{position:fixed;top:calc(calc(12vw + 2px) + calc(2 * 0.7rem + 2 * 0.5rem + 1rem));bottom:0;left:0;right:0;-webkit-overflow-scrolling:touch}div.search-empty{margin:1.2rem;text-align:center}section.stickerpack{margin-top:.75rem}section.stickerpack>div.sticker-list{display:flex;flex-wrap:wrap}section.stickerpack>h1{margin:0 0 0 .75rem}div.sticker{display:flex;padding:4px;cursor:pointer;position:relative;width:var(--sticker-size);height:var(--sticker-size);box-sizing:border-box}div.sticker:hover{background-color:var(--highlight-color)}div.sticker>img{display:none;width:100%;object-fit:contain}div.sticker>img.visible{display:initial}div.sticker>.icon{width:70%;height:70%;margin:15%}div.search-box{position:relative;display:flex}div.search-box>input[type=text]{flex-grow:1;background-color:var(--search-box-color);outline:none;border:none;border-radius:.25rem;height:1rem;padding:.7rem;padding-right:calc(1rem + 0.7rem);margin:.5rem;font-size:1rem;color:var(--text-color)}div.search-box>span.icon{display:flex;position:absolute;top:calc(50% - 1rem / 2);right:1rem;width:1rem;height:1rem;box-sizing:border-box}div.settings-list{display:flex;flex-direction:column}div.settings-list>*{margin:.5rem}div.settings-list button{padding:.5rem;border-radius:.25rem}div.settings-list input{width:100%} diff --git a/web/style/index.sass b/web/style/index.sass new file mode 100644 index 0000000..c079f65 --- /dev/null +++ b/web/style/index.sass @@ -0,0 +1,206 @@ +// maunium-stickerpicker - A fast and simple Matrix sticker picker widget. +// Copyright (C) 2020 Tulir Asokan +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +* + font-family: sans-serif + +body + margin: 0 + +h1 + font-size: 1rem + +\:root + --stickers-per-row: 4 + --sticker-size: calc(100vw / var(--stickers-per-row)) + +$nav-sticker-size: 12vw +$nav-bottom-highlight: 2px +$nav-height: calc(#{$nav-sticker-size} + #{$nav-bottom-highlight}) +$nav-height-inverse: calc(-#{$nav-sticker-size} - #{$nav-bottom-highlight}) + +$search-box-icon-size: 1rem +$search-box-input-height: 1rem +$search-box-input-padding: .7rem +$search-box-input-margin: .5rem +$search-box-height: calc(2 * #{$search-box-input-padding} + 2 * #{$search-box-input-margin} + #{$search-box-input-height}) + +main + color: var(--text-color) + + &.spinner + margin-top: 5rem + + &.error, &.empty + margin: 2rem + + &.empty + text-align: center + + &.has-content + position: fixed + top: 0 + left: 0 + right: 0 + bottom: 0 + display: grid + grid-template-rows: $nav-height min-content auto + +main.theme-light + --highlight-color: #eee + --search-box-color: var(--highlight-color) + --text-color: black + background-color: white + +main.theme-dark + --highlight-color: #444 + --search-box-color: #383e4b + --text-color: white + background-color: #22262e + +main.theme-black + --highlight-color: #222 + --search-box-color: var(--highlight-color) + --text-color: white + background-color: black + +.icon + width: 100% + height: 100% + background-color: var(--text-color) + mask-size: contain + -webkit-mask-size: contain + mask-image: var(--icon-image) + -webkit-mask-image: var(--icon-image) + + &.icon-settings + --icon-image: url(../res/settings.svg) + + &.icon-recent + --icon-image: url(../res/recent.svg) + + &.icon.icon-search + --icon-image: url(../res/search.svg) + +nav + display: flex + overflow-x: auto + + > a + border-bottom: $nav-bottom-highlight solid transparent + + &.visible + border-bottom-color: green + + > div.sticker + width: $nav-sticker-size + height: $nav-sticker-size + +div.pack-list, nav + scrollbar-width: none + + &::-webkit-scrollbar + display: none + +div.pack-list + overflow-y: auto + +div.pack-list.ios-safari-hack + position: fixed + top: calc(#{$nav-height} + #{$search-box-height}) + bottom: 0 + left: 0 + right: 0 + -webkit-overflow-scrolling: touch + +div.search-empty + margin: 1.2rem + text-align: center + +section.stickerpack + margin-top: .75rem + + > div.sticker-list + display: flex + flex-wrap: wrap + + > h1 + margin: 0 0 0 .75rem + +div.sticker + display: flex + padding: 4px + cursor: pointer + position: relative + width: var(--sticker-size) + height: var(--sticker-size) + box-sizing: border-box + + &:hover + background-color: var(--highlight-color) + + > img + display: none + width: 100% + object-fit: contain + + &.visible + display: initial + + > .icon + width: 70% + height: 70% + margin: 15% + +div.search-box + position: relative + display: flex + + >input[type="text"] + flex-grow: 1 + background-color: var(--search-box-color) + outline: none + border: none + border-radius: .25rem + height: $search-box-input-height + padding: $search-box-input-padding + padding-right: calc(#{$search-box-icon-size} + #{$search-box-input-padding}) + margin: $search-box-input-margin + font-size: 1rem + color: var(--text-color) + + >span.icon + display: flex + position: absolute + top: calc(50% - #{$search-box-icon-size} / 2) + right: $search-box-icon-size + width: $search-box-icon-size + height: $search-box-icon-size + box-sizing: border-box + +div.settings-list + display: flex + flex-direction: column + + > * + margin: .5rem + + button + padding: .5rem + border-radius: .25rem + + input + width: 100% diff --git a/web/style/spinner.css b/web/style/spinner.css new file mode 100644 index 0000000..de03d9c --- /dev/null +++ b/web/style/spinner.css @@ -0,0 +1 @@ +.sk-center-wrapper{width:100%;display:flex;justify-content:space-around}.sk-chase{position:relative;animation:sk-chase 2.5s infinite linear both}.sk-chase.green>.sk-chase-dot:before{background-color:#00c853}.sk-chase>.sk-chase-dot{width:100%;height:100%;position:absolute;left:0;top:0;animation:sk-chase-dot 2s infinite ease-in-out both}.sk-chase>.sk-chase-dot:before{content:"";display:block;width:25%;height:25%;border-radius:100%;animation:sk-chase-dot-before 2s infinite ease-in-out both;background-color:#fff}.sk-chase>.sk-chase-dot:nth-child(1){animation-delay:-1.1s}.sk-chase>.sk-chase-dot:nth-child(2){animation-delay:-1s}.sk-chase>.sk-chase-dot:nth-child(3){animation-delay:-0.9s}.sk-chase>.sk-chase-dot:nth-child(4){animation-delay:-0.8s}.sk-chase>.sk-chase-dot:nth-child(5){animation-delay:-0.7s}.sk-chase>.sk-chase-dot:nth-child(6){animation-delay:-0.6s}.sk-chase>.sk-chase-dot:nth-child(1):before{animation-delay:-1.1s}.sk-chase>.sk-chase-dot:nth-child(2):before{animation-delay:-1s}.sk-chase>.sk-chase-dot:nth-child(3):before{animation-delay:-0.9s}.sk-chase>.sk-chase-dot:nth-child(4):before{animation-delay:-0.8s}.sk-chase>.sk-chase-dot:nth-child(5):before{animation-delay:-0.7s}.sk-chase>.sk-chase-dot:nth-child(6):before{animation-delay:-0.6s}@keyframes sk-chase{100%{transform:rotate(360deg)}}@keyframes sk-chase-dot{80%,100%{transform:rotate(360deg)}}@keyframes sk-chase-dot-before{50%{transform:scale(0.4)}100%,0%{transform:scale(1)}} diff --git a/web/style/spinner.sass b/web/style/spinner.sass new file mode 100644 index 0000000..cbc9042 --- /dev/null +++ b/web/style/spinner.sass @@ -0,0 +1,68 @@ +// Chase spinner from https://tobiasahlin.com/spinkit/. MIT license +.sk-center-wrapper + width: 100% + display: flex + justify-content: space-around + +.sk-chase + position: relative + animation: sk-chase 2.5s infinite linear both + + &.green > .sk-chase-dot:before + background-color: #00C853 + + > .sk-chase-dot + width: 100% + height: 100% + position: absolute + left: 0 + top: 0 + animation: sk-chase-dot 2.0s infinite ease-in-out both + + &:before + content: '' + display: block + width: 25% + height: 25% + border-radius: 100% + animation: sk-chase-dot-before 2.0s infinite ease-in-out both + background-color: #FFF + + &:nth-child(1) + animation-delay: -1.1s + &:nth-child(2) + animation-delay: -1.0s + &:nth-child(3) + animation-delay: -0.9s + &:nth-child(4) + animation-delay: -0.8s + &:nth-child(5) + animation-delay: -0.7s + &:nth-child(6) + animation-delay: -0.6s + &:nth-child(1):before + animation-delay: -1.1s + &:nth-child(2):before + animation-delay: -1.0s + &:nth-child(3):before + animation-delay: -0.9s + &:nth-child(4):before + animation-delay: -0.8s + &:nth-child(5):before + animation-delay: -0.7s + &:nth-child(6):before + animation-delay: -0.6s + +@keyframes sk-chase + 100% + transform: rotate(360deg) + +@keyframes sk-chase-dot + 80%, 100% + transform: rotate(360deg) + +@keyframes sk-chase-dot-before + 50% + transform: scale(0.4) + 100%, 0% + transform: scale(1.0) diff --git a/web/yarn.lock b/web/yarn.lock new file mode 100644 index 0000000..20a24ac --- /dev/null +++ b/web/yarn.lock @@ -0,0 +1,583 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@rollup/plugin-commonjs@^16.0.0": + version "16.0.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-16.0.0.tgz#169004d56cd0f0a1d0f35915d31a036b0efe281f" + integrity sha512-LuNyypCP3msCGVQJ7ki8PqYdpjfEkE/xtFa5DqlF+7IBD0JsfMZ87C58heSwIMint58sAUZbt3ITqOmdQv/dXw== + dependencies: + "@rollup/pluginutils" "^3.1.0" + commondir "^1.0.1" + estree-walker "^2.0.1" + glob "^7.1.6" + is-reference "^1.2.1" + magic-string "^0.25.7" + resolve "^1.17.0" + +"@rollup/plugin-inject@^4.0.0", "@rollup/plugin-inject@^4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@rollup/plugin-inject/-/plugin-inject-4.0.2.tgz#55b21bb244a07675f7fdde577db929c82fc17395" + integrity sha512-TSLMA8waJ7Dmgmoc8JfPnwUwVZgLjjIAM6MqeIFqPO2ODK36JqE0Cf2F54UTgCUuW8da93Mvoj75a6KAVWgylw== + dependencies: + "@rollup/pluginutils" "^3.0.4" + estree-walker "^1.0.1" + magic-string "^0.25.5" + +"@rollup/plugin-json@^4.0.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-4.1.0.tgz#54e09867ae6963c593844d8bd7a9c718294496f3" + integrity sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw== + dependencies: + "@rollup/pluginutils" "^3.0.8" + +"@rollup/plugin-node-resolve@^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-10.0.0.tgz#44064a2b98df7530e66acf8941ff262fc9b4ead8" + integrity sha512-sNijGta8fqzwA1VwUEtTvWCx2E7qC70NMsDh4ZG13byAXYigBNZMxALhKUSycBks5gupJdq0lFrKumFrRZ8H3A== + dependencies: + "@rollup/pluginutils" "^3.1.0" + "@types/resolve" "1.17.1" + builtin-modules "^3.1.0" + deepmerge "^4.2.2" + is-module "^1.0.0" + resolve "^1.17.0" + +"@rollup/plugin-replace@^2.4.2": + version "2.4.2" + resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz#a2d539314fbc77c244858faa523012825068510a" + integrity sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg== + dependencies: + "@rollup/pluginutils" "^3.1.0" + magic-string "^0.25.7" + +"@rollup/pluginutils@^3.0.4", "@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" + integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== + dependencies: + "@types/estree" "0.0.39" + estree-walker "^1.0.1" + picomatch "^2.2.2" + +"@types/estree@*": + version "0.0.50" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" + integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== + +"@types/estree@0.0.39": + version "0.0.39" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" + integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== + +"@types/node@*": + version "16.10.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.10.2.tgz#5764ca9aa94470adb4e1185fe2e9f19458992b2e" + integrity sha512-zCclL4/rx+W5SQTzFs9wyvvyCwoK9QtBpratqz2IYJ3O8Umrn0m3nsTv0wQBk9sRGpvUe9CwPDrQFB10f1FIjQ== + +"@types/resolve@1.17.1": + version "1.17.1" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" + integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw== + dependencies: + "@types/node" "*" + +anymatch@~3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +assert@^1.4.1: + version "1.5.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" + integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== + dependencies: + object-assign "^4.1.1" + util "0.10.3" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +builtin-modules@^3.1.0, builtin-modules@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887" + integrity sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA== + +builtins@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" + integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og= + +"chokidar@>=3.0.0 <4.0.0": + version "3.5.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" + integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +cjs-module-lexer@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" + integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + +es-module-lexer@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.6.0.tgz#e72ab05b7412e62b9be37c37a09bdb6000d706f0" + integrity sha512-f8kcHX1ArhllUtb/wVSyvygoKCznIjnxhLxy7TCvIiMdT7fL4ZDTIKaadMe6eLvOXg6Wk02UeoFgUoZ2EKZZUA== + +esinstall@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/esinstall/-/esinstall-1.1.7.tgz#ceabeb4b8685bf48c805a503e292dfafe4e0cb22" + integrity sha512-irDsrIF7fZ5BCQEAV5gmH+4nsK6JhnkI9C9VloXdmzJLbM1EcshPw8Ap95UUGc4ZJdzGeOrjV+jgKjQ/Z7Q3pg== + dependencies: + "@rollup/plugin-commonjs" "^16.0.0" + "@rollup/plugin-inject" "^4.0.2" + "@rollup/plugin-json" "^4.0.0" + "@rollup/plugin-node-resolve" "^10.0.0" + "@rollup/plugin-replace" "^2.4.2" + builtin-modules "^3.2.0" + cjs-module-lexer "^1.2.1" + es-module-lexer "^0.6.0" + execa "^5.1.1" + is-valid-identifier "^2.0.2" + kleur "^4.1.1" + mkdirp "^1.0.3" + picomatch "^2.3.0" + resolve "^1.20.0" + rimraf "^3.0.0" + rollup "~2.37.1" + rollup-plugin-polyfill-node "^0.6.2" + slash "~3.0.0" + validate-npm-package-name "^3.0.0" + vm2 "^3.9.2" + +estree-walker@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" + integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== + +estree-walker@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + +execa@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@~2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== + +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@^7.1.3, glob@^7.1.6: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +htm@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/htm/-/htm-3.1.0.tgz#0c305493b60da9f6ed097a2aaf4c994bd85ea022" + integrity sha512-L0s3Sid5r6YwrEvkig14SK3Emmc+kIjlfLhEGn2Vy3bk21JyDEes4MoDsbJk6luaPp8bugErnxPz86ZuAw6e5Q== + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-core-module@^2.2.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.7.0.tgz#3c0ef7d31b4acfc574f80c58409d568a836848e3" + integrity sha512-ByY+tjCciCr+9nLryBYcSD50EOGWt95c7tIsKTG1J2ixKKXPvF7Ej3AVd+UfDydAJom3biBGDBALaO79ktwgEQ== + dependencies: + has "^1.0.3" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" + integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-reference@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" + integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== + dependencies: + "@types/estree" "*" + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-valid-identifier@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-valid-identifier/-/is-valid-identifier-2.0.2.tgz#146d9dbf29821b8118580b039d2203aa4bd1da4b" + integrity sha512-mpS5EGqXOwzXtKAg6I44jIAqeBfntFLxpAth1rrKbxtKyI6LPktyDYpHBI+tHlduhhX/SF26mFXmxQu995QVqg== + dependencies: + assert "^1.4.1" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +kleur@^4.1.1: + version "4.1.4" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.4.tgz#8c202987d7e577766d039a8cd461934c01cda04d" + integrity sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA== + +magic-string@^0.25.5, magic-string@^0.25.7: + version "0.25.7" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" + integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== + dependencies: + sourcemap-codec "^1.4.4" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" + integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== + +preact@^10.5.14: + version "10.5.14" + resolved "https://registry.yarnpkg.com/preact/-/preact-10.5.14.tgz#0b14a2eefba3c10a57116b90d1a65f5f00cd2701" + integrity sha512-KojoltCrshZ099ksUZ2OQKfbH66uquFoxHSbnwKbTJHeQNvx42EmC7wQVWNuDt6vC5s3nudRHFtKbpY4ijKlaQ== + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +resolve@^1.17.0, resolve@^1.20.0: + version "1.20.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== + dependencies: + is-core-module "^2.2.0" + path-parse "^1.0.6" + +rimraf@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rollup-plugin-polyfill-node@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rollup-plugin-polyfill-node/-/rollup-plugin-polyfill-node-0.6.2.tgz#dea62e00f5cc2c174e4b4654b5daab79b1a92fc3" + integrity sha512-gMCVuR0zsKq0jdBn8pSXN1Ejsc458k2QsFFvQdbHoM0Pot5hEnck+pBP/FDwFS6uAi77pD3rDTytsaUStsOMlA== + dependencies: + "@rollup/plugin-inject" "^4.0.0" + +rollup@~2.37.1: + version "2.37.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.37.1.tgz#aa7aadffd75c80393f9314f9857e851b0ffd34e7" + integrity sha512-V3ojEeyGeSdrMSuhP3diBb06P+qV4gKQeanbDv+Qh/BZbhdZ7kHV0xAt8Yjk4GFshq/WjO7R4c7DFM20AwTFVQ== + optionalDependencies: + fsevents "~2.1.2" + +sass@^1.42.1: + version "1.42.1" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.42.1.tgz#5ab17bebc1cb1881ad2e0c9a932c66ad64e441e2" + integrity sha512-/zvGoN8B7dspKc5mC6HlaygyCBRvnyzzgD5khiaCfglWztY99cYoiTUksVx11NlnemrcfH5CEaCpsUKoW0cQqg== + dependencies: + chokidar ">=3.0.0 <4.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +signal-exit@^3.0.3: + version "3.0.5" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.5.tgz#9e3e8cc0c75a99472b44321033a7702e7738252f" + integrity sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ== + +slash@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +sourcemap-codec@^1.4.4: + version "1.4.8" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +util@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= + dependencies: + inherits "2.0.1" + +validate-npm-package-name@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e" + integrity sha1-X6kS2B630MdK/BQN5zF/DKffQ34= + dependencies: + builtins "^1.0.3" + +vm2@^3.9.2: + version "3.9.3" + resolved "https://registry.yarnpkg.com/vm2/-/vm2-3.9.3.tgz#29917f6cc081cc43a3f580c26c5b553fd3c91f40" + integrity sha512-smLS+18RjXYMl9joyJxMNI9l4w7biW8ilSDaVRvFBDwOH8P0BK1ognFQTpg0wyQ6wIKLTblHJvROW692L/E53Q== + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=