Setup Screen: Overall UI/UX tweaks
- Added initial description and illustration - Swap "Check" button for fields with a debounced @input event - Turn Blender's list into a radio selector - Tweak wording when paths are not found - Add microtip library for tooltips - Make navigation steps clickable, according to the state
This commit is contained in:
parent
2741f900b9
commit
08f52993ad
@ -95,7 +95,7 @@ func (f *Flamenco) CheckSharedStoragePath(e echo.Context) error {
|
||||
|
||||
// Check for emptyness.
|
||||
if path == "" {
|
||||
return mkError("An empty path is never suitable as shared storage")
|
||||
return mkError("An empty path is not suitable as shared storage")
|
||||
}
|
||||
|
||||
// Check whether it is actually a directory.
|
||||
|
@ -83,7 +83,7 @@ func TestCheckSharedStoragePath(t *testing.T) {
|
||||
assertResponseJSON(t, echoCtx, http.StatusOK, api.PathCheckResult{
|
||||
Path: "",
|
||||
IsUsable: false,
|
||||
Cause: "An empty path is never suitable as shared storage",
|
||||
Cause: "An empty path is not suitable as shared storage",
|
||||
})
|
||||
|
||||
// Test usable path (well, at least readable & writable; it may not be shared via Samba/NFS).
|
||||
|
@ -19,6 +19,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"luxon": "^2.3.1",
|
||||
"microtip": "^0.2.2",
|
||||
"pinia": "^2.0.13",
|
||||
"socket.io-client": "2",
|
||||
"superagent": "^7.1.2",
|
||||
|
BIN
web/app/public/architecture.png
Normal file
BIN
web/app/public/architecture.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 45 KiB |
@ -1,13 +1,39 @@
|
||||
<script setup>
|
||||
import { inject } from 'vue';
|
||||
const props = defineProps({
|
||||
title: String,
|
||||
nextLabel: {
|
||||
type: String,
|
||||
default: 'Next'
|
||||
},
|
||||
isBackVisible: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
isNextClickable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
});
|
||||
const selectedStep = inject('selectedStep');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-show="selectedStep === title">
|
||||
<slot />
|
||||
<div>
|
||||
<h2>{{ title }}</h2>
|
||||
<slot></slot>
|
||||
<div class="btn-bar btn-bar-wide">
|
||||
<button
|
||||
v-show="isBackVisible"
|
||||
@click="$emit('backClicked')"
|
||||
class="btn"
|
||||
>Back
|
||||
</button>
|
||||
<button
|
||||
@click="$emit('nextClicked')"
|
||||
:disabled="!isNextClickable"
|
||||
class="btn btn-primary"
|
||||
>
|
||||
{{ nextLabel }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -1,125 +0,0 @@
|
||||
<script setup>
|
||||
import { useSlots, ref, provide } from 'vue';
|
||||
const slots = useSlots();
|
||||
|
||||
const stepTitles = ref(slots.default().map((step) => step.props.title));
|
||||
const selectedStep = ref(stepTitles.value[0]);
|
||||
provide('selectedStep', selectedStep);
|
||||
|
||||
function updateStepTitle(title) {
|
||||
selectedStep.value = title;
|
||||
}
|
||||
|
||||
function selectedStepNumber() {
|
||||
return stepTitles.value.indexOf(selectedStep.value);
|
||||
}
|
||||
|
||||
function nextStepTitle() {
|
||||
selectedStep.value = stepTitles.value[selectedStepNumber() + 1];
|
||||
}
|
||||
|
||||
function prevStepTitle() {
|
||||
selectedStep.value = stepTitles.value[selectedStepNumber() - 1];
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="setup-step">
|
||||
<h2>{{ selectedStep }}</h2>
|
||||
<nav>
|
||||
<ul class="progress">
|
||||
<li
|
||||
v-for="(title, index) in stepTitles"
|
||||
:class="{current: selectedStep == title, done: index < selectedStepNumber() }"
|
||||
@click="updateStepTitle(title)"
|
||||
>
|
||||
<span></span>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<slot />
|
||||
<div
|
||||
v-if="selectedStepNumber() != stepTitles.length - 1"
|
||||
class="btn-bar btn-bar-wide">
|
||||
<button
|
||||
v-if="selectedStepNumber() > 0"
|
||||
@click="prevStepTitle()"
|
||||
class="btn">Back
|
||||
</button>
|
||||
<button
|
||||
@click="nextStepTitle()"
|
||||
class="btn btn-primary">Next</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
|
||||
.progress {
|
||||
--wiz-progress-indicator-size: 8px;
|
||||
--wiz-progress-indicator-border-width: 2px;
|
||||
--wiz-progress-indicator-color: var(--color-text-hint);
|
||||
--wiz-progress-indicator-color-current: var(--color-accent);
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
list-style: none;
|
||||
margin-bottom: 2rem;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
|
||||
}
|
||||
.progress li {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Progress indicator dot. */
|
||||
.progress li span {
|
||||
background-color: var(--color-background-column);
|
||||
border-radius: 50%;
|
||||
border: var(--wiz-progress-indicator-border-width) solid var(--color-background-column);
|
||||
box-shadow: 0 0 0 var(--wiz-progress-indicator-border-width) var(--wiz-progress-indicator-color);
|
||||
content: '';
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
height: var(--wiz-progress-indicator-size);
|
||||
position: relative;
|
||||
width: var(--wiz-progress-indicator-size);
|
||||
}
|
||||
|
||||
.progress li.done span {
|
||||
background-color: var(--wiz-progress-indicator-color-current);
|
||||
box-shadow: 0 0 0 var(--wiz-progress-indicator-border-width) var(--wiz-progress-indicator-color-current);
|
||||
}
|
||||
|
||||
.progress li.current span {
|
||||
background-color: var(--color-background-column);
|
||||
box-shadow: 0 0 0 var(--wiz-progress-indicator-border-width) var(--wiz-progress-indicator-color-current);
|
||||
}
|
||||
|
||||
.progress li.current span {
|
||||
box-shadow: 0 0 0 var(--wiz-progress-indicator-border-width) var(--wiz-progress-indicator-color-current);
|
||||
}
|
||||
|
||||
|
||||
/* Progress indicator line between dots. */
|
||||
.progress:before {
|
||||
background-color: var(--wiz-progress-indicator-color);
|
||||
content: '';
|
||||
display: block;
|
||||
height: var(--wiz-progress-indicator-border-width);
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.setup-step {
|
||||
background-color: var(--color-background-column);
|
||||
border-radius: var(--border-radius);
|
||||
padding: var(--spacer) var(--spacer-lg);
|
||||
}
|
||||
|
||||
</style>
|
@ -1,60 +1,125 @@
|
||||
<template>
|
||||
<div class="setup-container">
|
||||
<steps-wrapper>
|
||||
<step-item title="Welcome">
|
||||
<p>Let's set you up.</p>
|
||||
<h1>Flamenco Setup Assistant</h1>
|
||||
<div class="setup-step">
|
||||
<ul class="progress">
|
||||
<li
|
||||
v-for="step in 4" :key="step"
|
||||
@click="jumpToStep(step)"
|
||||
:class="{
|
||||
current: step == currentSetupStep,
|
||||
done: step < currentSetupStep,
|
||||
disabled: step > overallSetupStep,
|
||||
}"
|
||||
>
|
||||
<span></span>
|
||||
</li>
|
||||
</ul>
|
||||
<step-item
|
||||
v-show="currentSetupStep == 1"
|
||||
@next-clicked="nextStep"
|
||||
:is-next-clickable="true"
|
||||
:is-back-visible="false"
|
||||
title="Welcome!"
|
||||
next-label="Let's go"
|
||||
>
|
||||
<p>This setup assistant will guide you through the initial configuration of Flamenco. You will be up
|
||||
and running in a few minutes!
|
||||
</p>
|
||||
<p>Before we start, here is a quick overview of the Flamenco architecture.</p>
|
||||
<img src="architecture.png" />
|
||||
<p>The illustration shows the key components of Flamenco, and how they interact together. In particular:</p>
|
||||
<ul>
|
||||
<li><strong>Manager</strong>: This application. It coordinates all the activity.</li>
|
||||
<li><strong>Worker</strong>: An workstation or dedicated rendering machine. It executes the tasks assigned by the Manager.</li>
|
||||
<li><strong>Shared Storage</strong>: A location accessible by the Manager and the Workers, where files, logs and internal previews can be saved.</li>
|
||||
<li><strong>Blender Add-on</strong>: This is needed to connect to the Manager and submit a job from Blender.</li>
|
||||
</ul>
|
||||
<p>More information is available on the online documentation at flamenco.blender.org.</p>
|
||||
</step-item>
|
||||
<step-item
|
||||
v-show="currentSetupStep == 2"
|
||||
@next-clicked="nextStep"
|
||||
@back-clicked="prevStep"
|
||||
:is-next-clickable="sharedStorageCheckResult !=null && sharedStorageCheckResult.is_usable"
|
||||
title="Shared Storage"
|
||||
>
|
||||
<p>Please specify a storage path (or drive), where you want to store your Flamenco data.
|
||||
The location of the shared storage should be accessible by Flamenco Manager and by the Workers.
|
||||
This could be:
|
||||
</p>
|
||||
<ul>
|
||||
<li>A NAS in your network</li>
|
||||
<li>A local drive or folder, if you are working alone</li>
|
||||
<li>Some other file sharing server</li>
|
||||
</ul>
|
||||
|
||||
<step-item title="Shared Storage">
|
||||
<p>Flamenco needs some shared storage, to have a central place where the
|
||||
Manager and Workers exchange files. This could be a NAS in your network,
|
||||
or some other file sharing server.</p>
|
||||
|
||||
<p>Make sure this path is the same for all machines involved.</p>
|
||||
|
||||
<p class="hint">Using a service like Syncthing, ownCloud, or Dropbox for
|
||||
this is not recommended, as Flamenco does not know when every machine has
|
||||
received the files.</p>
|
||||
|
||||
<!-- TODO: @submit.prevent makes the button triggerable by pressing ENTER
|
||||
in the input field, but also prevents the browser from caching
|
||||
previously-used values. Would be great if we could have both. -->
|
||||
<form @submit.prevent="checkSharedStoragePath">
|
||||
<input v-model="sharedStoragePath" type="text">
|
||||
<button type="submit">Check</button>
|
||||
</form>
|
||||
<p>Using a service like Syncthing, ownCloud, or Dropbox for
|
||||
this is not recommended, as Flamenco can't coordinate data synchronization.</p>
|
||||
|
||||
<input
|
||||
v-model="sharedStoragePath"
|
||||
@input="checkSharedStoragePath"
|
||||
@keyup.enter="nextStepAfterStoragePath"
|
||||
type="text"
|
||||
placeholder="Shared Storage Path"
|
||||
class="path-input"
|
||||
>
|
||||
<p v-if="sharedStorageCheckResult != null"
|
||||
:class="{ 'check-ok': sharedStorageCheckResult.is_usable, 'check-failed': !sharedStorageCheckResult.is_usable }">
|
||||
:class="{
|
||||
'check-ok': sharedStorageCheckResult.is_usable,
|
||||
'check-failed': !sharedStorageCheckResult.is_usable
|
||||
}">
|
||||
{{ sharedStorageCheckResult.cause }}
|
||||
</p>
|
||||
<p v-else></p>
|
||||
</step-item>
|
||||
<step-item title="Blender">
|
||||
<p>Choose which Blender to use below:</p>
|
||||
<step-item
|
||||
v-show="currentSetupStep == 3"
|
||||
@next-clicked="nextStep"
|
||||
@back-clicked="prevStep"
|
||||
:is-next-clickable="selectedBlender != null && selectedBlender.is_usable"
|
||||
title="Blender"
|
||||
>
|
||||
|
||||
<div v-if="isBlenderExeFinding" class="is-in-progress">Looking for Blender installs...</div>
|
||||
|
||||
<div v-for="blender in allBlenders" class="blender-selector"
|
||||
:class="{ 'selected-blender': (blender == selectedBlender) }">
|
||||
<dl>
|
||||
<dt>Version</dt>
|
||||
<dd>{{ blender.cause }}</dd>
|
||||
|
||||
<dt>Path</dt>
|
||||
<dd>{{ blender.path }}</dd>
|
||||
|
||||
<dt>Source</dt>
|
||||
<dd>{{ sourceLabels[blender.source] }}</dd>
|
||||
</dl>
|
||||
<button @click="selectedBlender = blender" :disabled="selectedBlender == blender">Use this Blender</button>
|
||||
<fieldset v-if="allBlenders.length > 1">
|
||||
<legend>Choose which Blender to use:</legend>
|
||||
<div v-for="(blender, index) in allBlenders">
|
||||
<label :for="'blender-'+index">
|
||||
<input type="radio" v-model="selectedBlender" name="blender" :value="blender.path" :id="'blender-'+index">
|
||||
{{ blender.cause }}
|
||||
<span
|
||||
:aria-label="blender.path"
|
||||
data-microtip-position="top"
|
||||
role="tooltip">
|
||||
[Path]
|
||||
</span>
|
||||
<span
|
||||
:aria-label="sourceLabels[blender.source]"
|
||||
data-microtip-position="top"
|
||||
role="tooltip">
|
||||
[Source]
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<p>Or provide an alternative command to try:</p>
|
||||
<p v-if="allBlenders.length <= 1">
|
||||
Provide a path to Blender. This path should be accessible by all Workers. If your rendering
|
||||
setup features operating systems different form the one you are currently using, you can
|
||||
manually set up the other paths later.
|
||||
</p>
|
||||
<p v-else>Or provide an alternative command to try.</p>
|
||||
|
||||
<form @submit.prevent="checkBlenderExePath">
|
||||
<input v-model="customBlenderExe" type="text">
|
||||
<button type="submit">Check</button>
|
||||
</form>
|
||||
<input
|
||||
@input="checkBlenderExePath"
|
||||
v-model="customBlenderExe"
|
||||
type="text"
|
||||
placeholder="Blender Path"
|
||||
class="path-input"
|
||||
>
|
||||
|
||||
<div v-if="isBlenderExeChecking" class="is-in-progress">Checking...</div>
|
||||
|
||||
@ -63,7 +128,14 @@
|
||||
<p v-if="blenderExeCheckResult != null && !blenderExeCheckResult.is_usable" class="check-failed">
|
||||
{{ blenderExeCheckResult.cause }}</p>
|
||||
</step-item>
|
||||
<step-item title="Review">
|
||||
<step-item
|
||||
v-show="currentSetupStep == 4"
|
||||
@next-clicked="confirmWizard"
|
||||
@back-clicked="prevStep"
|
||||
next-label="Confirm"
|
||||
title="Review"
|
||||
:is-next-clickable="isConfigComplete"
|
||||
>
|
||||
<div v-if="isConfigComplete">
|
||||
<p>This is the configuration that will be used by Flamenco:</p>
|
||||
<dl>
|
||||
@ -85,9 +157,8 @@
|
||||
</dl>
|
||||
</div>
|
||||
<p v-if="isConfirmed" class="check-ok">Configuration has been saved, Flamenco will restart.</p>
|
||||
<button @click="confirmWizard" :disabled="isConfirming">Confirm</button>
|
||||
</step-item>
|
||||
</steps-wrapper>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="app-footer">
|
||||
@ -98,19 +169,33 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import microtip from 'microtip/microtip.css'
|
||||
import NotificationBar from '@/components/footer/NotificationBar.vue'
|
||||
import UpdateListener from '@/components/UpdateListener.vue'
|
||||
import StepItem from '@/components/steps/StepItem.vue';
|
||||
import StepsWrapper from '@/components/steps/StepsWrapper.vue';
|
||||
import { MetaApi, PathCheckInput, WizardConfig } from "@/manager-api";
|
||||
import { apiClient } from '@/stores/api-query-count';
|
||||
|
||||
function debounce(func, wait, immediate) {
|
||||
var timeout;
|
||||
return function() {
|
||||
var context = this, args = arguments;
|
||||
var later = function() {
|
||||
timeout = null;
|
||||
if (!immediate) func.apply(context, args);
|
||||
};
|
||||
var callNow = immediate && !timeout;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
if (callNow) func.apply(context, args);
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'FirstTimeWizardView',
|
||||
components: {
|
||||
NotificationBar,
|
||||
UpdateListener,
|
||||
StepsWrapper,
|
||||
StepItem,
|
||||
},
|
||||
data: () => ({
|
||||
@ -134,6 +219,8 @@ export default {
|
||||
},
|
||||
isConfirming: false,
|
||||
isConfirmed: false,
|
||||
currentSetupStep: 1,
|
||||
overallSetupStep: 1,
|
||||
}),
|
||||
computed: {
|
||||
cleanSharedStoragePath() {
|
||||
@ -142,9 +229,14 @@ export default {
|
||||
cleanCustomBlenderExe() {
|
||||
return this.customBlenderExe.trim();
|
||||
},
|
||||
isSharedStorageValid() {
|
||||
return this.sharedStorageCheckResult != null && this.sharedStorageCheckResult.is_usable;
|
||||
},
|
||||
isSelectedBlenderValid() {
|
||||
return this.selectedBlender != null && this.selectedBlender.is_usable;
|
||||
},
|
||||
isConfigComplete() {
|
||||
return (this.sharedStorageCheckResult != null && this.sharedStorageCheckResult.is_usable) &&
|
||||
(this.selectedBlender != null && this.selectedBlender.is_usable);
|
||||
return this.isSharedStorageValid && this.isSelectedBlenderValid;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
@ -159,7 +251,6 @@ export default {
|
||||
onSIODisconnected(reason) {
|
||||
},
|
||||
|
||||
// TODO: add a Refresh button that calls this again.
|
||||
checkSharedStoragePath() {
|
||||
const pathCheck = new PathCheckInput(this.cleanSharedStoragePath);
|
||||
console.log("requesting path check:", pathCheck);
|
||||
@ -173,6 +264,7 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
findBlenderExePath() {
|
||||
this.isBlenderExeFinding = true;
|
||||
this.autoFoundBlenders = [];
|
||||
@ -232,6 +324,29 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
nextStepAfterStoragePath() {
|
||||
if (this.isSharedStorageValid) {
|
||||
this.nextStep();
|
||||
}
|
||||
},
|
||||
|
||||
nextStep() {
|
||||
if (this.overallSetupStep <= this.currentSetupStep) {
|
||||
this.overallSetupStep = this.currentSetupStep + 1;
|
||||
}
|
||||
this.currentSetupStep++;
|
||||
},
|
||||
|
||||
prevStep() {
|
||||
this.currentSetupStep--;
|
||||
},
|
||||
|
||||
jumpToStep(step) {
|
||||
if (step <= this.overallSetupStep) {
|
||||
this.currentSetupStep = step;
|
||||
}
|
||||
},
|
||||
|
||||
confirmWizard() {
|
||||
const wizardConfig = new WizardConfig(
|
||||
this.sharedStorageCheckResult.path,
|
||||
@ -254,10 +369,83 @@ export default {
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.checkSharedStoragePath = debounce(this.checkSharedStoragePath, 200)
|
||||
this.checkBlenderExePath = debounce(this.checkBlenderExePath, 200)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
|
||||
.progress {
|
||||
--wiz-progress-indicator-size: 8px;
|
||||
--wiz-progress-indicator-border-width: 2px;
|
||||
--wiz-progress-indicator-color: var(--color-text-hint);
|
||||
--wiz-progress-indicator-color-current: var(--color-accent);
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
list-style: none;
|
||||
margin-bottom: 2rem;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
|
||||
}
|
||||
.progress li {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Progress indicator dot. */
|
||||
.progress li span {
|
||||
background-color: var(--color-background-column);
|
||||
border-radius: 50%;
|
||||
border: var(--wiz-progress-indicator-border-width) solid var(--color-background-column);
|
||||
box-shadow: 0 0 0 var(--wiz-progress-indicator-border-width) var(--wiz-progress-indicator-color);
|
||||
content: '';
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
height: var(--wiz-progress-indicator-size);
|
||||
position: relative;
|
||||
width: var(--wiz-progress-indicator-size);
|
||||
}
|
||||
|
||||
.progress li.disabled span {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.progress li.done span {
|
||||
background-color: var(--wiz-progress-indicator-color-current);
|
||||
box-shadow: 0 0 0 var(--wiz-progress-indicator-border-width) var(--wiz-progress-indicator-color-current);
|
||||
}
|
||||
|
||||
.progress li.current span {
|
||||
background-color: var(--color-background-column);
|
||||
box-shadow: 0 0 0 var(--wiz-progress-indicator-border-width) var(--wiz-progress-indicator-color-current);
|
||||
}
|
||||
|
||||
.progress li.current span {
|
||||
box-shadow: 0 0 0 var(--wiz-progress-indicator-border-width) var(--wiz-progress-indicator-color-current);
|
||||
}
|
||||
|
||||
|
||||
/* Progress indicator line between dots. */
|
||||
.progress:before {
|
||||
background-color: var(--wiz-progress-indicator-color);
|
||||
content: '';
|
||||
display: block;
|
||||
height: var(--wiz-progress-indicator-border-width);
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.setup-step {
|
||||
background-color: var(--color-background-column);
|
||||
border-radius: var(--border-radius);
|
||||
padding: var(--spacer) var(--spacer-lg);
|
||||
}
|
||||
|
||||
|
||||
body.is-first-time-wizard #app {
|
||||
grid-template-areas:
|
||||
@ -282,23 +470,34 @@ body.is-first-time-wizard #app {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
input.path-input {
|
||||
width: 100%;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.setup-container {
|
||||
--color-check-failed: var(--color-status-failed);
|
||||
--color-check-ok: var(--color-status-completed);
|
||||
|
||||
max-width: 640px;
|
||||
margin: 20vh auto auto;
|
||||
margin: 10vh auto auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.setup-container h1 {
|
||||
font-size: xx-large;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.setup-container section {
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
.setup-container img {
|
||||
max-width: 100%;
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.setup-container p.hint {
|
||||
color: var(--color-text-hint);
|
||||
font-size: smaller;
|
||||
|
@ -912,6 +912,11 @@ methods@^1.1.2:
|
||||
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
|
||||
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
|
||||
|
||||
microtip@^0.2.2:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/microtip/-/microtip-0.2.2.tgz#9d9d43cbbf6815d6d16a514abc71c17c5b91d1e9"
|
||||
integrity sha512-oah38eH5vSHVFP6yXjbKWOYt92mav++0j3zh844h1vhOscqEg7Rf4agDEIwUTFCcAPPdlhYUQMe6eZfHgD+4oQ==
|
||||
|
||||
mime-db@1.52.0:
|
||||
version "1.52.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
|
||||
|
Loading…
x
Reference in New Issue
Block a user