🔀 Merge pull request #183 from Lissy93/FEATURE/better-loading

[FEATURE] Better Loading UX
This commit is contained in:
Alicia Sykes 2021-08-28 14:11:52 +01:00 committed by GitHub
commit 5db0909e37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 249 additions and 42 deletions

View File

@ -1,5 +1,10 @@
# Changelog # Changelog
## ⚡️ 1.6.8 - Improved Loading Experience [PR #183](https://github.com/Lissy93/dashy/pull/183)
- During app initialization, show the build progress and status message
- While requests are being made, show loader at top of screen
- Also adds some UI improvements to Workspace view
## ⚡️ 1.6.7 - Option for non-SSL status checks plus minor things [PR #182](https://github.com/Lissy93/dashy/pull/182) ## ⚡️ 1.6.7 - Option for non-SSL status checks plus minor things [PR #182](https://github.com/Lissy93/dashy/pull/182)
- Adds an option for user to use status checks with non-HTTPS services, Re: #181 - Adds an option for user to use status checks with non-HTTPS services, Re: #181
- Updates the .env template, plus the variables used in the server - Updates the .env template, plus the variables used in the server

View File

@ -1,6 +1,6 @@
{ {
"name": "Dashy", "name": "Dashy",
"version": "1.6.7", "version": "1.6.8",
"license": "MIT", "license": "MIT",
"main": "server", "main": "server",
"scripts": { "scripts": {
@ -27,6 +27,7 @@
"keycloak-js": "^15.0.2", "keycloak-js": "^15.0.2",
"register-service-worker": "^1.6.2", "register-service-worker": "^1.6.2",
"remedial": "^1.0.8", "remedial": "^1.0.8",
"rsup-progress": "^2.0.4",
"serve-static": "^1.14.1", "serve-static": "^1.14.1",
"v-jsoneditor": "^1.4.2", "v-jsoneditor": "^1.4.2",
"v-tooltip": "^2.1.3", "v-tooltip": "^2.1.3",

View File

@ -1,6 +1,7 @@
<!doctype html> <!doctype html>
<!-- Dashy: Licensed under MIT, (C) 2021 Alicia Sykes <https://aliciasykes.com> -->
<!-- This is the default page, displayed while the app is still building -->
<html> <html>
<head> <head>
<title>Dashy</title> <title>Dashy</title>
<meta name="description" content="Welcome to Dashy"> <meta name="description" content="Welcome to Dashy">
@ -12,6 +13,7 @@
</head> </head>
<body> <body>
<!-- Dashy Title Wavey Text -->
<svg viewbox="0 0 100 20"> <svg viewbox="0 0 100 20">
<defs> <defs>
<linearGradient id="gradient" x1="0" x2="0" y1="0" y2="1"> <linearGradient id="gradient" x1="0" x2="0" y1="0" y2="1">
@ -29,9 +31,16 @@
<text text-anchor="middle" x="50" y="15" font-size="17" fill="url(#wave)" fill-opacity="0.8">Dashy</text> <text text-anchor="middle" x="50" y="15" font-size="17" fill="url(#wave)" fill-opacity="0.8">Dashy</text>
<text text-anchor="middle" x="50" y="15" font-size="17" fill="url(#gradient)" fill-opacity="0.5">Dashy</text> <text text-anchor="middle" x="50" y="15" font-size="17" fill="url(#gradient)" fill-opacity="0.5">Dashy</text>
</svg> </svg>
<!-- Progress Bar -->
<div class="progress-outer" id="bar-outer">
<div class="progress-inner" id="bar"></div>
</div>
<!-- Status label, with animated dots -->
<div> <div>
<h2>Initializing</h2> <h2 id="status-text">Initializing</h2>
<span class="dots-cont"> <span id="dots" class="dots-cont">
<span class="dot dot-1"></span> <span class="dot dot-1"></span>
<span class="dot dot-2"></span> <span class="dot dot-2"></span>
<span class="dot dot-3"></span> <span class="dot dot-3"></span>
@ -39,7 +48,10 @@
</span> </span>
</div> </div>
<p class="time-note" id="note">This may take a minute or two</p>
<style lang="css"> <style lang="css">
/* Page Layout Styles */
body, body,
html { html {
margin: 0; margin: 0;
@ -68,6 +80,7 @@
display: inline; display: inline;
} }
/* Animated Dots */
.dots-cont { .dots-cont {
display: inline; display: inline;
} }
@ -84,44 +97,168 @@
position: relative; position: relative;
animation: jump 1s infinite; animation: jump 1s infinite;
} }
.dots-cont .dot-1 { -webkit-animation-delay: 100ms; animation-delay: 100ms; }
.dots-cont .dot-1 { .dots-cont .dot-2 { -webkit-animation-delay: 200ms; animation-delay: 200ms; }
-webkit-animation-delay: 100ms; .dots-cont .dot-3 { -webkit-animation-delay: 300ms; animation-delay: 300ms; }
animation-delay: 100ms; .dots-cont .dot-4 { -webkit-animation-delay: 400ms; animation-delay: 400ms; }
}
.dots-cont .dot-2 {
-webkit-animation-delay: 200ms;
animation-delay: 200ms;
}
.dots-cont .dot-3 {
-webkit-animation-delay: 300ms;
animation-delay: 300ms;
}
.dots-cont .dot-4 {
-webkit-animation-delay: 400ms;
animation-delay: 400ms;
}
@keyframes jump { @keyframes jump {
0% { 0% { bottom: 0px; }
bottom: 0px; 20% { bottom: 5px; }
40% { bottom: 0px; }
} }
20% { /* Little note */
bottom: 5px; p.time-note, p.time-note a {
font-size: 1rem;
color: #808080a6;
font-family: Tahoma, 'Trebuchet MS', sans-serif;
} }
40% { /* Progress Bar */
bottom: 0px; .progress-outer {
position: relative;
margin: 1rem;
height: 0.5rem;
width: 20rem;
border: 1px solid #fff;
border-radius: 6px;
} }
.progress-outer .progress-inner {
position: absolute;
background-color: #fff;
width: 0px;
height: 0.5rem;
border-radius: 15px;
} }
#bar.stage-0 { animation: progress-0 8s infinite linear; }
#bar.stage-1 { animation: progress-1 8s infinite linear; }
#bar.stage-2 { animation: progress-2 8s infinite linear; }
#bar.stage-3 { animation: progress-3 8s infinite linear; }
#bar.stage-4 { animation: progress-4 8s infinite linear; }
#bar.stage-5 { animation: progress-5 8s infinite linear; }
#bar.stage-6 { animation: progress-6 8s infinite linear; }
#bar.stage-7 { animation: progress-7 8s infinite linear; }
#bar.stage-8 { animation: progress-8 8s infinite linear; }
@keyframes progress {
0% { width: 0%; }
25% { width: 50%; }
50% { width: 75%; }
75% { width: 85%; }
100% { width: 100%; }
}
@keyframes progress-0 {
0% { width: 0%; }
50% { width: 20%; }
100% { width: 30%; }
}
@keyframes progress-1 {
0% { width: 30%; }
50% { width: 42%; }
100% { width: 50%; }
}
@keyframes progress-2 {
0% { width: 50%; }
50% { width: 60%; }
100% { width: 65%; }
}
@keyframes progress-3 {
0% { width: 65%; }
100% { width: 75%; }
}
@keyframes progress-4 {
0% { width: 75%; }
100% { width: 85%; }
}
@keyframes progress-5 {
0% { width: 85%; }
100% { width: 90%; }
}
@keyframes progress-6 {
0% { width: 90%; }
100% { width: 94%; }
}
@keyframes progress-7 {
0% { width: 94%; }
50% { width: 96%; }
75% { width: 90%; }
100% { width: 96%; }
}
@keyframes progress-8 {
0% { width: 95%; }
50% { width: 97%; }
75% { width: 94%; }
100% { width: 98%; }
}
.hide { display: none; }
</style> </style>
<script> <script>
setTimeout(() => { location.reload(); }, 10000); const refreshRate = 8000;
// Refresh at interval
setTimeout(() => { location.reload(); }, refreshRate);
// Get current stage
let initStage = parseInt(sessionStorage.getItem('initStage') || 0);
// Check if stage in session storage is old, and if so, reset it
const now = Math.round(Date.now()/1000);
const buildStarted = sessionStorage.getItem('buildStarted');
if (!buildStarted) { // First time build
sessionStorage.setItem('buildStarted', now);
} else if ((now - parseInt(buildStarted)) > 600) {
initStage = 0;
sessionStorage.setItem('buildStarted', now);
}
// Grab elements from page
const statusTextElem = document.getElementById('status-text');
const progressBarElem = document.getElementById('bar');
const progressOuterElem = document.getElementById('bar-outer');
const loadingDotsElem = document.getElementById('dots');
const noteElem = document.getElementById('note');
// Based on stage, modify element content/ styles
if (initStage === 0) {
statusTextElem.innerHTML = 'Initializing'
progressBarElem.classList.add('stage-0');
} else if (initStage === 1) {
statusTextElem.innerHTML = 'Running Checks'
progressBarElem.classList.add('stage-1');
} else if (initStage === 2) {
statusTextElem.innerHTML = 'Building'
progressBarElem.classList.add('stage-2');
} else if (initStage === 3) {
statusTextElem.innerHTML = 'Building'
progressBarElem.classList.add('stage-3');
} else if (initStage === 4) {
statusTextElem.innerHTML = 'Finishing Off'
progressBarElem.classList.add('stage-4');
} else if (initStage === 5) {
statusTextElem.innerHTML = 'Almost Done'
progressBarElem.classList.add('stage-5');
} else if (initStage === 6) {
statusTextElem.innerHTML = 'Not Long Left'
progressBarElem.classList.add('stage-6');
} else if (initStage === 7) {
statusTextElem.innerHTML = 'Taking Longer than Expected'
progressBarElem.classList.add('stage-7');
noteElem.innerHTML = 'See the console for more info';
} else if (initStage >= 8) {
const docsLink = '<a href="https://github.com/Lissy93/dashy/tree/master/docs#readme">Documentation</a>';
statusTextElem.innerHTML = 'Possible Error, Check Logs'
noteElem.innerHTML = 'For troubleshooting, please see the ' + docsLink;
progressOuterElem.classList.add('hide');
loadingDotsElem.classList.add('hide');
} else {
statusTextElem.innerHTML = 'Building App'
progressOuterElem.classList.add('hide')
}
// Iterate the stage number
sessionStorage.setItem('initStage', initStage + 1)
</script> </script>
</body> </body>

View File

@ -60,6 +60,11 @@
"css-note-l2": "Styles overrides are only stored locally, so it is recommended to make a copy of your CSS.", "css-note-l2": "Styles overrides are only stored locally, so it is recommended to make a copy of your CSS.",
"css-note-l3": "To remove all custom styles, delete the contents and hit Save Changes" "css-note-l3": "To remove all custom styles, delete the contents and hit Save Changes"
}, },
"alternate-views": {
"default": "Default",
"workspace": "Workspace",
"minimal": "Minimal"
},
"settings": { "settings": {
"theme-label": "Theme", "theme-label": "Theme",
"layout-label": "Layout", "layout-label": "Layout",

View File

@ -31,6 +31,7 @@
<script> <script>
import axios from 'axios'; import axios from 'axios';
import ProgressBar from 'rsup-progress';
import ErrorHandler from '@/utils/ErrorHandler'; import ErrorHandler from '@/utils/ErrorHandler';
export default { export default {
@ -39,6 +40,7 @@ export default {
data() { data() {
return { return {
appVersion: process.env.VUE_APP_VERSION, // Current version, from package.json appVersion: process.env.VUE_APP_VERSION, // Current version, from package.json
progress: new ProgressBar({ color: 'var(--progress-bar)' }),
latestVersion: '', // Will store latest version, when request returns latestVersion: '', // Will store latest version, when request returns
checksEnabled: true, // Should we check for updates checksEnabled: true, // Should we check for updates
isUpToDate: true, // Is current version === latest version isUpToDate: true, // Is current version === latest version
@ -60,14 +62,17 @@ export default {
/* Gets the apps latest version from Dashy's git repo */ /* Gets the apps latest version from Dashy's git repo */
checkVersion() { checkVersion() {
const packageUrl = 'https://raw.githubusercontent.com/Lissy93/dashy/master/package.json'; const packageUrl = 'https://raw.githubusercontent.com/Lissy93/dashy/master/package.json';
this.progress.start();
axios.get(packageUrl).then((response) => { axios.get(packageUrl).then((response) => {
if (response && response.data && response.data.version) { if (response && response.data && response.data.version) {
this.latestVersion = response.data.version; this.latestVersion = response.data.version;
this.isUpToDate = this.checkIfUpToDate(this.appVersion, this.latestVersion); this.isUpToDate = this.checkIfUpToDate(this.appVersion, this.latestVersion);
this.finished = true; this.finished = true;
this.progress.end();
} }
}).catch(() => { }).catch(() => {
this.error = true; this.error = true;
this.progress.end();
}); });
}, },
/* Compares the current version, with the package.json version */ /* Compares the current version, with the package.json version */

View File

@ -59,6 +59,7 @@
<script> <script>
import sha256 from 'crypto-js/sha256'; import sha256 from 'crypto-js/sha256';
import ProgressBar from 'rsup-progress';
import Button from '@/components/FormElements/Button'; import Button from '@/components/FormElements/Button';
import Input from '@/components/FormElements/Input'; import Input from '@/components/FormElements/Input';
import IconBackup from '@/assets/interface-icons/config-backup.svg'; import IconBackup from '@/assets/interface-icons/config-backup.svg';
@ -77,6 +78,7 @@ export default {
restorePassword: '', restorePassword: '',
restoreCode: '', restoreCode: '',
backupId: localStorage[localStorageKeys.BACKUP_ID] || '', backupId: localStorage[localStorageKeys.BACKUP_ID] || '',
progress: new ProgressBar({ color: 'var(--progress-bar)' }),
}; };
}, },
components: { components: {
@ -87,11 +89,14 @@ export default {
}, },
methods: { methods: {
restoreBackup() { restoreBackup() {
this.progress.start();
restore(this.restoreCode, this.restorePassword) restore(this.restoreCode, this.restorePassword)
.then((response) => { .then((response) => {
this.restoreFromBackup(response, this.restoreCode); this.restoreFromBackup(response, this.restoreCode);
this.progress.end();
}).catch((msg) => { }).catch((msg) => {
this.showErrorMsg(msg); this.showErrorMsg(msg);
this.progress.end();
}); });
}, },
checkPass() { checkPass() {
@ -107,6 +112,7 @@ export default {
} }
}, },
makeBackup() { makeBackup() {
this.progress.start();
backup(this.config, this.backupPassword) backup(this.config, this.backupPassword)
.then((response) => { .then((response) => {
if (!response.data || response.data.errorMsg || !response.data.backupId) { if (!response.data || response.data.errorMsg || !response.data.backupId) {
@ -114,11 +120,14 @@ export default {
} else { // All clear, no error } else { // All clear, no error
this.updateUiAfterBackup(response.data.backupId, false); this.updateUiAfterBackup(response.data.backupId, false);
} }
this.progress.end();
}).catch(() => { }).catch(() => {
this.showErrorMsg(this.$t('cloud-sync.backup-error-unknown')); this.showErrorMsg(this.$t('cloud-sync.backup-error-unknown'));
this.progress.end();
}); });
}, },
makeUpdate() { makeUpdate() {
this.progress.start();
update(this.config, this.backupPassword, this.backupId) update(this.config, this.backupPassword, this.backupId)
.then((response) => { .then((response) => {
if (!response.data || response.data.errorMsg || !response.data.backupId) { if (!response.data || response.data.errorMsg || !response.data.backupId) {
@ -126,8 +135,10 @@ export default {
} else { // All clear, no error } else { // All clear, no error
this.updateUiAfterBackup(response.data.backupId, true); this.updateUiAfterBackup(response.data.backupId, true);
} }
this.progress.end();
}).catch(() => { }).catch(() => {
this.showErrorMsg(this.$t('cloud-sync.backup-error-unknown')); this.showErrorMsg(this.$t('cloud-sync.backup-error-unknown'));
this.progress.end();
}); });
}, },
restoreFromBackup(config, backupId) { restoreFromBackup(config, backupId) {

View File

@ -59,6 +59,7 @@
<script> <script>
import axios from 'axios'; import axios from 'axios';
import ProgressBar from 'rsup-progress';
import VJsoneditor from 'v-jsoneditor'; import VJsoneditor from 'v-jsoneditor';
import { localStorageKeys } from '@/utils/defaults'; import { localStorageKeys } from '@/utils/defaults';
import configSchema from '@/utils/ConfigSchema.json'; import configSchema from '@/utils/ConfigSchema.json';
@ -89,6 +90,7 @@ export default {
responseText: '', responseText: '',
saveSuccess: undefined, saveSuccess: undefined,
allowWriteToDisk: this.shouldAllowWriteToDisk(), allowWriteToDisk: this.shouldAllowWriteToDisk(),
progress: new ProgressBar({ color: 'var(--progress-bar)' }),
}; };
}, },
computed: { computed: {
@ -123,6 +125,7 @@ export default {
const body = { config: yaml, timestamp: new Date() }; const body = { config: yaml, timestamp: new Date() };
const request = axios.post(endpoint, body, headers); const request = axios.post(endpoint, body, headers);
// 3. Make the request, and handle response // 3. Make the request, and handle response
this.progress.start();
request.then((response) => { request.then((response) => {
this.saveSuccess = response.data.success || false; this.saveSuccess = response.data.success || false;
this.responseText = response.data.message; this.responseText = response.data.message;
@ -132,11 +135,13 @@ export default {
} else { } else {
this.showToast(this.$t('config-editor.error-msg-cannot-save'), false); this.showToast(this.$t('config-editor.error-msg-cannot-save'), false);
} }
this.progress.end();
}) })
.catch((error) => { .catch((error) => {
this.saveSuccess = false; this.saveSuccess = false;
this.responseText = error; this.responseText = error;
this.showToast(error, false); this.showToast(error, false);
this.progress.end();
}); });
}, },
saveConfigLocally() { saveConfigLocally() {

View File

@ -46,6 +46,7 @@
<script> <script>
import axios from 'axios'; import axios from 'axios';
import ProgressBar from 'rsup-progress';
import Button from '@/components/FormElements/Button'; import Button from '@/components/FormElements/Button';
import { modalNames } from '@/utils/defaults'; import { modalNames } from '@/utils/defaults';
import RebuildIcon from '@/assets/interface-icons/application-rebuild.svg'; import RebuildIcon from '@/assets/interface-icons/application-rebuild.svg';
@ -69,6 +70,7 @@ export default {
output: '', output: '',
message: '', message: '',
allowRebuild: true, allowRebuild: true,
progress: new ProgressBar({ color: 'var(--progress-bar)' }),
}), }),
methods: { methods: {
/* Calls to the rebuild endpoint, to kickoff the app build */ /* Calls to the rebuild endpoint, to kickoff the app build */
@ -76,12 +78,15 @@ export default {
const baseUrl = process.env.VUE_APP_DOMAIN || window.location.origin; const baseUrl = process.env.VUE_APP_DOMAIN || window.location.origin;
const endpoint = `${baseUrl}/config-manager/rebuild`; const endpoint = `${baseUrl}/config-manager/rebuild`;
this.loading = true; this.loading = true;
this.progress.start();
axios.get(endpoint) axios.get(endpoint)
.then((response) => { .then((response) => {
this.finished(response.data || false); this.finished(response.data || false);
this.progress.end();
}) })
.catch((error) => { .catch((error) => {
this.finished({ success: false, error }); this.finished({ success: false, error });
this.progress.end();
}); });
}, },
/* Called when rebuild is complete, updates UI with either success or fail message */ /* Called when rebuild is complete, updates UI with either success or fail message */

View File

@ -26,16 +26,16 @@
<ul> <ul>
<li> <li>
<router-link to="/home"> <router-link to="/home">
<IconHome /><span>Default</span> <IconHome /><span>{{ $t('alternate-views.default') }}</span>
</router-link> </router-link>
</li> </li>
<li> <li>
<router-link to="/minimal"> <router-link to="/minimal">
<IconMinimalView /><span>Minimal</span> <IconMinimalView /><span>{{ $t('alternate-views.minimal') }}</span>
</router-link> </router-link>
<li> <li>
<router-link to="/workspace"> <router-link to="/workspace">
<IconWorkspaceView /><span>Workspace</span> <IconWorkspaceView /><span>{{ $t('alternate-views.workspace') }}</span>
</router-link> </router-link>
</li> </li>
</ul> </ul>

View File

@ -16,6 +16,14 @@
/> />
</transition> </transition>
</div> </div>
<div class="switch-view-buttons">
<router-link to="/home">
<IconHome class="view-icon" v-tooltip="$t('alternate-views.default')" />
</router-link>
<router-link to="/minimal">
<IconMinimalView class="view-icon" v-tooltip="$t('alternate-views.minimal')" />
</router-link>
</div>
</nav> </nav>
</template> </template>
@ -23,6 +31,8 @@
import SideBarItem from '@/components/Workspace/SideBarItem.vue'; import SideBarItem from '@/components/Workspace/SideBarItem.vue';
import SideBarSection from '@/components/Workspace/SideBarSection.vue'; import SideBarSection from '@/components/Workspace/SideBarSection.vue';
import IconHome from '@/assets/interface-icons/application-home.svg';
import IconMinimalView from '@/assets/interface-icons/application-minimal.svg';
export default { export default {
name: 'SideBar', name: 'SideBar',
@ -38,6 +48,8 @@ export default {
components: { components: {
SideBarItem, SideBarItem,
SideBarSection, SideBarSection,
IconMinimalView,
IconHome,
}, },
methods: { methods: {
/* Toggles the section clicked, and closes all other sections */ /* Toggles the section clicked, and closes all other sections */
@ -87,4 +99,13 @@ nav.side-bar {
transform: translate(0, -80%); transform: translate(0, -80%);
} }
.switch-view-buttons {
margin-top: 0.5rem;
display: flex;
@extend .svg-button;
.view-icon {
border: none;
}
}
</style> </style>

View File

@ -46,8 +46,8 @@ export default {
<style lang="scss" scoped> <style lang="scss" scoped>
div.side-bar-item { div.side-bar-item {
color: var(--side-bar-color); color: var(--side-bar-item-color);
background: var(--side-bar-background); background: var(--side-bar-item-background);
text-align: center; text-align: center;
&.text-only { &.text-only {
background: none; background: none;

View File

@ -73,6 +73,8 @@
--side-bar-background: var(--background-darker); --side-bar-background: var(--background-darker);
--side-bar-background-lighter: var(--background); --side-bar-background-lighter: var(--background);
--side-bar-color: var(--primary); --side-bar-color: var(--primary);
--side-bar-item-background: var(--side-bar-background);
--side-bar-item-color: var(--side-bar-color);
// Minimal view // Minimal view
--minimal-view-background-color: var(--background); --minimal-view-background-color: var(--background);
--minimal-view-title-color: var(--primary); --minimal-view-title-color: var(--primary);
@ -96,6 +98,7 @@
--scroll-bar-background: var(--background-darker); --scroll-bar-background: var(--background-darker);
--highlight-color: var(--background); --highlight-color: var(--background);
--highlight-background: var(--primary); --highlight-background: var(--primary);
--progress-bar: var(--primary);
// Misc components // Misc components
--loading-screen-color: var(--primary); --loading-screen-color: var(--primary);
--loading-screen-background: var(--background); --loading-screen-background: var(--background);

View File

@ -360,7 +360,7 @@ html[data-theme='material'], html[data-theme='material-dark'] {
padding: 0; padding: 0;
} }
/* Custom layout for medium and large icons */ /* Custom layout for medium and large icons */
.item:not(.size-small) { .item-wrapper .item:not(.size-small) {
display: flex; display: flex;
flex-direction: row-reverse; flex-direction: row-reverse;
justify-content: flex-end; justify-content: flex-end;
@ -510,6 +510,8 @@ html[data-theme='material'] {
--context-menu-secondary-color: #f5f5f5; --context-menu-secondary-color: #f5f5f5;
--transparent-white-50: #00000080; --transparent-white-50: #00000080;
--status-check-tooltip-background: #fff; --status-check-tooltip-background: #fff;
--side-bar-background-lighter: #0c4eba;
--side-bar-item-background: #f5f5f5;
--minimal-view-background-color: var(--background); --minimal-view-background-color: var(--background);
--minimal-view-title-color: var(--background-darker); --minimal-view-title-color: var(--background-darker);
@ -603,6 +605,8 @@ html[data-theme='material-dark'] {
--curve-factor: 2px; --curve-factor: 2px;
--curve-factor-navbar: 0; --curve-factor-navbar: 0;
--side-bar-background: #131a1f;
--welcome-popup-background: #131a1f; --welcome-popup-background: #131a1f;
--welcome-popup-text-color: var(--primary); --welcome-popup-text-color: var(--primary);
--config-settings-background: #131a1f; --config-settings-background: #131a1f;

View File

@ -8363,6 +8363,11 @@ router@~1.3.5:
setprototypeof "1.2.0" setprototypeof "1.2.0"
utils-merge "1.0.1" utils-merge "1.0.1"
rsup-progress@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/rsup-progress/-/rsup-progress-2.0.4.tgz#bf5c1cbf7265e195112a68c7bc379d6d2c11ee7f"
integrity sha512-ZhyoR6KUjpUEDlCRBEh7o7F7gdpeCh9VA9SKHedafmh/yoTcfx9bN2nWy3L4PdiJxCcVcOmWSWj1TYFGxNdU7g==
run-async@^2.4.0: run-async@^2.4.0:
version "2.4.1" version "2.4.1"
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"