Removed tour feature

refs 10b48b1d6d
refs 1531712d92

- the tour feature is no longer used so all associated code can be removed
- removes `liquid-tether` dependency as it was only used by the tour throbbers/popovers
This commit is contained in:
Kevin Ansfield 2021-03-02 14:29:26 +00:00
parent 1531712d92
commit e66bb4feac
14 changed files with 8 additions and 644 deletions

View File

@ -9,7 +9,6 @@ export default Authenticator.extend({
feature: service(),
ghostPaths: service(),
settings: service(),
tour: service(),
whatsNew: service(),
sessionEndpoint: computed('ghostPaths.apiRoot', function () {
@ -34,8 +33,7 @@ export default Authenticator.extend({
let preloadPromises = [
this.config.fetchAuthenticated(),
this.feature.fetch(),
this.settings.fetch(),
this.tour.fetchViewed()
this.settings.fetch()
];
// kick off background update of "whats new"

View File

@ -1,41 +0,0 @@
{{#if this.isVisible}}
{{!-- tether the throbber --}}
<LiquidTether
@class="throbber-container"
@target={{this.target}}
@attachment="middle center"
@targetAttachment={{this.throbberAttachment}}
@targetOffset={{this.throbberOffset}}
>
<a class="throbber-trigger" href="#" {{action "open"}} id={{this._throbberElementId}}>
<span class="throbber"></span>
</a>
</LiquidTether>
{{#if this.isOpen}}
{{!-- wormhole the background click overlay --}}
<LiquidWormhole @class="tour-container">
<div class="tour-background" {{action "close" on="click"}}></div>
</LiquidWormhole>
{{!-- tether the popover --}}
<LiquidTether
@class="tour"
@target={{this._throbberElementSelector}}
@attachment={{this._popoverAttachment}}
@targetAttachment={{this._popoverTargetAttachment}}
@offset={{this._popoverOffset}}
>
<div class="popover-item popover-triangle-{{this.popoverTriangleClass}}">
<h3 class="popover-title">{{this._throbber.title}}</h3>
<div class="popover-body">
{{{this._throbber.message}}}
</div>
<footer class="popover-foot">
<a class="tour-optout" href="#" {{action 'optOut'}}>Not your first time? <em>Skip these tips</em></a>
<a class="tour-dismiss gh-btn gh-btn-black" href="#" {{action 'markAsViewed'}}><span>Ok, got it</span></a>
</footer>
</div>
</LiquidTether>
{{/if}}
{{/if}}

View File

@ -1,170 +0,0 @@
import Component from '@ember/component';
import {computed} from '@ember/object';
import {isBlank} from '@ember/utils';
import {reads} from '@ember/object/computed';
import {run} from '@ember/runloop';
import {inject as service} from '@ember/service';
let instancesCounter = 0;
let triangleClassPositions = {
'top-left': {
attachment: 'top left',
targetAttachment: 'bottom center',
offset: '0 28px'
},
top: {
attachment: 'top center',
targetAttachment: 'bottom center'
},
'top-right': {
attachment: 'top right',
targetAttachment: 'bottom center',
offset: '0 -28px'
},
'right-top': {
attachment: 'top right',
targetAttachment: 'middle left',
offset: '28px 0'
},
right: {
attachment: 'middle right',
targetAttachment: 'middle left'
},
'right-bottom': {
attachment: 'bottom right',
targetAttachment: 'middle left',
offset: '-28px 0'
},
'bottom-right': {
attachment: 'bottom right',
targetAttachment: 'top center',
offset: '0 -28px'
},
bottom: {
attachment: 'bottom center',
targetAttachment: 'top center'
},
'bottom-left': {
attachment: 'bottom left',
targetAttachment: 'top center',
offset: '0 28px'
},
'left-bottom': {
attachment: 'bottom left',
targetAttachment: 'middle right',
offset: '-28px 0'
},
left: {
attachment: 'middle left',
targetAttachment: 'middle right'
},
'left-top': {
attachment: 'top left',
targetAttachment: 'middle right',
offset: '28px 0'
}
};
const GhTourItemComponent = Component.extend({
mediaQueries: service(),
tour: service(),
ui: service(),
tagName: '',
throbberId: null,
target: null,
throbberAttachment: 'middle center',
popoverTriangleClass: 'top',
isOpen: false,
_elementId: null,
_throbber: null,
_throbberElementId: null,
_throbberElementSelector: null,
_popoverAttachment: null,
_popoverTargetAttachment: null,
_popoverOffset: null,
isMobile: reads('mediaQueries.isMobile'),
isVisible: computed('isMobile', '_throbber', 'ui.showTour', function () {
let showTour = this.ui.showTour;
let isMobile = this.isMobile;
let hasThrobber = !isBlank(this._throbber);
return showTour && !isMobile && hasThrobber;
}),
init() {
this._super(...arguments);
// this is a tagless component so we need to generate our own elementId
this._elementId = instancesCounter += 1;
this._throbberElementId = `throbber-${this._elementId}`;
this._throbberElementSelector = `#throbber-${this._elementId}`;
this._handleOptOut = run.bind(this, this._remove);
this._handleViewed = run.bind(this, this._removeIfViewed);
this.tour.on('optOut', this._handleOptOut);
this.tour.on('viewed', this._handleViewed);
},
didReceiveAttrs() {
let throbberId = this.throbberId;
let throbber = this.tour.activeThrobber(throbberId);
let triangleClass = this.popoverTriangleClass;
let popoverPositions = triangleClassPositions[triangleClass];
this._throbber = throbber;
this._popoverAttachment = popoverPositions.attachment;
this._popoverTargetAttachment = popoverPositions.targetAttachment;
this._popoverOffset = popoverPositions.offset;
},
willDestroyElement() {
this.tour.off('optOut', this._handleOptOut);
this.tour.off('viewed', this._handleViewed);
this._super(...arguments);
},
actions: {
open() {
this.set('isOpen', true);
},
close() {
this._close();
},
markAsViewed() {
let throbberId = this.throbberId;
this.tour.markThrobberAsViewed(throbberId);
this.set('_throbber', null);
this._close();
},
optOut() {
this.tour.optOut();
this.set('_throbber', null);
this._close();
}
},
_removeIfViewed(id) {
if (id === this.throbberId) {
this._remove();
}
},
_remove() {
this.set('_throbber', null);
},
_close() {
this.set('isOpen', false);
}
});
export default GhTourItemComponent;

View File

@ -35,7 +35,6 @@ export default Route.extend(ApplicationRouteMixin, ShortcutsRoute, {
notifications: service(),
router: service(),
settings: service(),
tour: service(),
ui: service(),
whatsNew: service(),
@ -66,8 +65,7 @@ export default Route.extend(ApplicationRouteMixin, ShortcutsRoute, {
return RSVP.all([
this.config.fetchAuthenticated(),
this.feature.fetch(),
this.settings.fetch(),
this.tour.fetchViewed()
this.settings.fetch()
]).then((results) => {
this._appLoaded = true;

View File

@ -1,139 +0,0 @@
import Evented from '@ember/object/evented';
import RSVP from 'rsvp';
import Service, {inject as service} from '@ember/service';
import {computed} from '@ember/object';
export default Service.extend(Evented, {
ghostPaths: service(),
session: service(),
// this service is responsible for managing tour item visibility and syncing
// the viewed state with the server
//
// tour items need to be centrally defined here so that we have a single
// source of truth for marking all tour items as viewed
//
// a <GhTourItem @trobberId="unique-id"/> component can be inserted in any template,
// this will use the tour service to grab content and determine visibility
// with the component in control of rendering the throbber/controlling the
// modal - this allows the component lifecycle hooks to perform automatic
// display/cleanup when the relevant UI is visible.
viewed: null,
// IDs should **NOT** be changed if they have been part of a release unless
// the re-display of the throbber should be forced. In that case it may be
// useful to add a version number eg. `my-feature` -> `my-feature-v2`.
// Format is as follows:
//
// {
// id: 'test',
// title: 'This is a test',
// message: 'This is a test of our <strong>feature tour</strong> feature'
// }
//
// TODO: it may be better to keep this configuration elsewhere to keep the
// service clean. Eventually we'll want apps to be able to register their
// own throbbers and tour content
throbbers: null,
init() {
this._super(...arguments);
let adminUrl = `${window.location.origin}${this.get('ghostPaths.url').admin()}`;
let adminDisplayUrl = adminUrl.replace(`${window.location.protocol}//`, '');
this.viewed = [];
this.throbbers = [{
id: 'getting-started',
title: 'Getting started with Ghost',
message: `Welcome to Ghost Admin! From here you can browse your site, manage your content, and edit your settings.<br><br>You can always login to Ghost Admin by visiting <a href="${adminUrl}" target="_blank">${adminDisplayUrl}</a>`
}, {
id: 'using-the-editor',
title: 'Using the Ghost editor',
message: 'Ghost uses Markdown to allow you to write and format content quickly and easily. This toolbar also helps! Hit the <strong>?</strong> icon for more editor shortcuts.'
}, {
id: 'featured-post',
title: 'Setting a featured post',
message: 'Depending on your theme, featured posts receive special styling to make them stand out as a particularly important or emphasised story.'
}, {
id: 'upload-a-theme',
title: 'Customising your publication',
message: 'Using custom themes you can completely control the look and feel of your site to suit your brand. Here\'s a full guide to help: <strong><a href="https://ghost.org/docs/themes/" target="_blank">https://ghost.org/docs/themes/</a></strong>'
}];
},
_activeThrobbers: computed('viewed.[]', 'throbbers.[]', function () {
// return throbbers that haven't been viewed
let viewed = this.viewed;
let throbbers = this.throbbers;
return throbbers.reject(throbber => viewed.includes(throbber.id));
}),
// retrieve the IDs of the viewed throbbers from the server, always returns
// a promise
fetchViewed() {
return this.get('session.user').then((user) => {
let viewed = user.get('tour') || [];
this.set('viewed', viewed);
return viewed;
});
},
// save the list of viewed throbbers to the server overwriting the
// entire list
syncViewed() {
let viewed = this.viewed;
return this.get('session.user').then((user) => {
user.set('tour', viewed);
return user.save();
});
},
// returns throbber content for a given ID only if that throbber hasn't been
// viewed. Used by the <GhTourItem /> component to determine visibility
activeThrobber(id) {
let activeThrobbers = this._activeThrobbers;
return activeThrobbers.findBy('id', id);
},
// when a throbber is opened the component will call this method to mark
// it as viewed and sync with the server. Always returns a promise
markThrobberAsViewed(id) {
let viewed = this.viewed;
if (!viewed.includes(id)) {
viewed.pushObject(id);
this.trigger('viewed', id);
return this.syncViewed();
} else {
return RSVP.resolve(true);
}
},
// opting-out will use the list of IDs defined in this file making it the
// single-source-of-truth and allowing future client updates to control when
// new UI should be surfaced through tour items
optOut() {
let allThrobberIds = this.throbbers.mapBy('id');
this.set('viewed', allThrobberIds);
this.trigger('optOut');
return this.syncViewed();
},
// this is not used anywhere at the moment but it's useful to use via ember
// inspector as a reset mechanism
reEnable() {
this.set('viewed', []);
return this.syncViewed();
}
});

View File

@ -42,7 +42,6 @@ export default Service.extend({
isFullScreen: false,
showMobileMenu: false,
showSettingsMenu: false,
showTour: true,
mainClass: '',
hasSideNav: not('isSideNavHidden'),

View File

@ -16,7 +16,6 @@
@import "patterns/tables.css";
@import "patterns/navlist.css";
@import "patterns/boxes.css";
@import "patterns/throbber.css";
/* Components: Groups of Patterns
@ -34,7 +33,6 @@
@import "components/power-calendar.css";
@import "components/publishmenu.css";
@import "components/popovers.css";
@import "components/tour.css";
@import "components/unsplash.css";
@import "components/codemirror.css";
@import "components/lists.css";
@ -291,13 +289,13 @@ input:focus,
/* ---------------------------------------------------------------------- */
.gh-expandable-header .gh-btn:not(.gh-btn-white):not(.gh-btn-green):not(.gh-btn-blue):not(.gh-btn-red),
.gh-expandable-content .gh-btn:not(.gh-btn-white):not(.gh-btn-green):not(.gh-btn-blue):not(.gh-btn-red),
.gh-expandable-content .gh-btn:not(.gh-btn-white):not(.gh-btn-green):not(.gh-btn-blue):not(.gh-btn-red),
.gh-main-section-content.grey .gh-btn:not(.gh-btn-white):not(.gh-btn-green):not(.gh-btn-blue):not(.gh-btn-red) {
background: var(--lightgrey);
}
.gh-expandable-header .gh-btn:not(.gh-btn-white):not(.gh-btn-green):not(.gh-btn-blue):not(.gh-btn-red):hover,
.gh-expandable-content .gh-btn:not(.gh-btn-white):not(.gh-btn-green):not(.gh-btn-blue):not(.gh-btn-red):hover,
.gh-expandable-content .gh-btn:not(.gh-btn-white):not(.gh-btn-green):not(.gh-btn-blue):not(.gh-btn-red):hover,
.gh-main-section-content.grey .gh-btn:not(.gh-btn-white):not(.gh-btn-green):not(.gh-btn-blue):not(.gh-btn-red):hover {
background: var(--lightgrey-l1);
}
@ -435,19 +433,6 @@ input:focus,
color: var(--midgrey);
}
.tour-dismiss {
border: none;
background: linear-gradient( rgb(50, 50, 50), rgb(23, 23, 23) );
}
.tour-dismiss span {
background: linear-gradient( rgb(69, 69, 69), rgb(42, 44, 45) 60%, rgb(42, 44, 45) 90%, rgb(49, 52, 53) );
}
.throbber:before {
background: rgba(0,0,0,0.2);
}
.gh-editor-header-small {
background: var(--dark-main-bg-color);
}
@ -885,4 +870,4 @@ input:focus,
.gh-dashboard-box {
border-color: var(--lightgrey);
}
}

View File

@ -16,7 +16,6 @@
@import "patterns/tables.css";
@import "patterns/navlist.css";
@import "patterns/boxes.css";
@import "patterns/throbber.css";
/* Components: Groups of Patterns
@ -34,7 +33,6 @@
@import "components/power-calendar.css";
@import "components/publishmenu.css";
@import "components/popovers.css";
@import "components/tour.css";
@import "components/unsplash.css";
@import "components/codemirror.css";
@import "components/lists.css";

View File

@ -1,152 +0,0 @@
/* ------------------------------------------------------------
Popovers
Styles for the popover component
* Popovers
* Open/Close
* Positioning
* Triangles classes
------------------------------------------------------------ */
.throbber-container {
z-index: 998;
}
.tour-background {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 999;
background: rgba(54,83,109,0.08);
}
.tour.liquid-wormhole-element {
z-index: 999;
}
.tour .popover-item {
padding: 20px;
max-width: none;
width: 460px;
color: #fff;
background: linear-gradient(135deg, color-mod(var(--blue) l(+3%)) 0%, color-mod(var(--blue) l(-5%) s(-5%)) 100%);
box-shadow: rgba(0,0,0,0.05) 0 0 0 1px inset, rgba(0,0,0,0.15) 0 1px 8px;
border-radius: 10px;
}
.tour .popover-item a {
color: #fff;
font-weight: 500;
}
.tour .popover-title {
margin-bottom: 0.4em;
color: #fff;
font-size: 2.4rem;
font-weight: 600;
letter-spacing: 0.5px;
}
.tour .popover-desc {
margin: 0;
}
.tour .popover-body {
margin: 0;
font-size: 1.4rem;
line-height: 1.55em;
font-weight: 300;
letter-spacing: 0.5px;
color: rgba(255,255,255,0.85);
}
.tour .popover-foot {
display: flex;
justify-content: space-between;
margin-top: 25px;
}
.tour .popover-item .tour-optout {
align-self: flex-end;
margin-bottom: 7px;
font-size: 1.2rem;
font-weight: 300;
}
.tour .popover-item .tour-optout em {
font-style: normal;
font-weight: 400;
text-decoration: underline;
}
/* 'dem Triangles */
.tour .popover-triangle-top:before,
.tour .popover-triangle-top-left:before,
.tour .popover-triangle-top-right:before {
border-right: 14px solid transparent;
border-bottom: calc(14px * 0.8) solid color-mod(var(--blue) l(-5%) s(-10%));
border-left: 14px solid transparent;
}
.tour .popover-triangle-top:after,
.tour .popover-triangle-top-left:after,
.tour .popover-triangle-top-right:after {
top: -10px;
border-right: 14px solid transparent;
border-bottom: calc(14px * 0.8) solid color-mod(var(--blue) l(-5%) s(-10%));
border-left: 14px solid transparent;
}
.tour .popover-triangle-bottom:before,
.tour .popover-triangle-bottom-left:before,
.tour .popover-triangle-bottom-right:before {
border-top: calc(14px * 0.8) solid color-mod(var(--blue) l(-5%) s(-10%));
border-right: 14px solid transparent;
border-left: 14px solid transparent;
}
.tour .popover-triangle-bottom:after,
.tour .popover-triangle-bottom-left:after,
.tour .popover-triangle-bottom-right:after {
bottom: -10px;
border-top: calc(14px * 0.8) solid color-mod(var(--blue) l(-5%) s(-10%));
border-right: 14px solid transparent;
border-left: 14px solid transparent;
}
.tour .popover-triangle-left:before,
.tour .popover-triangle-left-top:before,
.tour .popover-triangle-left-bottom:before {
border-top: 14px solid transparent;
border-right: calc(14px * 0.8) solid color-mod(var(--blue) l(-5%) s(-10%));
border-bottom: 14px solid transparent;
}
.tour .popover-triangle-left:after,
.tour .popover-triangle-left-top:after,
.tour .popover-triangle-left-bottom:after {
left: -10px;
border-top: 14px solid transparent;
border-right: calc(14px * 0.8) solid color-mod(var(--blue) l(-5%) s(-10%));
border-bottom: 14px solid transparent;
}
.tour .popover-triangle-right:before,
.tour .popover-triangle-right-top:before,
.tour .popover-triangle-right-bottom:before {
border-top: 14px solid transparent;
border-bottom: 14px solid transparent;
border-left: calc(14px * 0.8) solid color-mod(var(--blue) l(-5%) s(-10%));
}
.tour .popover-triangle-right:after,
.tour .popover-triangle-right-top:after,
.tour .popover-triangle-right-bottom:after {
right: -10px;
border-top: 14px solid transparent;
border-bottom: 14px solid transparent;
border-left: calc(14px * 0.8) solid color-mod(var(--blue) l(-5%) s(-10%));
}

View File

@ -1,70 +0,0 @@
/* ------------------------------------------------------------
Throbber
Pulsing little circle which indicates a tour is available
------------------------------------------------------------ */
/* click-area for triggering the popover */
.throbber-trigger {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 50px;
height: 50px;
}
.throbber {
position: relative;
display: block;
width: 14px;
height: 14px;
background: color-mod(var(--blue) alpha(0.9));
border-radius: 100%;
box-shadow: color-mod(var(--blue) alpha(0.9)) 0 0 0 2px;
}
.throbber:before {
content: "";
position: absolute;
top: 50%;
left: 50%;
display: block;
margin: -13px 0 0 -13px;
width: 26px;
height: 26px;
border: rgba(255,255,255,0.1) 2px solid;
background: rgba(255,255,255,0.4);
border-radius: 100%;
box-shadow: color-mod(var(--blue) alpha(0.7)) 0 0 0 2px;
animation-name: throbber-pulse, throbber-fade;
animation-duration: 1.5s;
animation-timing-function: ease-in-out;
animation-iteration-count: infinite;
animation-fill-mode: forwards;
animation-delay: 0.2s;
pointer-events: none;
}
@keyframes throbber-fade {
0%,
100% {
opacity: 0.5;
}
40%,
60% {
opacity: 0.8;
}
}
@keyframes throbber-pulse {
0% {
transform: scale3d(0.5, 0.5, 1);
}
50% {
transform: scale3d(1, 1, 1);
}
100% {
transform: scale3d(0.5, 0.5, 1);
}
}

View File

@ -11,20 +11,6 @@ export default function () {
this.use('crossFade', {duration: 100})
);
this.transition(
this.hasClass('tour-container'),
this.toValue(true),
this.use('fade', {duration: 150}),
this.reverse('fade', {duration: 150})
);
this.transition(
this.hasClass('tour'),
this.toValue(true),
this.use('fade', {duration: 300}),
this.reverse('fade', {duration: 300})
);
// TODO: Maybe animate with explode. gh-unsplash-window should ideally slide in from bottom to top of screen
// this.transition(
// this.hasClass('gh-unsplash-window'),

View File

@ -114,7 +114,6 @@
"grunt-shell": "3.0.1",
"keymaster": "https://github.com/madrobby/keymaster.git",
"liquid-fire": "0.31.0",
"liquid-tether": "2.0.7",
"liquid-wormhole": "2.1.5",
"loader.js": "4.7.0",
"markdown-it": "12.0.4",

View File

@ -35,13 +35,6 @@ const mockSettings = Service.extend({
}
});
const mockTour = Service.extend({
init() {
this._super(...arguments);
this.fetchViewed = sinon.stub().resolves();
}
});
const mockGhostPaths = Service.extend({
apiRoot: ghostPaths().apiRoot
});
@ -54,7 +47,6 @@ describe('Unit: Authenticator: cookie', () => {
this.owner.register('service:config', mockConfig);
this.owner.register('service:feature', mockFeature);
this.owner.register('service:settings', mockSettings);
this.owner.register('service:tour', mockTour);
this.owner.register('service:ghost-paths', mockGhostPaths);
});
@ -72,7 +64,6 @@ describe('Unit: Authenticator: cookie', () => {
let config = this.owner.lookup('service:config');
let feature = this.owner.lookup('service:feature');
let settings = this.owner.lookup('service:settings');
let tour = this.owner.lookup('service:tour');
return authenticator.authenticate('AzureDiamond', 'hunter2').then(() => {
expect(post.args[0][0]).to.equal(`${ghostPaths().apiRoot}/session`);
@ -93,7 +84,6 @@ describe('Unit: Authenticator: cookie', () => {
expect(config.fetchAuthenticated.calledOnce, 'config.fetchAuthenticated called').to.be.true;
expect(feature.fetch.calledOnce, 'feature.fetch called').to.be.true;
expect(settings.fetch.calledOnce, 'settings.fetch called').to.be.true;
expect(tour.fetchViewed.calledOnce, 'tour.fetchViewed called').to.be.true;
});
});
});

View File

@ -3393,7 +3393,7 @@ broccoli-funnel@3.0.3, broccoli-funnel@^3.0.0, broccoli-funnel@^3.0.3:
path-posix "^1.0.0"
walk-sync "^2.0.2"
broccoli-funnel@^1.0.1, broccoli-funnel@^1.1.0:
broccoli-funnel@^1.0.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/broccoli-funnel/-/broccoli-funnel-1.2.0.tgz#cddc3afc5ff1685a8023488fff74ce6fb5a51296"
integrity sha1-zdw6/F/xaFqAI0iP/3TOb7WlEpY=
@ -3461,7 +3461,7 @@ broccoli-merge-trees@4.2.0, broccoli-merge-trees@^4.0.0, broccoli-merge-trees@^4
broccoli-plugin "^4.0.2"
merge-trees "^2.0.0"
broccoli-merge-trees@^1.0.0, broccoli-merge-trees@^1.1.1, broccoli-merge-trees@^1.2.4:
broccoli-merge-trees@^1.0.0, broccoli-merge-trees@^1.1.1:
version "1.2.4"
resolved "https://registry.yarnpkg.com/broccoli-merge-trees/-/broccoli-merge-trees-1.2.4.tgz#a001519bb5067f06589d91afa2942445a2d0fdb5"
integrity sha1-oAFRm7UGfwZYnZGvopQkRaLQ/bU=
@ -9505,19 +9505,7 @@ liquid-fire@0.31.0:
match-media "^0.2.0"
velocity-animate "^1.5.2"
liquid-tether@2.0.7:
version "2.0.7"
resolved "https://registry.yarnpkg.com/liquid-tether/-/liquid-tether-2.0.7.tgz#c859e0caad2fd25b4dd57f6c8c51b10eb5529f53"
integrity sha512-cekrPA2Ue7jA5X0y6FQ0/ChCpd40QFzVbOJnko7KoMn5kng+xkrWHkNg20VJhOIFrPgikREZpoGtYoFCg3/V1g==
dependencies:
broccoli-funnel "^1.1.0"
broccoli-merge-trees "^1.2.4"
ember-cli-babel "^6.12.0"
ember-cli-htmlbars "^2.0.3"
liquid-wormhole "^2.1.4"
tether "^1.4.0"
liquid-wormhole@2.1.5, liquid-wormhole@^2.1.4:
liquid-wormhole@2.1.5:
version "2.1.5"
resolved "https://registry.yarnpkg.com/liquid-wormhole/-/liquid-wormhole-2.1.5.tgz#23ebdd85dd40d1dad424ab346f755baebc2d93d8"
integrity sha512-dmnXYkxF+N8JUibDS937ucClQ+ApMLht7CCv09tWZLCFonuRPzt98pisuUxRVnFo95+kdwEVR+S2guB3JUny7A==
@ -13340,11 +13328,6 @@ testem@3.2.0, testem@^3.2.0:
tmp "0.0.33"
xmldom "^0.1.19"
tether@^1.4.0:
version "1.4.7"
resolved "https://registry.yarnpkg.com/tether/-/tether-1.4.7.tgz#d56a818590d8fe72e387f77a67f93ab96d8e1fb2"
integrity sha512-Z0J1aExjoFU8pybVkQAo/vD2wfSO63r+XOPfWQMC5qtf1bI7IWqNk4MiyBcgvvnY8kqnY06dVdvwTK2S3PU/Fw==
text-table@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"