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.
|
// Check for emptyness.
|
||||||
if path == "" {
|
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.
|
// Check whether it is actually a directory.
|
||||||
|
@ -83,7 +83,7 @@ func TestCheckSharedStoragePath(t *testing.T) {
|
|||||||
assertResponseJSON(t, echoCtx, http.StatusOK, api.PathCheckResult{
|
assertResponseJSON(t, echoCtx, http.StatusOK, api.PathCheckResult{
|
||||||
Path: "",
|
Path: "",
|
||||||
IsUsable: false,
|
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).
|
// Test usable path (well, at least readable & writable; it may not be shared via Samba/NFS).
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"luxon": "^2.3.1",
|
"luxon": "^2.3.1",
|
||||||
|
"microtip": "^0.2.2",
|
||||||
"pinia": "^2.0.13",
|
"pinia": "^2.0.13",
|
||||||
"socket.io-client": "2",
|
"socket.io-client": "2",
|
||||||
"superagent": "^7.1.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>
|
<script setup>
|
||||||
import { inject } from 'vue';
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
title: String,
|
title: String,
|
||||||
|
nextLabel: {
|
||||||
|
type: String,
|
||||||
|
default: 'Next'
|
||||||
|
},
|
||||||
|
isBackVisible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
isNextClickable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const selectedStep = inject('selectedStep');
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-show="selectedStep === title">
|
<div>
|
||||||
<slot />
|
<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>
|
</div>
|
||||||
</template>
|
</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>
|
<template>
|
||||||
<div class="setup-container">
|
<div class="setup-container">
|
||||||
<steps-wrapper>
|
<h1>Flamenco Setup Assistant</h1>
|
||||||
<step-item title="Welcome">
|
<div class="setup-step">
|
||||||
<p>Let's set you up.</p>
|
<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>
|
||||||
|
<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>Using a service like Syncthing, ownCloud, or Dropbox for
|
||||||
<p>Flamenco needs some shared storage, to have a central place where the
|
this is not recommended, as Flamenco can't coordinate data synchronization.</p>
|
||||||
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>
|
|
||||||
|
|
||||||
|
<input
|
||||||
|
v-model="sharedStoragePath"
|
||||||
|
@input="checkSharedStoragePath"
|
||||||
|
@keyup.enter="nextStepAfterStoragePath"
|
||||||
|
type="text"
|
||||||
|
placeholder="Shared Storage Path"
|
||||||
|
class="path-input"
|
||||||
|
>
|
||||||
<p v-if="sharedStorageCheckResult != null"
|
<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 }}
|
{{ sharedStorageCheckResult.cause }}
|
||||||
</p>
|
</p>
|
||||||
|
<p v-else></p>
|
||||||
</step-item>
|
</step-item>
|
||||||
<step-item title="Blender">
|
<step-item
|
||||||
<p>Choose which Blender to use below:</p>
|
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-if="isBlenderExeFinding" class="is-in-progress">Looking for Blender installs...</div>
|
||||||
|
|
||||||
<div v-for="blender in allBlenders" class="blender-selector"
|
<fieldset v-if="allBlenders.length > 1">
|
||||||
:class="{ 'selected-blender': (blender == selectedBlender) }">
|
<legend>Choose which Blender to use:</legend>
|
||||||
<dl>
|
<div v-for="(blender, index) in allBlenders">
|
||||||
<dt>Version</dt>
|
<label :for="'blender-'+index">
|
||||||
<dd>{{ blender.cause }}</dd>
|
<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>
|
||||||
|
|
||||||
<dt>Path</dt>
|
<p v-if="allBlenders.length <= 1">
|
||||||
<dd>{{ blender.path }}</dd>
|
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>
|
||||||
|
|
||||||
<dt>Source</dt>
|
<input
|
||||||
<dd>{{ sourceLabels[blender.source] }}</dd>
|
@input="checkBlenderExePath"
|
||||||
</dl>
|
v-model="customBlenderExe"
|
||||||
<button @click="selectedBlender = blender" :disabled="selectedBlender == blender">Use this Blender</button>
|
type="text"
|
||||||
</div>
|
placeholder="Blender Path"
|
||||||
|
class="path-input"
|
||||||
<p>Or provide an alternative command to try:</p>
|
>
|
||||||
|
|
||||||
<form @submit.prevent="checkBlenderExePath">
|
|
||||||
<input v-model="customBlenderExe" type="text">
|
|
||||||
<button type="submit">Check</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div v-if="isBlenderExeChecking" class="is-in-progress">Checking...</div>
|
<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">
|
<p v-if="blenderExeCheckResult != null && !blenderExeCheckResult.is_usable" class="check-failed">
|
||||||
{{ blenderExeCheckResult.cause }}</p>
|
{{ blenderExeCheckResult.cause }}</p>
|
||||||
</step-item>
|
</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">
|
<div v-if="isConfigComplete">
|
||||||
<p>This is the configuration that will be used by Flamenco:</p>
|
<p>This is the configuration that will be used by Flamenco:</p>
|
||||||
<dl>
|
<dl>
|
||||||
@ -85,9 +157,8 @@
|
|||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
<p v-if="isConfirmed" class="check-ok">Configuration has been saved, Flamenco will restart.</p>
|
<p v-if="isConfirmed" class="check-ok">Configuration has been saved, Flamenco will restart.</p>
|
||||||
<button @click="confirmWizard" :disabled="isConfirming">Confirm</button>
|
|
||||||
</step-item>
|
</step-item>
|
||||||
</steps-wrapper>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer class="app-footer">
|
<footer class="app-footer">
|
||||||
@ -98,19 +169,33 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import microtip from 'microtip/microtip.css'
|
||||||
import NotificationBar from '@/components/footer/NotificationBar.vue'
|
import NotificationBar from '@/components/footer/NotificationBar.vue'
|
||||||
import UpdateListener from '@/components/UpdateListener.vue'
|
import UpdateListener from '@/components/UpdateListener.vue'
|
||||||
import StepItem from '@/components/steps/StepItem.vue';
|
import StepItem from '@/components/steps/StepItem.vue';
|
||||||
import StepsWrapper from '@/components/steps/StepsWrapper.vue';
|
|
||||||
import { MetaApi, PathCheckInput, WizardConfig } from "@/manager-api";
|
import { MetaApi, PathCheckInput, WizardConfig } from "@/manager-api";
|
||||||
import { apiClient } from '@/stores/api-query-count';
|
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 {
|
export default {
|
||||||
name: 'FirstTimeWizardView',
|
name: 'FirstTimeWizardView',
|
||||||
components: {
|
components: {
|
||||||
NotificationBar,
|
NotificationBar,
|
||||||
UpdateListener,
|
UpdateListener,
|
||||||
StepsWrapper,
|
|
||||||
StepItem,
|
StepItem,
|
||||||
},
|
},
|
||||||
data: () => ({
|
data: () => ({
|
||||||
@ -134,6 +219,8 @@ export default {
|
|||||||
},
|
},
|
||||||
isConfirming: false,
|
isConfirming: false,
|
||||||
isConfirmed: false,
|
isConfirmed: false,
|
||||||
|
currentSetupStep: 1,
|
||||||
|
overallSetupStep: 1,
|
||||||
}),
|
}),
|
||||||
computed: {
|
computed: {
|
||||||
cleanSharedStoragePath() {
|
cleanSharedStoragePath() {
|
||||||
@ -142,9 +229,14 @@ export default {
|
|||||||
cleanCustomBlenderExe() {
|
cleanCustomBlenderExe() {
|
||||||
return this.customBlenderExe.trim();
|
return this.customBlenderExe.trim();
|
||||||
},
|
},
|
||||||
|
isSharedStorageValid() {
|
||||||
|
return this.sharedStorageCheckResult != null && this.sharedStorageCheckResult.is_usable;
|
||||||
|
},
|
||||||
|
isSelectedBlenderValid() {
|
||||||
|
return this.selectedBlender != null && this.selectedBlender.is_usable;
|
||||||
|
},
|
||||||
isConfigComplete() {
|
isConfigComplete() {
|
||||||
return (this.sharedStorageCheckResult != null && this.sharedStorageCheckResult.is_usable) &&
|
return this.isSharedStorageValid && this.isSelectedBlenderValid;
|
||||||
(this.selectedBlender != null && this.selectedBlender.is_usable);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@ -159,7 +251,6 @@ export default {
|
|||||||
onSIODisconnected(reason) {
|
onSIODisconnected(reason) {
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO: add a Refresh button that calls this again.
|
|
||||||
checkSharedStoragePath() {
|
checkSharedStoragePath() {
|
||||||
const pathCheck = new PathCheckInput(this.cleanSharedStoragePath);
|
const pathCheck = new PathCheckInput(this.cleanSharedStoragePath);
|
||||||
console.log("requesting path check:", pathCheck);
|
console.log("requesting path check:", pathCheck);
|
||||||
@ -173,6 +264,7 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
findBlenderExePath() {
|
findBlenderExePath() {
|
||||||
this.isBlenderExeFinding = true;
|
this.isBlenderExeFinding = true;
|
||||||
this.autoFoundBlenders = [];
|
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() {
|
confirmWizard() {
|
||||||
const wizardConfig = new WizardConfig(
|
const wizardConfig = new WizardConfig(
|
||||||
this.sharedStorageCheckResult.path,
|
this.sharedStorageCheckResult.path,
|
||||||
@ -254,10 +369,83 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
created() {
|
||||||
|
this.checkSharedStoragePath = debounce(this.checkSharedStoragePath, 200)
|
||||||
|
this.checkBlenderExePath = debounce(this.checkBlenderExePath, 200)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<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 {
|
body.is-first-time-wizard #app {
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
@ -282,23 +470,34 @@ body.is-first-time-wizard #app {
|
|||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input.path-input {
|
||||||
|
width: 100%;
|
||||||
|
height: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.setup-container {
|
.setup-container {
|
||||||
--color-check-failed: var(--color-status-failed);
|
--color-check-failed: var(--color-status-failed);
|
||||||
--color-check-ok: var(--color-status-completed);
|
--color-check-ok: var(--color-status-completed);
|
||||||
|
|
||||||
max-width: 640px;
|
max-width: 640px;
|
||||||
margin: 20vh auto auto;
|
margin: 10vh auto auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.setup-container h1 {
|
.setup-container h1 {
|
||||||
font-size: xx-large;
|
font-size: xx-large;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.setup-container section {
|
.setup-container section {
|
||||||
font-size: larger;
|
font-size: larger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.setup-container img {
|
||||||
|
max-width: 100%;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
}
|
||||||
|
|
||||||
.setup-container p.hint {
|
.setup-container p.hint {
|
||||||
color: var(--color-text-hint);
|
color: var(--color-text-hint);
|
||||||
font-size: smaller;
|
font-size: smaller;
|
||||||
|
@ -912,6 +912,11 @@ methods@^1.1.2:
|
|||||||
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
|
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
|
||||||
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
|
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:
|
mime-db@1.52.0:
|
||||||
version "1.52.0"
|
version "1.52.0"
|
||||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
|
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user