diff options
67 files changed, 1110 insertions, 649 deletions
@@ -1,38 +1,45 @@ <script setup lang="ts"> -import '~/assets/main.css' +import '~/assets/main.css'; useHead({ - link: [{ - rel: 'preconnect', - href: 'https://rsms.me/' - }, { - rel: 'stylesheet', - href: 'https://rsms.me/inter/inter.css', - crossorigin: '' - }, { - rel: 'apple-touch-icon', - sizes: '180x180', - href: '/apple-touch-icon.png' - }, { - rel: 'icon', - type: 'image/png', - sizes: '32x32', - href: '/favicon-32x32.png' - }, { - rel: 'icon', - type: 'image/png', - sizes: '16x16', - href: '/favicon-16x16.png' - }, { - rel: 'manifest', - href: '/site.webmanifest' - }] -}) + link: [ + { + rel: 'preconnect', + href: 'https://rsms.me/', + }, + { + rel: 'stylesheet', + href: 'https://rsms.me/inter/inter.css', + crossorigin: '', + }, + { + rel: 'apple-touch-icon', + sizes: '180x180', + href: '/apple-touch-icon.png', + }, + { + rel: 'icon', + type: 'image/png', + sizes: '32x32', + href: '/favicon-32x32.png', + }, + { + rel: 'icon', + type: 'image/png', + sizes: '16x16', + href: '/favicon-16x16.png', + }, + { + rel: 'manifest', + href: '/site.webmanifest', + }, + ], +}); useSeoMeta({ title: 'Quests Web Editor', ogTitle: 'Quests Web Editor', -}) +}); </script> <template> diff --git a/components/base/Button.vue b/components/base/Button.vue index a53e329..663ac47 100644 --- a/components/base/Button.vue +++ b/components/base/Button.vue @@ -13,7 +13,7 @@ const props = defineProps({ label: String, icon: { type: Array<String>, - required: false + required: false, }, disabled: Boolean, }); @@ -28,8 +28,11 @@ const onClick = (event: MouseEvent) => { </script> <template> - <a id="button" :class="{ text: type === 'text', solid: type === 'solid', disabled: disabled, [accent]: true }" - @click.stop="onClick"> + <a + id="button" + :class="{ text: type === 'text', solid: type === 'solid', disabled: disabled, [accent]: true }" + @click.stop="onClick" + > <font-awesome-icon :icon="icon" v-if="icon" /> {{ label }} </a> @@ -68,7 +71,7 @@ const onClick = (event: MouseEvent) => { color: var(--color-false); &:hover { - color: var(--color-false-hover) + color: var(--color-false-hover); } } @@ -103,4 +106,4 @@ const onClick = (event: MouseEvent) => { background-color: var(--color-border); } } -</style>
\ No newline at end of file +</style> diff --git a/components/base/Checkbox.vue b/components/base/Checkbox.vue index e0325e7..b262b8e 100644 --- a/components/base/Checkbox.vue +++ b/components/base/Checkbox.vue @@ -6,7 +6,6 @@ defineProps({ label: String, description: String, }); - </script> <template> @@ -38,5 +37,4 @@ input { .checkbox { padding: 0 0 0 20px; } - -</style>
\ No newline at end of file +</style> diff --git a/components/base/ItemStack/ItemStackForm.vue b/components/base/ItemStack/ItemStackForm.vue index ef13abd..ddf8144 100644 --- a/components/base/ItemStack/ItemStackForm.vue +++ b/components/base/ItemStack/ItemStackForm.vue @@ -16,25 +16,27 @@ const yamlCode = ref(''); const switchToYamlEditor = () => { yamlCode.value = stringify({ - type: model.value.type || model.value.material || model.value.item || "", - ...("name" in model.value && { name: model.value.name }), - ...("lore" in model.value && { lore: model.value.lore }), - ...("enchantments" in model.value && { enchantments: model.value.enchantments }), - ...("itemflags" in model.value && { itemflags: model.value.itemflags }), - ...("custommodeldata" in model.value && { custommodeldata: model.value.custommodeldata }), - ...("unbreakable" in model.value && { unbreakable: model.value.unbreakable }), - ...("attributemodifiers" in model.value && { attributemodifiers: model.value.attributemodifiers }), - ...("owner-base64" in model.value && { ["owner-base64"]: model.value["owner-base64"] }), - ...("owner-username" in model.value && { ["owner-username"]: model.value["owner-username"] }), - ...("owner-uuid" in model.value && { ["owner-uuid"]: model.value["owner-uuid"] }) - }) + type: model.value.type || model.value.material || model.value.item || '', + ...('name' in model.value && { name: model.value.name }), + ...('lore' in model.value && { lore: model.value.lore }), + ...('enchantments' in model.value && { enchantments: model.value.enchantments }), + ...('itemflags' in model.value && { itemflags: model.value.itemflags }), + ...('custommodeldata' in model.value && { custommodeldata: model.value.custommodeldata }), + ...('unbreakable' in model.value && { unbreakable: model.value.unbreakable }), + ...('attributemodifiers' in model.value && { + attributemodifiers: model.value.attributemodifiers, + }), + ...('owner-base64' in model.value && { ['owner-base64']: model.value['owner-base64'] }), + ...('owner-username' in model.value && { ['owner-username']: model.value['owner-username'] }), + ...('owner-uuid' in model.value && { ['owner-uuid']: model.value['owner-uuid'] }), + }); editAsYaml.value = true; -} +}; const switchToGuiEditor = () => { parseAndSetYamlValues(); editAsYaml.value = false; -} +}; const parseAndSetYamlValues = (): boolean => { let newValues; @@ -45,19 +47,31 @@ const parseAndSetYamlValues = (): boolean => { } model.value.type = newValues.type; - if ("name" in newValues) model.value.name = String(newValues.name); - if ("lore" in newValues) model.value.lore = Array.isArray(newValues.lore) ? newValues.lore : [newValues.lore]; - if ("enchantments" in newValues) model.value.enchantments = Array.isArray(newValues.enchantments) ? newValues.enchantments : [newValues.enchantments]; - if ("itemflags" in newValues) model.value.itemflags = Array.isArray(newValues.itemflags) ? newValues.itemflags : [newValues.itemflags]; - if ("custommodeldata" in newValues) model.value.custommodeldata = Number(newValues.custommodeldata) || 0; - if ("unbreakable" in newValues) model.value.unbreakable = !!newValues.unbreakable; - if ("attributemodifiers" in newValues) model.value.attributemodifiers = Array.isArray(newValues.attributemodifiers) ? newValues.attributemodifiers : [newValues.attributemodifiers]; - if ("owner-base64" in newValues) model.value["owner-base64"] = String(newValues["owner-base64"]); - if ("owner-username" in newValues) model.value["owner-username"] = String(newValues["owner-username"]); - if ("owner-uuid" in newValues) model.value["owner-uuid"] = String(newValues["owner-uuid"]); + if ('name' in newValues) model.value.name = String(newValues.name); + if ('lore' in newValues) + model.value.lore = Array.isArray(newValues.lore) ? newValues.lore : [newValues.lore]; + if ('enchantments' in newValues) + model.value.enchantments = Array.isArray(newValues.enchantments) + ? newValues.enchantments + : [newValues.enchantments]; + if ('itemflags' in newValues) + model.value.itemflags = Array.isArray(newValues.itemflags) + ? newValues.itemflags + : [newValues.itemflags]; + if ('custommodeldata' in newValues) + model.value.custommodeldata = Number(newValues.custommodeldata) || 0; + if ('unbreakable' in newValues) model.value.unbreakable = !!newValues.unbreakable; + if ('attributemodifiers' in newValues) + model.value.attributemodifiers = Array.isArray(newValues.attributemodifiers) + ? newValues.attributemodifiers + : [newValues.attributemodifiers]; + if ('owner-base64' in newValues) model.value['owner-base64'] = String(newValues['owner-base64']); + if ('owner-username' in newValues) + model.value['owner-username'] = String(newValues['owner-username']); + if ('owner-uuid' in newValues) model.value['owner-uuid'] = String(newValues['owner-uuid']); return true; -} +}; const itemName = computed({ get() { @@ -70,7 +84,7 @@ const itemName = computed({ const isOptionSet = (option: string) => { return option in model.value; -} +}; const setOption = (option: string, type: 'string' | 'string-list' | 'number' | 'boolean') => { switch (type) { @@ -87,11 +101,11 @@ const setOption = (option: string, type: 'string' | 'string-list' | 'number' | ' model.value[option] = []; break; } -} +}; const removeOption = (option: string) => { delete model.value[option]; -} +}; const itemType = computed({ get() { @@ -114,8 +128,8 @@ const itemLore = computed({ }, set(newValue: string) { model.value.lore = newValue.split('\n'); - } -}) + }, +}); const itemEnchantments = computed({ get() { @@ -123,8 +137,8 @@ const itemEnchantments = computed({ }, set(newValue: any) { model.value.enchantments = newValue; - } -}) + }, +}); const itemCustomModelData = computed({ get() { @@ -132,8 +146,8 @@ const itemCustomModelData = computed({ }, set(newValue: any) { model.value.custommodeldata = newValue; - } -}) + }, +}); const itemItemflags = computed({ get() { @@ -141,8 +155,8 @@ const itemItemflags = computed({ }, set(newValue: any) { model.value.itemflags = newValue; - } -}) + }, +}); </script> <template> @@ -151,69 +165,163 @@ const itemItemflags = computed({ <div class="label-with-button"> <label for="itemstack-type">Type</label> - <Button label="Edit as YAML" :icon="['fas', 'pencil']" @click="switchToYamlEditor()"></Button> + <Button + label="Edit as YAML" + :icon="['fas', 'pencil']" + @click="switchToYamlEditor()" + ></Button> </div> - <multiselect v-model="itemType" :options="materials" :searchable="true" placeholder="Choose a material" /> + <multiselect + v-model="itemType" + :options="materials" + :searchable="true" + placeholder="Choose a material" + /> </div> <div class="option-group" v-if="isOptionSet('name')"> - <ItemStackFormOptionLabel option="name" type="string" label="Name" :is-option-set-fn="isOptionSet" - :set-option-fn="setOption" :remove-option-fn="removeOption" /> + <ItemStackFormOptionLabel + option="name" + type="string" + label="Name" + :is-option-set-fn="isOptionSet" + :set-option-fn="setOption" + :remove-option-fn="removeOption" + /> - <input id="itemstack-name" name="itemstack-name" v-model="itemName" placeholder="Enter display name" /> + <input + id="itemstack-name" + name="itemstack-name" + v-model="itemName" + placeholder="Enter display name" + /> </div> <div class="option-group" v-if="isOptionSet('lore')"> - <ItemStackFormOptionLabel option="lore" type="string-list" label="Lore" :is-option-set-fn="isOptionSet" - :set-option-fn="setOption" :remove-option-fn="removeOption" /> + <ItemStackFormOptionLabel + option="lore" + type="string-list" + label="Lore" + :is-option-set-fn="isOptionSet" + :set-option-fn="setOption" + :remove-option-fn="removeOption" + /> - <textarea rows="5" id="itemstack-lore" name="itemstack-lore" v-model="itemLore" placeholder="Enter item lore" /> + <textarea + rows="5" + id="itemstack-lore" + name="itemstack-lore" + v-model="itemLore" + placeholder="Enter item lore" + /> </div> <div class="option-group" v-if="isOptionSet('enchantments')"> - <ItemStackFormOptionLabel option="enchantments" type="string-list" label="Enchantments" - :is-option-set-fn="isOptionSet" :set-option-fn="setOption" :remove-option-fn="removeOption" /> + <ItemStackFormOptionLabel + option="enchantments" + type="string-list" + label="Enchantments" + :is-option-set-fn="isOptionSet" + :set-option-fn="setOption" + :remove-option-fn="removeOption" + /> - <multiselect v-model="itemEnchantments" :options="enchantments" :searchable="true" :multiple="true" - placeholder="Enter enchantment names" /> + <multiselect + v-model="itemEnchantments" + :options="enchantments" + :searchable="true" + :multiple="true" + placeholder="Enter enchantment names" + /> </div> <div class="option-group" v-if="isOptionSet('itemflags')"> - <ItemStackFormOptionLabel option="itemflags" type="string-list" label="Item flags" :is-option-set-fn="isOptionSet" - :set-option-fn="setOption" :remove-option-fn="removeOption" /> + <ItemStackFormOptionLabel + option="itemflags" + type="string-list" + label="Item flags" + :is-option-set-fn="isOptionSet" + :set-option-fn="setOption" + :remove-option-fn="removeOption" + /> - <multiselect v-model="itemItemflags" :options="itemflags" :searchable="true" :multiple="true" - placeholder="Enter item flags" /> + <multiselect + v-model="itemItemflags" + :options="itemflags" + :searchable="true" + :multiple="true" + placeholder="Enter item flags" + /> </div> <div class="option-group" v-if="isOptionSet('custommodeldata')"> - <ItemStackFormOptionLabel option="custommodeldata" type="number" label="Custom model data" - :is-option-set-fn="isOptionSet" :set-option-fn="setOption" :remove-option-fn="removeOption" /> + <ItemStackFormOptionLabel + option="custommodeldata" + type="number" + label="Custom model data" + :is-option-set-fn="isOptionSet" + :set-option-fn="setOption" + :remove-option-fn="removeOption" + /> - <input id="itemstack-custommodeldata" name="itemstack-custommodeldata" v-model="itemCustomModelData" type="number" - placeholder="Enter custom model data" /> + <input + id="itemstack-custommodeldata" + name="itemstack-custommodeldata" + v-model="itemCustomModelData" + type="number" + placeholder="Enter custom model data" + /> </div> <div> - <Button v-if="!isOptionSet('name')" @click="setOption('name', 'string')" label="Add specific name" - :icon="['fas', 'plus']"></Button> - <Button v-if="!isOptionSet('lore')" @click="setOption('lore', 'string-list')" label="Add specific lore" - :icon="['fas', 'plus']"></Button> - <Button v-if="!isOptionSet('enchantments')" @click="setOption('enchantments', 'string-list')" - label="Add specific enchantments" :icon="['fas', 'plus']"></Button> - <Button v-if="!isOptionSet('itemflags')" @click="setOption('itemflags', 'string-list')" - label="Add specific item flags" :icon="['fas', 'plus']"></Button> - <Button v-if="!isOptionSet('custommodeldata')" @click="setOption('custommodeldata', 'string')" - label="Add specific custom model data" :icon="['fas', 'plus']"></Button> + <Button + v-if="!isOptionSet('name')" + @click="setOption('name', 'string')" + label="Add specific name" + :icon="['fas', 'plus']" + ></Button> + <Button + v-if="!isOptionSet('lore')" + @click="setOption('lore', 'string-list')" + label="Add specific lore" + :icon="['fas', 'plus']" + ></Button> + <Button + v-if="!isOptionSet('enchantments')" + @click="setOption('enchantments', 'string-list')" + label="Add specific enchantments" + :icon="['fas', 'plus']" + ></Button> + <Button + v-if="!isOptionSet('itemflags')" + @click="setOption('itemflags', 'string-list')" + label="Add specific item flags" + :icon="['fas', 'plus']" + ></Button> + <Button + v-if="!isOptionSet('custommodeldata')" + @click="setOption('custommodeldata', 'string')" + label="Add specific custom model data" + :icon="['fas', 'plus']" + ></Button> </div> - <p>For - <NuxtLink to="https://quests.leonardobishop.com/configuration/defining-items.html#unbreakable"> - unbreakable</NuxtLink>, - <NuxtLink to="https://quests.leonardobishop.com/configuration/defining-items.html#attribute-modifiers"> - attribute-modifiers</NuxtLink>, and - <NuxtLink to="https://quests.leonardobishop.com/configuration/defining-items.html#owner">owner</NuxtLink> please - switch to YAML. + <p> + For + <NuxtLink + to="https://quests.leonardobishop.com/configuration/defining-items.html#unbreakable" + > + unbreakable</NuxtLink + >, + <NuxtLink + to="https://quests.leonardobishop.com/configuration/defining-items.html#attribute-modifiers" + > + attribute-modifiers</NuxtLink + >, and + <NuxtLink to="https://quests.leonardobishop.com/configuration/defining-items.html#owner" + >owner</NuxtLink + > + please switch to YAML. </p> </div> @@ -222,7 +330,11 @@ const itemItemflags = computed({ <div class="label-with-button"> <label for="itemstack-name">YAML</label> - <Button label="Use GUI editor" :icon="['fas', 'pencil']" @click="switchToGuiEditor"></Button> + <Button + label="Use GUI editor" + :icon="['fas', 'pencil']" + @click="switchToGuiEditor" + ></Button> </div> <textarea id="yaml-editor" rows="10" v-model="yamlCode"></textarea> @@ -245,4 +357,4 @@ const itemItemflags = computed({ #yaml-editor { font-family: monospace; } -</style>
\ No newline at end of file +</style> diff --git a/components/base/ItemStack/ItemStackFormOptionLabel.vue b/components/base/ItemStack/ItemStackFormOptionLabel.vue index ab067f8..e334950 100644 --- a/components/base/ItemStack/ItemStackFormOptionLabel.vue +++ b/components/base/ItemStack/ItemStackFormOptionLabel.vue @@ -1,20 +1,24 @@ <script setup lang="ts"> defineProps<{ - option: string - label: string - type: string + option: string; + label: string; + type: string; isOptionSetFn: (option: string) => boolean; removeOptionFn: (option: string) => void; setOptionFn: (option: string, type: any) => void; -}>() +}>(); </script> <template> <div class="label-with-button"> <label :for="'itemstack-' + option">{{ label }}</label> - <Button v-if="isOptionSetFn(option)" label="Remove" :icon="['fas', 'minus']" - @click="removeOptionFn(option)"></Button> + <Button + v-if="isOptionSetFn(option)" + label="Remove" + :icon="['fas', 'minus']" + @click="removeOptionFn(option)" + ></Button> </div> </template> @@ -23,4 +27,4 @@ defineProps<{ display: flex; justify-content: space-between; } -</style>
\ No newline at end of file +</style> diff --git a/components/base/ItemStack/ItemStackModal.vue b/components/base/ItemStack/ItemStackModal.vue index 2a53926..e4e39e5 100644 --- a/components/base/ItemStack/ItemStackModal.vue +++ b/components/base/ItemStack/ItemStackModal.vue @@ -5,7 +5,7 @@ import materials from '@/lib/materials'; const model = defineModel(); const emit = defineEmits(['confirm']); const props = defineProps<{ - value: any + value: any; }>(); const session = useSessionStore(); @@ -17,15 +17,14 @@ const isQuestItem = computed(() => { }); const isItemStack = computed(() => { return ( - typeof value.value === 'object' - && ( - value.value?.item !== undefined - || value.value?.type !== undefined - || value.value?.material !== undefined - )) + typeof value.value === 'object' && + (value.value?.item !== undefined || + value.value?.type !== undefined || + value.value?.material !== undefined) + ); }); const isMaterial = computed(() => { - return typeof value.value === 'string' && materials.includes(value.value) + return typeof value.value === 'string' && materials.includes(value.value); }); const selectedType = ref( @@ -46,13 +45,15 @@ const selectedQuestItem = computed({ return value.value?.['quest-item']; }, set(newValue: string) { - value.value = {} + value.value = {}; if (newValue) { value.value['quest-item'] = newValue; } - } -}) -const knownQuestItems = computed(() => { return session.session.items.map((item) => item.id) }); + }, +}); +const knownQuestItems = computed(() => { + return session.session.items.map((item) => item.id); +}); const setSelectedType = (type: string) => { if (type === 'questitem') { @@ -78,21 +79,33 @@ const confirm = () => { <template v-slot:body> <div id="type"> - <span class="option" @click="setSelectedType('questitem')" :class="{ selected: selectedType === 'questitem' }"> + <span + class="option" + @click="setSelectedType('questitem')" + :class="{ selected: selectedType === 'questitem' }" + > <span> <font-awesome-icon :icon="['fas', 'tag']" /> Quest Item </span> <p v-if="noTypeSelected">Re-use a quest item.</p> </span> - <span class="option" @click="setSelectedType('itemstack')" :class="{ selected: selectedType === 'itemstack' }"> + <span + class="option" + @click="setSelectedType('itemstack')" + :class="{ selected: selectedType === 'itemstack' }" + > <span> <font-awesome-icon :icon="['fas', 'cube']" /> ItemStack </span> <p v-if="noTypeSelected">Define a new item stack.</p> </span> - <span class="option" @click="setSelectedType('material')" :class="{ selected: selectedType === 'material' }"> + <span + class="option" + @click="setSelectedType('material')" + :class="{ selected: selectedType === 'material' }" + > <span> <font-awesome-icon :icon="['fas', 'apple-whole']" /> Material @@ -103,7 +116,12 @@ const confirm = () => { <div id="material" class="option-group" v-if="selectedType === 'material'"> <label for="material">Material</label> - <multiselect v-model="value" :options="materials" :searchable="true" placeholder="Enter material name" /> + <multiselect + v-model="value" + :options="materials" + :searchable="true" + placeholder="Enter material name" + /> <p>Any items of this material will be matched.</p> </div> @@ -113,8 +131,12 @@ const confirm = () => { <div id="quest-item" class="option-group" v-if="selectedType === 'questitem'"> <label for="quest-item">Quest Item</label> - <multiselect v-model="selectedQuestItem" :options="knownQuestItems" :searchable="true" - placeholder="Enter quest item" /> + <multiselect + v-model="selectedQuestItem" + :options="knownQuestItems" + :searchable="true" + placeholder="Enter quest item" + /> </div> <div id="confirm" class="control-group"> @@ -174,4 +196,4 @@ const confirm = () => { } } } -</style>
\ No newline at end of file +</style> diff --git a/components/base/ItemStack/ItemStackPicker.vue b/components/base/ItemStack/ItemStackPicker.vue index 2dc35d3..09c0130 100644 --- a/components/base/ItemStack/ItemStackPicker.vue +++ b/components/base/ItemStack/ItemStackPicker.vue @@ -13,11 +13,13 @@ const showItemStackModal = ref(false); //TODO unshitify const empty = computed(() => { - return value.value === undefined - || value.value === null - || value.value === '' - || (Array.isArray(value.value) && value.value.length === 0) - || (typeof value.value === 'object' && Object.keys(value.value).length === 0); + return ( + value.value === undefined || + value.value === null || + value.value === '' || + (Array.isArray(value.value) && value.value.length === 0) || + (typeof value.value === 'object' && Object.keys(value.value).length === 0) + ); }); const isQuestItem = computed(() => { return value.value?.['quest-item'] !== undefined; @@ -27,16 +29,12 @@ const isItemStack = computed(() => { return false; } - const key = 'item' in value.value - ? 'item' - : 'type' in value.value - ? 'type' - : 'material' + const key = 'item' in value.value ? 'item' : 'type' in value.value ? 'type' : 'material'; - return (!!value.value[key]); + return !!value.value[key]; }); const isMaterial = computed(() => { - return typeof value.value === 'string' && materials.includes(value.value) + return typeof value.value === 'string' && materials.includes(value.value); }); const update = (newValue: any) => { @@ -49,13 +47,19 @@ const update = (newValue: any) => { <template> <div class="itemstack" @click="showItemStackModal = true"> <span v-if="empty" class="empty">ItemStack...</span> - <span v-if="isQuestItem" class="item"><font-awesome-icon :icon="['fas', 'tag']" /> Quest Item: {{ - value['quest-item'] }}</span> - <span v-if="isItemStack" class="item"><font-awesome-icon :icon="['fas', 'cube']" /> ItemStack: {{ value.type || - value.item || value.material }}</span> - <span v-if="isMaterial" class="item"><font-awesome-icon :icon="['fas', 'apple-whole']" /> {{ value }}</span> - <span v-if="!empty && !isQuestItem && !isItemStack && !isMaterial" class="invalid"><font-awesome-icon - :icon="['fas', 'triangle-exclamation']" /> Invalid ItemStack</span> + <span v-if="isQuestItem" class="item" + ><font-awesome-icon :icon="['fas', 'tag']" /> Quest Item: {{ value['quest-item'] }}</span + > + <span v-if="isItemStack" class="item" + ><font-awesome-icon :icon="['fas', 'cube']" /> ItemStack: + {{ value.type || value.item || value.material }}</span + > + <span v-if="isMaterial" class="item" + ><font-awesome-icon :icon="['fas', 'apple-whole']" /> {{ value }}</span + > + <span v-if="!empty && !isQuestItem && !isItemStack && !isMaterial" class="invalid" + ><font-awesome-icon :icon="['fas', 'triangle-exclamation']" /> Invalid ItemStack</span + > </div> <ItemStackModal v-model="showItemStackModal" :value="value" @confirm="update" /> @@ -94,4 +98,4 @@ const update = (newValue: any) => { background-color: var(--color-hover); } } -</style>
\ No newline at end of file +</style> diff --git a/components/base/Modal.vue b/components/base/Modal.vue index f3a32d0..0edc297 100644 --- a/components/base/Modal.vue +++ b/components/base/Modal.vue @@ -1,6 +1,5 @@ <script setup lang="ts"> const model = defineModel(); - </script> <template> @@ -69,4 +68,4 @@ const model = defineModel(); font-weight: 600; border-bottom: none; } -</style>
\ No newline at end of file +</style> diff --git a/components/base/Pulser.vue b/components/base/Pulser.vue index 796b3cc..c4151fb 100644 --- a/components/base/Pulser.vue +++ b/components/base/Pulser.vue @@ -12,7 +12,7 @@ height: 100px; width: 100px; - >div { + > div { animation: growAndFade 3s infinite ease-out; background-color: var(--color-primary); border-radius: 50%; @@ -37,7 +37,7 @@ @keyframes growAndFade { 0% { - opacity: .25; + opacity: 0.25; transform: scale(0); } @@ -53,4 +53,4 @@ body { justify-content: center; margin: 0; } -</style>
\ No newline at end of file +</style> diff --git a/components/base/TrueFalseSwitch.vue b/components/base/TrueFalseSwitch.vue index 152efa3..46a9d9a 100644 --- a/components/base/TrueFalseSwitch.vue +++ b/components/base/TrueFalseSwitch.vue @@ -50,4 +50,4 @@ const invert = () => { background-color: var(--color-hover); } } -</style>
\ No newline at end of file +</style> diff --git a/components/editor/EditorOptionsPanel.vue b/components/editor/EditorOptionsPanel.vue index 7cf8982..05d1f34 100644 --- a/components/editor/EditorOptionsPanel.vue +++ b/components/editor/EditorOptionsPanel.vue @@ -1,5 +1,4 @@ -<script setup lang="ts"> -</script> +<script setup lang="ts"></script> <template> <div id="options-panel"> @@ -16,4 +15,4 @@ padding: 1rem; height: 100%; } -</style>
\ No newline at end of file +</style> diff --git a/components/editor/EditorSidebar.vue b/components/editor/EditorSidebar.vue index 7ac4996..9d5818d 100644 --- a/components/editor/EditorSidebar.vue +++ b/components/editor/EditorSidebar.vue @@ -10,19 +10,27 @@ const currentType = ref('quests' as 'quests' | 'items'); const setSelectedType = (type: 'quests' | 'items') => { currentType.value = type; -} +}; </script> <template> <div id="sidebar-container"> <div id="selector"> - <span class="option" @click="setSelectedType('quests')" :class="{ selected: currentType === 'quests' }"> + <span + class="option" + @click="setSelectedType('quests')" + :class="{ selected: currentType === 'quests' }" + > <span> <font-awesome-icon :icon="['far', 'compass']" /> Quests </span> </span> - <span class="option" @click="setSelectedType('items')" :class="{ selected: currentType === 'items' }"> + <span + class="option" + @click="setSelectedType('items')" + :class="{ selected: currentType === 'items' }" + > <span> <font-awesome-icon :icon="['fas', 'cubes']" /> Items @@ -30,13 +38,22 @@ const setSelectedType = (type: 'quests' | 'items') => { </span> </div> <div id="quests" v-if="currentType === 'quests'"> - <EditorSidebarCategory v-for="category in session.categories" :key="category.id" :category="category" /> + <EditorSidebarCategory + v-for="category in session.categories" + :key="category.id" + :category="category" + /> <EditorSidebarQuest - v-for="quest in session.quests.filter((q) => (!session.categories.some((c) => c.id === q.options.category)))" - :key="quest.id" :quest="quest" /> - <p id="count">{{ session.quests.length }} quest{{ session.quests.length === 1 ? '' : 's' }}, {{ - session.categories.length }} - categor{{ session.categories.length === 1 ? 'y' : 'ies' }}</p> + v-for="quest in session.quests.filter( + (q) => !session.categories.some((c) => c.id === q.options.category) + )" + :key="quest.id" + :quest="quest" + /> + <p id="count"> + {{ session.quests.length }} quest{{ session.quests.length === 1 ? '' : 's' }}, + {{ session.categories.length }} categor{{ session.categories.length === 1 ? 'y' : 'ies' }} + </p> </div> <div id="items" v-if="currentType === 'items'"> <EditorSidebarItem v-for="item in session.items" :key="item.id" :item="item" /> @@ -88,7 +105,7 @@ const setSelectedType = (type: 'quests' | 'items') => { transition: color 0.3s; &:hover { - color: var(--color-text) + color: var(--color-text); } } } @@ -105,7 +122,7 @@ const setSelectedType = (type: 'quests' | 'items') => { border-top: 1px solid var(--color-border); position: absolute; bottom: 0; - width: 100% + width: 100%; } #count { @@ -115,4 +132,4 @@ const setSelectedType = (type: 'quests' | 'items') => { color: var(--color-text-mute); } } -</style>
\ No newline at end of file +</style> diff --git a/components/editor/EditorSidebarCategory.vue b/components/editor/EditorSidebarCategory.vue index 27a5ce0..c5adbca 100644 --- a/components/editor/EditorSidebarCategory.vue +++ b/components/editor/EditorSidebarCategory.vue @@ -33,8 +33,11 @@ const selected = computed(() => { <template> <div id="category-container" :class="{ selected: selected }"> <span id="category-title" @click="setSelectedCategory"> - <font-awesome-icon @click.stop="expandCategory" class="category-icon" - :icon="expanded ? ['fas', 'caret-down'] : ['fas', 'caret-up']" /> + <font-awesome-icon + @click.stop="expandCategory" + class="category-icon" + :icon="expanded ? ['fas', 'caret-down'] : ['fas', 'caret-up']" + /> <span id="category-name"> <span id="category-display-name">{{ stripColorCodes(category.display.name) }}</span> <code id="category-display-id">{{ category.id }}</code> @@ -42,7 +45,12 @@ const selected = computed(() => { </span> </div> <div v-if="expanded" id="quests"> - <EditorSidebarQuest class="quest" v-for="quest in questsInCategory" :key="quest.id" :quest="quest" /> + <EditorSidebarQuest + class="quest" + v-for="quest in questsInCategory" + :key="quest.id" + :quest="quest" + /> </div> </template> @@ -91,4 +99,4 @@ const selected = computed(() => { #category-container:hover { background-color: var(--color-hover); } -</style>
\ No newline at end of file +</style> diff --git a/components/editor/EditorSidebarItem.vue b/components/editor/EditorSidebarItem.vue index 55f81d0..0e0040d 100644 --- a/components/editor/EditorSidebarItem.vue +++ b/components/editor/EditorSidebarItem.vue @@ -63,4 +63,4 @@ const selected = computed(() => { #item-container:hover { background-color: var(--color-hover); } -</style>
\ No newline at end of file +</style> diff --git a/components/editor/EditorSidebarMainConfiguration.vue b/components/editor/EditorSidebarMainConfiguration.vue index 9ebef0e..9bc9239 100644 --- a/components/editor/EditorSidebarMainConfiguration.vue +++ b/components/editor/EditorSidebarMainConfiguration.vue @@ -16,9 +16,7 @@ const selected = computed(() => { <div id="container" :class="{ selected: selected }"> <span id="title" @click="setSelected"> <font-awesome-icon class="icon" :icon="['fas', 'wrench']" /> - <span id="name"> - Configuration - </span> + <span id="name"> Configuration </span> </span> </div> </template> @@ -53,4 +51,4 @@ const selected = computed(() => { #container:hover { background-color: var(--color-hover); } -</style>
\ No newline at end of file +</style> diff --git a/components/editor/EditorSidebarQuest.vue b/components/editor/EditorSidebarQuest.vue index fb21042..54814f0 100644 --- a/components/editor/EditorSidebarQuest.vue +++ b/components/editor/EditorSidebarQuest.vue @@ -64,4 +64,4 @@ const selected = computed(() => { #quest-container:hover { background-color: var(--color-hover); } -</style>
\ No newline at end of file +</style> diff --git a/components/editor/category/EditorCategoryChildrenOptionsPanel.vue b/components/editor/category/EditorCategoryChildrenOptionsPanel.vue index 57dcef2..f4a73ff 100644 --- a/components/editor/category/EditorCategoryChildrenOptionsPanel.vue +++ b/components/editor/category/EditorCategoryChildrenOptionsPanel.vue @@ -18,8 +18,7 @@ const category = computed(() => { <div id="options"> <h2>Quests in this category</h2> <p>Drag to reorder.</p> - <div class="option-group"> - </div> + <div class="option-group"></div> </div> </EditorOptionsPanel> </template> diff --git a/components/editor/category/EditorCategoryOptionsPanel.vue b/components/editor/category/EditorCategoryOptionsPanel.vue index 112b063..c543f73 100644 --- a/components/editor/category/EditorCategoryOptionsPanel.vue +++ b/components/editor/category/EditorCategoryOptionsPanel.vue @@ -17,9 +17,12 @@ const category = computed(() => { <EditorOptionsPanel v-if="category"> <div id="options"> <div class="option-group"> - <Checkbox id="category-permissionrequired" label="Require permission for category" + <Checkbox + id="category-permissionrequired" + label="Require permission for category" description="Players must have permission to open and start quests in this category." - v-model="category.permissionRequired" /> + v-model="category.permissionRequired" + /> </div> </div> </EditorOptionsPanel> diff --git a/components/editor/quest/EditorQuestOptionsPanel.vue b/components/editor/quest/EditorQuestOptionsPanel.vue index 6c1c8b1..bc5f7ef 100644 --- a/components/editor/quest/EditorQuestOptionsPanel.vue +++ b/components/editor/quest/EditorQuestOptionsPanel.vue @@ -17,7 +17,6 @@ const knownCategories = computed(() => { const knownQuests = computed(() => { return sessionStore.session.quests.map((quest) => quest.id); }); - </script> <template> @@ -25,87 +24,126 @@ const knownQuests = computed(() => { <div id="options"> <div class="option-group"> <label for="quest-category">Category</label> - <multiselect id="quest-category" v-model="quest.options.category" :options="knownCategories" :searchable="true" - placeholder="No category"></multiselect> + <multiselect + id="quest-category" + v-model="quest.options.category" + :options="knownCategories" + :searchable="true" + placeholder="No category" + ></multiselect> </div> <div class="option-group"> <label for="quest-requirements">Requirements</label> - <multiselect id="quest-requirements" v-model="quest.options.requirements" :options="knownQuests" - :searchable="true" :taggable="true" :multiple="true" placeholder="Add requirement"></multiselect> + <multiselect + id="quest-requirements" + v-model="quest.options.requirements" + :options="knownQuests" + :searchable="true" + :taggable="true" + :multiple="true" + placeholder="Add requirement" + ></multiselect> <p class="description"> - This quest will only be available if the player has completed all of the quests listed above. + This quest will only be available if the player has completed all of the quests listed + above. </p> </div> <h2>Quest options</h2> <div class="option-group"> - <Checkbox id="quest-permissionrequired" label="Require permission to start quest" - description="Players must have permission to start the quest." v-model="quest.options.permissionRequired" /> + <Checkbox + id="quest-permissionrequired" + label="Require permission to start quest" + description="Players must have permission to start the quest." + v-model="quest.options.permissionRequired" + /> </div> <div class="option-group"> - <Checkbox id="quest-cancellable" label="Allow players to cancel quest" - description="Players can cancel the quest after they have started it." v-model="quest.options.cancellable" /> + <Checkbox + id="quest-cancellable" + label="Allow players to cancel quest" + description="Players can cancel the quest after they have started it." + v-model="quest.options.cancellable" + /> </div> <div class="option-group"> - <Checkbox id="quest-countstowardslimit" label="Count towards quest limit" + <Checkbox + id="quest-countstowardslimit" + label="Count towards quest limit" description="Quest will count towards the player's quest started limit." - v-model="quest.options.countsTowardsLimit" /> + v-model="quest.options.countsTowardsLimit" + /> </div> <div class="option-group"> - <Checkbox id="quest-repeatable" label="Allow players to repeat quest" + <Checkbox + id="quest-repeatable" + label="Allow players to repeat quest" description="Quest can be completed again after it has been completed once." - v-model="quest.options.repeatable" /> + v-model="quest.options.repeatable" + /> </div> <div class="option-group"> - <Checkbox id="quest-autostart" label="Automatically start quest" + <Checkbox + id="quest-autostart" + label="Automatically start quest" description="Quest will start automatically when the player has unlocked it." - v-model="quest.options.autostart" /> + v-model="quest.options.autostart" + /> </div> - <h2>Cooldown</h2> <div class="option-group"> - <Checkbox id="quest-cooldown" label="Enable cooldown" + <Checkbox + id="quest-cooldown" + label="Enable cooldown" description="Players will have to wait a certain amount of time before they can start the quest again." - v-model="quest.options.cooldown.enabled" /> + v-model="quest.options.cooldown.enabled" + /> </div> <div class="option-group"> - <label for="quest-cooldown-time"> - Cooldown (in seconds) - </label> - <input id="quest-cooldown-time" type="number" v-model="quest.options.cooldown.time" - :disabled="!quest.options.cooldown.enabled" /> + <label for="quest-cooldown-time"> Cooldown (in seconds) </label> + <input + id="quest-cooldown-time" + type="number" + v-model="quest.options.cooldown.time" + :disabled="!quest.options.cooldown.enabled" + /> <p class="description"> - Common values are: <code>3600</code> (1 hour), <code>86400</code> (24 hours), <code>604800</code> (7 days), - <code>2592000</code> (30 days) + Common values are: <code>3600</code> (1 hour), <code>86400</code> (24 hours), + <code>604800</code> (7 days), <code>2592000</code> (30 days) </p> </div> <h2>Time limit</h2> <div class="option-group"> - <Checkbox id="quest-timelimit" label="Enable time limit" + <Checkbox + id="quest-timelimit" + label="Enable time limit" description="Players will be required to complete the quest within a certain amount of time, otherwise it will be automatically cancelled." - v-model="quest.options.timeLimit.enabled" /> + v-model="quest.options.timeLimit.enabled" + /> </div> <div class="option-group"> - <label for="quest-timelimit-time"> - Time limit (in seconds) - </label> - <input id="quest-timelimit-time" type="number" v-model="quest.options.timeLimit.time" - :disabled="!quest.options.timeLimit.enabled" /> + <label for="quest-timelimit-time"> Time limit (in seconds) </label> + <input + id="quest-timelimit-time" + type="number" + v-model="quest.options.timeLimit.time" + :disabled="!quest.options.timeLimit.enabled" + /> <p class="description"> - Common values are: <code>3600</code> (1 hour), <code>86400</code> (24 hours), <code>604800</code> (7 days), - <code>2592000</code> (30 days) + Common values are: <code>3600</code> (1 hour), <code>86400</code> (24 hours), + <code>604800</code> (7 days), <code>2592000</code> (30 days) </p> </div> </div> diff --git a/components/editor/quest/EditorQuestTasksOptionsPanel.vue b/components/editor/quest/EditorQuestTasksOptionsPanel.vue index cb4b109..a9e3ac3 100644 --- a/components/editor/quest/EditorQuestTasksOptionsPanel.vue +++ b/components/editor/quest/EditorQuestTasksOptionsPanel.vue @@ -29,22 +29,40 @@ const addTask = (newId: string, newType: string) => { <template> <EditorOptionsPanel v-if="quest"> <div id="options"> - <h2>Tasks <code>({{ Object.keys(quest.tasks).length }})</code></h2> + <h2> + Tasks <code>({{ Object.keys(quest.tasks).length }})</code> + </h2> - <p v-if="Object.keys(quest.tasks).length === 0" class="error-text">This quest does not have any tasks.</p> - <EditorTaskConfiguration v-for="(task, taskId) in quest.tasks" :key="taskId" :taskId="String(taskId)" - :quest="quest" /> + <p v-if="Object.keys(quest.tasks).length === 0" class="error-text"> + This quest does not have any tasks. + </p> + <EditorTaskConfiguration + v-for="(task, taskId) in quest.tasks" + :key="taskId" + :taskId="String(taskId)" + :quest="quest" + /> <div id="controls"> - <Button id="add-task" :icon="['fas', 'plus']" type="solid" label="Add task" @click="showAddTaskModal = true" /> + <Button + id="add-task" + :icon="['fas', 'plus']" + type="solid" + label="Add task" + @click="showAddTaskModal = true" + /> </div> </div> </EditorOptionsPanel> - <EditorTaskModalCreate v-if="quest" v-model="showAddTaskModal" :questId="questId" @add="addTask" /> + <EditorTaskModalCreate + v-if="quest" + v-model="showAddTaskModal" + :questId="questId" + @add="addTask" + /> </template> - <style scoped> #options { display: flex; diff --git a/components/editor/quest/modal/EditorQuestModalDelete.vue b/components/editor/quest/modal/EditorQuestModalDelete.vue index e81bcc7..c4e1682 100644 --- a/components/editor/quest/modal/EditorQuestModalDelete.vue +++ b/components/editor/quest/modal/EditorQuestModalDelete.vue @@ -13,10 +13,18 @@ defineProps({ <template v-slot:header> <h2>Really delete quest '{{ questId }}'?</h2> </template> - <p>Are you sure you want to delete this quest? The quests editor does not have undo functionality (yet)! </p> + <p> + Are you sure you want to delete this quest? The quests editor does not have undo functionality + (yet)! + </p> <div id="confirm" class="control-group"> <Button :icon="['fas', 'times']" :label="'Cancel'" @click="model = false"></Button> - <Button type="solid" :icon="['fas', 'trash']" :label="'Delete'" @click="emit('delete')"></Button> + <Button + type="solid" + :icon="['fas', 'trash']" + :label="'Delete'" + @click="emit('delete')" + ></Button> </div> </Modal> </template> @@ -27,4 +35,4 @@ defineProps({ justify-content: flex-end; margin-top: 1rem; } -</style>
\ No newline at end of file +</style> diff --git a/components/editor/quest/modal/EditorQuestModalDuplicate.vue b/components/editor/quest/modal/EditorQuestModalDuplicate.vue index 73a2fd0..6c333fb 100644 --- a/components/editor/quest/modal/EditorQuestModalDuplicate.vue +++ b/components/editor/quest/modal/EditorQuestModalDuplicate.vue @@ -17,7 +17,6 @@ const newQuestId = ref(props.questId); const isDuplicate = computed(() => { return session.getQuestById(newQuestId.value!) !== undefined; }); - </script> <template> @@ -36,8 +35,13 @@ const isDuplicate = computed(() => { <p>A Quest ID must be unique, alphanumeric, and not contain any spaces.</p> <div id="confirm" class="control-group"> <Button :icon="['fas', 'times']" :label="'Cancel'" @click="model = false"></Button> - <Button type="solid" :icon="['fas', 'check']" :label="'Duplicate'" :disabled="isDuplicate" - @click="emit('duplicate', newQuestId)"></Button> + <Button + type="solid" + :icon="['fas', 'check']" + :label="'Duplicate'" + :disabled="isDuplicate" + @click="emit('duplicate', newQuestId)" + ></Button> </div> </div> </template> @@ -55,4 +59,4 @@ const isDuplicate = computed(() => { flex-direction: column; gap: 0.5rem; } -</style>
\ No newline at end of file +</style> diff --git a/components/editor/quest/modal/EditorQuestModalRename.vue b/components/editor/quest/modal/EditorQuestModalRename.vue index 3d846aa..13c4810 100644 --- a/components/editor/quest/modal/EditorQuestModalRename.vue +++ b/components/editor/quest/modal/EditorQuestModalRename.vue @@ -17,7 +17,6 @@ const newQuestId = ref(props.questId); const isDuplicate = computed(() => { return session.getQuestById(newQuestId.value!) !== undefined; }); - </script> <template> @@ -36,8 +35,13 @@ const isDuplicate = computed(() => { <p>A Quest ID must be unique, alphanumeric, and not contain any spaces.</p> <div id="confirm" class="control-group"> <Button :icon="['fas', 'times']" :label="'Cancel'" @click="model = false"></Button> - <Button type="solid" :icon="['fas', 'check']" :label="'Rename'" :disabled="isDuplicate" - @click="emit('update', newQuestId)"></Button> + <Button + type="solid" + :icon="['fas', 'check']" + :label="'Rename'" + :disabled="isDuplicate" + @click="emit('update', newQuestId)" + ></Button> </div> </div> </template> @@ -55,4 +59,4 @@ const isDuplicate = computed(() => { flex-direction: column; gap: 0.5rem; } -</style>
\ No newline at end of file +</style> diff --git a/components/editor/quest/modal/EditorQuestModalYaml.vue b/components/editor/quest/modal/EditorQuestModalYaml.vue index 365055c..c7c4936 100644 --- a/components/editor/quest/modal/EditorQuestModalYaml.vue +++ b/components/editor/quest/modal/EditorQuestModalYaml.vue @@ -6,7 +6,7 @@ const emit = defineEmits(['delete']); const props = defineProps({ questId: { required: true, - type: String + type: String, }, }); @@ -18,16 +18,16 @@ const yamlString = ref(''); const open = () => { const quest = session.getQuestById(props.questId); if (!quest) { - return + return; } const mappedObject = mapJsonQuestToYamlObject(quest); yamlString.value = stringify(mappedObject); showModal.value = true; -} +}; defineExpose({ - open -}) + open, +}); </script> <template> @@ -36,12 +36,19 @@ defineExpose({ <h2>YAML</h2> </template> - <p>YAML file for <code>{{ props.questId }}</code></p> + <p> + YAML file for <code>{{ props.questId }}</code> + </p> <textarea rows="20" :value="yamlString" readonly /> <div id="confirm" class="control-group"> - <Button type="solid" :icon="['fas', 'check']" :label="'Close'" @click="showModal = false"></Button> + <Button + type="solid" + :icon="['fas', 'check']" + :label="'Close'" + @click="showModal = false" + ></Button> </div> </Modal> </template> @@ -56,4 +63,4 @@ defineExpose({ textarea { width: 100%; } -</style>
\ No newline at end of file +</style> diff --git a/components/editor/task/EditorTaskConfiguration.vue b/components/editor/task/EditorTaskConfiguration.vue index 5313767..dab5888 100644 --- a/components/editor/task/EditorTaskConfiguration.vue +++ b/components/editor/task/EditorTaskConfiguration.vue @@ -13,14 +13,21 @@ const taskType = computed(() => props.quest.tasks[props.taskId].config.type); const taskDefintion = computed(() => sessionStore.getTaskDefinitionByTaskType(taskType.value)); const taskConfig = computed(() => { - return Object.keys(props.quest.tasks[props.taskId].config).filter((fieldName) => fieldName !== 'type').reduce((acc, fieldName) => { - acc[fieldName] = props.quest.tasks[props.taskId].config[fieldName]; - return acc; - }, {} as { [key: string]: any }); + return Object.keys(props.quest.tasks[props.taskId].config) + .filter((fieldName) => fieldName !== 'type') + .reduce( + (acc, fieldName) => { + acc[fieldName] = props.quest.tasks[props.taskId].config[fieldName]; + return acc; + }, + {} as { [key: string]: any } + ); }); const requiredFields = computed(() => { - return Object.keys(taskDefintion.value.configuration).filter((fieldName) => taskDefintion.value.configuration[fieldName].required); + return Object.keys(taskDefintion.value.configuration).filter( + (fieldName) => taskDefintion.value.configuration[fieldName].required + ); }); // const givenRequiredFields = computed(() => { @@ -32,13 +39,20 @@ const requiredFields = computed(() => { // }); const remainingGivenFields = computed(() => { - return Object.keys(taskConfig.value).filter((fieldName) => !requiredFields.value.includes(fieldName) && fieldName in taskDefintion.value.configuration); + return Object.keys(taskConfig.value).filter( + (fieldName) => + !requiredFields.value.includes(fieldName) && fieldName in taskDefintion.value.configuration + ); }); -const configKeysOptions = computed(() => Object.keys(taskDefintion.value.configuration).filter((key) => !Object.keys(taskConfig.value).some((fieldName) => fieldName === key))); +const configKeysOptions = computed(() => + Object.keys(taskDefintion.value.configuration).filter( + (key) => !Object.keys(taskConfig.value).some((fieldName) => fieldName === key) + ) +); // const configKeysOptions = computed(() => { // const keys = Object.keys(taskDefintion.value.configuration).filter((key) => !Object.keys(taskConfig.value).some((fieldName) => fieldName === key)); -// +// // return keys.map((key) => { // return { // value: key, @@ -49,7 +63,8 @@ const configKeysOptions = computed(() => Object.keys(taskDefintion.value.configu // }); const onAddOption = (option: any) => { - sessionStore.getQuestById(props.quest.id)!.tasks[props.taskId].config[option] = taskDefintion.value.configuration[option].default || null; + sessionStore.getQuestById(props.quest.id)!.tasks[props.taskId].config[option] = + taskDefintion.value.configuration[option].default || null; }; const updateValue = (fieldName: string, value: any) => { @@ -64,14 +79,14 @@ const showChangeModal = ref(false); const updateTaskType = (newType: string) => { sessionStore.getQuestById(props.quest.id)!.tasks[props.taskId].config = { - type: newType + type: newType, }; showChangeModal.value = false; -} +}; const deleteTaskType = (taskId: string) => { delete sessionStore.getQuestById(props.quest.id)!.tasks[taskId]; -} +}; </script> <template> @@ -82,43 +97,68 @@ const deleteTaskType = (taskId: string) => { {{ props.taskId }} </span> <code> - (<font-awesome-icon v-if="taskDefintion" id="task-icon" :icon="[taskDefintion.icon.style, taskDefintion.icon.name]" />{{ taskType }}) + (<font-awesome-icon + v-if="taskDefintion" + id="task-icon" + :icon="[taskDefintion.icon.style, taskDefintion.icon.name]" + />{{ taskType }}) </code> </p> <div id="task-controls" class="control-group"> <Button :icon="['fas', 'pen']" :label="'Change'" @click="showChangeModal = true"></Button> - <Button :icon="['fas', 'trash']" :label="'Delete'" @click="deleteTaskType(props.taskId)"></Button> + <Button + :icon="['fas', 'trash']" + :label="'Delete'" + @click="deleteTaskType(props.taskId)" + ></Button> </div> </div> <div id="task-configuration"> <div v-if="!taskDefintion" class="error"> <font-awesome-icon id="error-icon" :icon="['fas', 'triangle-exclamation']" /> <p id="error-message"> - Unable to edit task <code>{{ props.taskId }}</code>. + Unable to edit task <code>{{ props.taskId }}</code + >. </p> <p id="error-description"> - The quests web editor does not know how to configure task - type <code>{{ taskType }}</code> as it has no task definition. + The quests web editor does not know how to configure task type + <code>{{ taskType }}</code> as it has no task definition. </p> </div> <div v-if="taskDefintion"> - <EditorTaskConfigurationRow v-for="fieldName in [...requiredFields, ...remainingGivenFields]" - :key="`${quest.id}-${props.taskId}-${taskType}-${fieldName}`" :required="requiredFields.includes(fieldName)" - :configKey="fieldName" :initialValue="taskConfig[fieldName]" :taskType="taskType" - :type="(taskDefintion.configuration[fieldName].type as string)" - @update="(newValue: any) => updateValue(fieldName, newValue)" @delete="() => deleteValue(fieldName)" /> + <EditorTaskConfigurationRow + v-for="fieldName in [...requiredFields, ...remainingGivenFields]" + :key="`${quest.id}-${props.taskId}-${taskType}-${fieldName}`" + :required="requiredFields.includes(fieldName)" + :configKey="fieldName" + :initialValue="taskConfig[fieldName]" + :taskType="taskType" + :type="taskDefintion.configuration[fieldName].type as string" + @update="(newValue: any) => updateValue(fieldName, newValue)" + @delete="() => deleteValue(fieldName)" + /> <div id="add-option"> - <multiselect class="configuration-multiselect" :options="configKeysOptions" :searchable="true" - @select="onAddOption" placeholder="Add option..."> + <multiselect + class="configuration-multiselect" + :options="configKeysOptions" + :searchable="true" + @select="onAddOption" + placeholder="Add option..." + > </multiselect> </div> </div> </div> </div> - <EditorTaskModalChange v-model="showChangeModal" :taskId="props.taskId" :currentTaskType="taskType" - :key="`change-task-${props.taskId}`" @update="updateTaskType" /> + <EditorTaskModalChange + v-model="showChangeModal" + :taskId="props.taskId" + :currentTaskType="taskType" + :key="`change-task-${props.taskId}`" + @update="updateTaskType" + /> </template> <style scoped> @@ -133,7 +173,6 @@ const deleteTaskType = (taskId: string) => { #error-message { font-weight: 700; } - } #task-configuration-table { diff --git a/components/editor/task/EditorTaskConfigurationRow.vue b/components/editor/task/EditorTaskConfigurationRow.vue index c00896f..f400292 100644 --- a/components/editor/task/EditorTaskConfigurationRow.vue +++ b/components/editor/task/EditorTaskConfigurationRow.vue @@ -21,27 +21,37 @@ const emit = defineEmits(['update', 'delete']); const sessionStore = useSessionStore(); const definition = computed(() => { - const def = sessionStore.getTaskDefinitionByTaskType(props.taskType).configuration[props.configKey]; + const def = sessionStore.getTaskDefinitionByTaskType(props.taskType).configuration[ + props.configKey + ]; return { description: def.description, note: def.note }; }); const { description, note } = toRefs(definition.value); const showDescription = ref(false); -const currentValue = ref(props.initialValue || - (props.type === 'boolean' - ? false - : (props.type === 'material-list' || props.type === 'string-list' - ? [] - : props.type === 'itemstack' - ? null - : '' - ))); +const currentValue = ref( + props.initialValue || + (props.type === 'boolean' + ? false + : props.type === 'material-list' || props.type === 'string-list' + ? [] + : props.type === 'itemstack' + ? null + : '') +); if (props.initialValue !== currentValue.value) { emit('update', currentValue.value); } -const error = computed(() => currentValue.value === undefined || currentValue.value === null || currentValue.value === '' || (Array.isArray(currentValue.value) && currentValue.value.length === 0) || (typeof currentValue.value === 'object' && Object.keys(currentValue.value).length === 0)); +const error = computed( + () => + currentValue.value === undefined || + currentValue.value === null || + currentValue.value === '' || + (Array.isArray(currentValue.value) && currentValue.value.length === 0) || + (typeof currentValue.value === 'object' && Object.keys(currentValue.value).length === 0) +); const updateValue = (value: any) => { currentValue.value = value; }; @@ -53,7 +63,6 @@ watch(currentValue, () => { const addValue = (searchQuery: any) => { currentValue.value.push(searchQuery); }; - </script> <template> @@ -73,23 +82,49 @@ const addValue = (searchQuery: any) => { <input v-else-if="props.type === 'number'" type="number" v-model="currentValue" /> <!-- Data type 'boolean' --> - <TrueFalseSwitch v-else-if="props.type === 'boolean'" :value="!!currentValue" @update="updateValue" /> + <TrueFalseSwitch + v-else-if="props.type === 'boolean'" + :value="!!currentValue" + @update="updateValue" + /> <!-- Data type 'material-list' --> - <multiselect v-else-if="props.type === 'material-list'" v-model="currentValue" class="configuration-multiselect" - :options="materials" :multiple="true" :taggable="true" :searchable="true" placeholder="Enter material name" /> + <multiselect + v-else-if="props.type === 'material-list'" + v-model="currentValue" + class="configuration-multiselect" + :options="materials" + :multiple="true" + :taggable="true" + :searchable="true" + placeholder="Enter material name" + /> <!-- Data type 'string-list' --> - <multiselect v-else-if="props.type === 'string-list'" v-model="currentValue" class="configuration-multiselect" - :options="[]" @tag="addValue" :multiple="true" :taggable="true" :searchable="true" - placeholder="Enter string" /> + <multiselect + v-else-if="props.type === 'string-list'" + v-model="currentValue" + class="configuration-multiselect" + :options="[]" + @tag="addValue" + :multiple="true" + :taggable="true" + :searchable="true" + placeholder="Enter string" + /> <!-- Data type 'itemstack' --> - <ItemStackPicker v-else-if="props.type === 'itemstack'" :value="currentValue" @update="updateValue" /> + <ItemStackPicker + v-else-if="props.type === 'itemstack'" + :value="currentValue" + @update="updateValue" + /> <div v-if="showDescription || error" id="task-configuration-row-info"> <p v-if="error" class="error">A value is required.</p> - <p>{{ description }} <i>{{ note }}</i></p> + <p> + {{ description }} <i>{{ note }}</i> + </p> </div> </div> </div> @@ -182,4 +217,4 @@ input { .error { color: var(--color-false); } -</style>
\ No newline at end of file +</style> diff --git a/components/editor/task/modal/EditorTaskModalChange.vue b/components/editor/task/modal/EditorTaskModalChange.vue index 8ab5dbc..a6b05b8 100644 --- a/components/editor/task/modal/EditorTaskModalChange.vue +++ b/components/editor/task/modal/EditorTaskModalChange.vue @@ -18,7 +18,9 @@ const knownTaskTypes = computed(() => session.getKnownTaskTypes()); const newType = ref(''); const unknownTaskType = computed(() => !knownTaskTypes.value.includes(newType.value)); const noChange = computed(() => newType.value === props.currentTaskType); -const newTypeDescription = computed(() => session.getTaskDefinitionByTaskType(newType.value)?.description); +const newTypeDescription = computed( + () => session.getTaskDefinitionByTaskType(newType.value)?.description +); </script> <template> @@ -31,16 +33,26 @@ const newTypeDescription = computed(() => session.getTaskDefinitionByTaskType(ne <div id="body"> <div class="option-group"> <label for="new-type">New type</label> - <multiselect id="new-type" v-model="newType" :options="knownTaskTypes" :searchable="true" - placeholder="Select a new type"></multiselect> + <multiselect + id="new-type" + v-model="newType" + :options="knownTaskTypes" + :searchable="true" + placeholder="Select a new type" + ></multiselect> </div> <p v-if="unknownTaskType" class="error-text">Invalid task type.</p> <p v-if="newTypeDescription">{{ newTypeDescription }}</p> <p>Any configured options for this task will be overwritten.</p> <div id="confirm" class="control-group"> <Button :icon="['fas', 'times']" :label="'Cancel'" @click="model = false"></Button> - <Button type="solid" :icon="['fas', 'check']" :label="'Change'" :disabled="unknownTaskType || noChange" - @click="emit('update', newType)"></Button> + <Button + type="solid" + :icon="['fas', 'check']" + :label="'Change'" + :disabled="unknownTaskType || noChange" + @click="emit('update', newType)" + ></Button> </div> </div> </template> @@ -58,4 +70,4 @@ const newTypeDescription = computed(() => session.getTaskDefinitionByTaskType(ne flex-direction: column; gap: 0.5rem; } -</style>
\ No newline at end of file +</style> diff --git a/components/editor/task/modal/EditorTaskModalCreate.vue b/components/editor/task/modal/EditorTaskModalCreate.vue index e5b2d7a..cdd955f 100644 --- a/components/editor/task/modal/EditorTaskModalCreate.vue +++ b/components/editor/task/modal/EditorTaskModalCreate.vue @@ -24,7 +24,9 @@ const unknownTaskType = computed(() => !knownTaskTypes.value.includes(newType.va const invalidTaskId = computed(() => !validateTaskId(newId.value)); const duplicateTaskId = computed(() => knownTasks.value[newId.value] !== undefined); -const newTypeDescription = computed(() => session.getTaskDefinitionByTaskType(newType.value)?.description); +const newTypeDescription = computed( + () => session.getTaskDefinitionByTaskType(newType.value)?.description +); </script> <template> @@ -43,19 +45,26 @@ const newTypeDescription = computed(() => session.getTaskDefinitionByTaskType(ne </div> <div class="option-group"> <label for="new-type">Task type</label> - <multiselect id="new-type" v-model="newType" :options="knownTaskTypes" :searchable="true" - placeholder="Select a new type"></multiselect> + <multiselect + id="new-type" + v-model="newType" + :options="knownTaskTypes" + :searchable="true" + placeholder="Select a new type" + ></multiselect> <p v-if="unknownTaskType" class="error-text">Invalid task type.</p> </div> <p v-if="newTypeDescription">{{ newTypeDescription }}</p> <p>A task ID must be unique, alphanumeric, and not contain any spaces.</p> <div id="confirm" class="control-group"> <Button :icon="['fas', 'times']" :label="'Cancel'" @click="model = false"></Button> - <Button type="solid" :icon="['fas', 'check']" :label="'Confirm'" + <Button + type="solid" + :icon="['fas', 'check']" + :label="'Confirm'" :disabled="unknownTaskType || invalidTaskId || duplicateTaskId" - @click="emit('add', newId, newType)"></Button> - - + @click="emit('add', newId, newType)" + ></Button> </div> </div> </template> @@ -73,4 +82,4 @@ const newTypeDescription = computed(() => session.getTaskDefinitionByTaskType(ne flex-direction: column; gap: 0.5rem; } -</style>
\ No newline at end of file +</style> diff --git a/components/export/ExportButton.vue b/components/export/ExportButton.vue index 51c4b0a..a3274c1 100644 --- a/components/export/ExportButton.vue +++ b/components/export/ExportButton.vue @@ -5,11 +5,16 @@ const exportModal = ref<InstanceType<typeof ExportModal> | null>(null); const openExportModal = async () => { exportModal.value?.open(); -} +}; </script> <template> - <Button type="solid" :icon="['fas', 'file-export']" label="Save / Export" @click="openExportModal" /> + <Button + type="solid" + :icon="['fas', 'file-export']" + label="Save / Export" + @click="openExportModal" + /> <ExportModal ref="exportModal" /> -</template>
\ No newline at end of file +</template> diff --git a/components/export/ExportModal.vue b/components/export/ExportModal.vue index 1d09914..7adb603 100644 --- a/components/export/ExportModal.vue +++ b/components/export/ExportModal.vue @@ -5,14 +5,14 @@ const showModal = ref(false); const open = () => { showModal.value = true; -} +}; const { canUseFsApi } = getBrowserCapabilities(); const isUsingFsMode = computed(() => session.getSessionType() === 'filesystem'); defineExpose({ - open -}) + open, +}); </script> <template> @@ -58,7 +58,9 @@ defineExpose({ <div id="description"> <p id="subtitle">Send to Server</p> - <p>Upload your quest configuration to the server, which can be downloaded and automatically applied in-game. + <p> + Upload your quest configuration to the server, which can be downloaded and + automatically applied in-game. </p> <p class="error"> <font-awesome-icon :icon="['fas', 'xmark']" /> @@ -178,4 +180,4 @@ hr { justify-content: flex-end; margin-top: 1rem; } -</style>
\ No newline at end of file +</style> diff --git a/components/export/ExportZipButton.vue b/components/export/ExportZipButton.vue index 8fb6f60..65ab083 100644 --- a/components/export/ExportZipButton.vue +++ b/components/export/ExportZipButton.vue @@ -6,7 +6,7 @@ const exportZipModal = ref<InstanceType<typeof ExportZipModal> | null>(null); const startZipExport = async () => { exportZipModal.value?.open(); exportZipModal.value?.startExport(); -} +}; </script> <template> @@ -15,4 +15,4 @@ const startZipExport = async () => { <ExportZipModal ref="exportZipModal" /> </ClientOnly> -</template>
\ No newline at end of file +</template> diff --git a/components/export/ExportZipModal.vue b/components/export/ExportZipModal.vue index b84690d..d7d7ab2 100644 --- a/components/export/ExportZipModal.vue +++ b/components/export/ExportZipModal.vue @@ -15,7 +15,11 @@ const startExport = async () => { exportStore.setZipStatus('preparing'); - const { transformedQuests, transformedCategories, transformedItems } = await prepareZip(quests, categories, items); + const { transformedQuests, transformedCategories, transformedItems } = await prepareZip( + quests, + categories, + items + ); exportStore.setZipStatus('compressing'); @@ -25,20 +29,20 @@ const startExport = async () => { exportStore.setZipContents(blob); exportStore.setZipStatus('ready'); - saveAs(blob, "quests.zip"); + saveAs(blob, 'quests.zip'); } catch { exportStore.setZipStatus('failed'); } -} +}; const open = () => { showModal.value = true; -} +}; defineExpose({ open, - startExport -}) + startExport, +}); </script> <template> @@ -64,4 +68,4 @@ defineExpose({ justify-content: flex-end; margin-top: 1rem; } -</style>
\ No newline at end of file +</style> diff --git a/components/footer/SiteFooter.vue b/components/footer/SiteFooter.vue index 2f31e4f..632131f 100644 --- a/components/footer/SiteFooter.vue +++ b/components/footer/SiteFooter.vue @@ -5,19 +5,17 @@ const runtimeConfig = useRuntimeConfig(); <template> <footer id="footer"> - <p id="copyright-license"> - Released under the GPL-3.0 License. - </p> + <p id="copyright-license">Released under the GPL-3.0 License.</p> <p id="git-commit-hash"> <font-awesome-icon :icon="['fas', 'code-branch']" /> <NuxtLink to="https://github.com/LMBishop/quests-web-editor/">quests-web-editor</NuxtLink> <span>@</span> - <NuxtLink :to="`https://github.com/LMBishop/quests-web-editor/commit/${runtimeConfig.public.gitCommitHash}`"> + <NuxtLink + :to="`https://github.com/LMBishop/quests-web-editor/commit/${runtimeConfig.public.gitCommitHash}`" + > {{ runtimeConfig.public.gitCommitHashShort }} </NuxtLink> - <span> - ({{ runtimeConfig.public.gitBranch }}) - </span> + <span> ({{ runtimeConfig.public.gitBranch }}) </span> </p> </footer> </template> @@ -49,4 +47,4 @@ const runtimeConfig = useRuntimeConfig(); text-decoration: none; } } -</style>
\ No newline at end of file +</style> diff --git a/components/header/PageHeader.vue b/components/header/PageHeader.vue index 3c616ca..7e024c6 100644 --- a/components/header/PageHeader.vue +++ b/components/header/PageHeader.vue @@ -53,4 +53,4 @@ font-size: 1.2rem; color: var(--color-text-mute); } -</style>
\ No newline at end of file +</style> diff --git a/components/header/SiteHeader.vue b/components/header/SiteHeader.vue index 22ee209..c1b2aa9 100644 --- a/components/header/SiteHeader.vue +++ b/components/header/SiteHeader.vue @@ -3,7 +3,7 @@ const session = useSessionStore(); const navigateHome = () => { navigateTo('/'); -} +}; const sessionType = computed(() => session.getSessionType()); </script> @@ -53,7 +53,6 @@ const sessionType = computed(() => session.getSessionType()); font-size: 0.8rem; color: var(--color-header-text-mute); } - } #logo { @@ -82,4 +81,4 @@ header { display: flex; justify-content: space-between; } -</style>
\ No newline at end of file +</style> diff --git a/components/loader/LoaderDiscardSessionModal.vue b/components/loader/LoaderDiscardSessionModal.vue index 845b10a..6f5ed02 100644 --- a/components/loader/LoaderDiscardSessionModal.vue +++ b/components/loader/LoaderDiscardSessionModal.vue @@ -5,7 +5,7 @@ const showModal = ref(false); const open = () => { showModal.value = true; -} +}; const confirm = () => { session.setQuests([]); @@ -16,11 +16,11 @@ const confirm = () => { navigateToEditorPane(null); showModal.value = false; -} +}; defineExpose({ - open -}) + open, +}); </script> <template> @@ -29,12 +29,20 @@ defineExpose({ <h2>Discard current session</h2> </template> - <p>You are about to discard your current session. All changes will be lost. - Do you want to continue?</p> + <p> + You are about to discard your current session. All changes will be lost. Do you want to + continue? + </p> <div id="controls" class="control-group"> <Button :icon="['fas', 'xmark']" :label="'Cancel'" @click="showModal = false"></Button> - <Button type="solid" accent="danger" :icon="['fas', 'trash']" :label="'Confirm'" @click="confirm"></Button> + <Button + type="solid" + accent="danger" + :icon="['fas', 'trash']" + :label="'Confirm'" + @click="confirm" + ></Button> </div> </Modal> </template> @@ -45,4 +53,4 @@ defineExpose({ justify-content: flex-end; margin-top: 1rem; } -</style>
\ No newline at end of file +</style> diff --git a/components/loader/LoaderFileSystemButton.vue b/components/loader/LoaderFileSystemButton.vue index 3a70860..58c470c 100644 --- a/components/loader/LoaderFileSystemButton.vue +++ b/components/loader/LoaderFileSystemButton.vue @@ -27,14 +27,19 @@ const openFileSystemPrompt = async () => { console.error(e); loaderStore.setFileSystemLoaderStatus('invalid'); } -} +}; </script> <template> <ClientOnly> - <Button type="solid" :icon="['fas', 'folder-open']" label="Load" @click="openFileSystemPrompt" - :disabled="!canUseFsApi" /> + <Button + type="solid" + :icon="['fas', 'folder-open']" + label="Load" + @click="openFileSystemPrompt" + :disabled="!canUseFsApi" + /> <LoaderFileSystemModal ref="fileSystemModal" /> </ClientOnly> -</template>
\ No newline at end of file +</template> diff --git a/components/loader/LoaderFileSystemModal.vue b/components/loader/LoaderFileSystemModal.vue index 25fcfa2..a3f133b 100644 --- a/components/loader/LoaderFileSystemModal.vue +++ b/components/loader/LoaderFileSystemModal.vue @@ -6,7 +6,7 @@ const showModal = ref(false); const open = () => { showModal.value = true; -} +}; const confirm = () => { const categories = loader.getCategories(); @@ -21,7 +21,7 @@ const confirm = () => { navigateToEditorPane(null); showModal.value = false; -} +}; const status = computed(() => loader.getFileSystemLoaderStatus()); const questsCount = computed(() => loader.getQuests().length); @@ -30,8 +30,8 @@ const itemsCount = computed(() => loader.getItems().length); const path = computed(() => loader.getPath()); defineExpose({ - open -}) + open, +}); </script> <template> @@ -50,7 +50,10 @@ defineExpose({ </div> <div v-if="status === 'loaded'"> - <p>Parsing files in directory <code>{{ path }}</code>...</p> + <p> + Parsing files in directory <code>{{ path }}</code + >... + </p> </div> <div v-if="status === 'invalid'"> @@ -58,7 +61,10 @@ defineExpose({ </div> <div v-if="status === 'valid'"> - <p>Successfully parsed directory <code>{{ path }}</code>.</p> + <p> + Successfully parsed directory <code>{{ path }}</code + >. + </p> <ul> <li>{{ categoriesCount }} categories loaded</li> <li>{{ questsCount }} quests loaded</li> @@ -69,8 +75,13 @@ defineExpose({ <div id="controls" class="control-group"> <Button :icon="['fas', 'xmark']" :label="'Cancel'" @click="showModal = false"></Button> - <Button v-if="status === 'valid'" type="solid" :icon="['fas', 'check']" :label="'Confirm'" - @click="confirm"></Button> + <Button + v-if="status === 'valid'" + type="solid" + :icon="['fas', 'check']" + :label="'Confirm'" + @click="confirm" + ></Button> </div> </Modal> </template> @@ -81,4 +92,4 @@ defineExpose({ justify-content: flex-end; margin-top: 1rem; } -</style>
\ No newline at end of file +</style> diff --git a/components/loader/LoaderImportButton.vue b/components/loader/LoaderImportButton.vue index ff6d0ad..d0c4107 100644 --- a/components/loader/LoaderImportButton.vue +++ b/components/loader/LoaderImportButton.vue @@ -1,14 +1,18 @@ <script setup lang="ts"> defineProps({ - isPrimaryAction: Boolean -}) + isPrimaryAction: Boolean, +}); const navigateToImport = async () => { navigateTo('/import'); -} +}; </script> <template> - <Button :type="isPrimaryAction ? 'solid' : 'text'" :icon="['fas', 'file-import']" label="Import" - @click="navigateToImport" /> -</template>
\ No newline at end of file + <Button + :type="isPrimaryAction ? 'solid' : 'text'" + :icon="['fas', 'file-import']" + label="Import" + @click="navigateToImport" + /> +</template> diff --git a/components/loader/LoaderNetworkButton.vue b/components/loader/LoaderNetworkButton.vue index 3364597..8cce5dc 100644 --- a/components/loader/LoaderNetworkButton.vue +++ b/components/loader/LoaderNetworkButton.vue @@ -1,8 +1,7 @@ -<script setup lang="ts"> -</script> +<script setup lang="ts"></script> <template> <ClientOnly> <Button type="solid" :icon="['fas', 'download']" label="Download" :disabled="true" /> </ClientOnly> -</template>
\ No newline at end of file +</template> diff --git a/components/loader/LoaderTestDataButton.vue b/components/loader/LoaderTestDataButton.vue index 1e648d6..8ef8b3c 100644 --- a/components/loader/LoaderTestDataButton.vue +++ b/components/loader/LoaderTestDataButton.vue @@ -5,7 +5,7 @@ const testDataModal = ref<InstanceType<typeof LoaderTestDataModal> | null>(null) const openTestDataModal = async () => { testDataModal.value?.open(); -} +}; </script> <template> @@ -14,4 +14,4 @@ const openTestDataModal = async () => { <LoaderTestDataModal ref="testDataModal" /> </ClientOnly> -</template>
\ No newline at end of file +</template> diff --git a/components/loader/LoaderTestDataModal.vue b/components/loader/LoaderTestDataModal.vue index 8b0df21..582e224 100644 --- a/components/loader/LoaderTestDataModal.vue +++ b/components/loader/LoaderTestDataModal.vue @@ -8,7 +8,7 @@ const showModal = ref(false); const open = () => { showModal.value = true; -} +}; const confirm = () => { const quests = loadQuestsFromJson(testData.quests); @@ -23,11 +23,11 @@ const confirm = () => { navigateToEditorPane(null); showModal.value = false; -} +}; defineExpose({ - open -}) + open, +}); </script> <template> @@ -36,8 +36,10 @@ defineExpose({ <h2>Import test data</h2> </template> - <p>You can view a demo of the Quests editor by loading test data. This will replace your current workspace. - Do you want to continue?</p> + <p> + You can view a demo of the Quests editor by loading test data. This will replace your current + workspace. Do you want to continue? + </p> <div id="controls" class="control-group"> <Button :icon="['fas', 'xmark']" :label="'Cancel'" @click="showModal = false"></Button> @@ -52,4 +54,4 @@ defineExpose({ justify-content: flex-end; margin-top: 1rem; } -</style>
\ No newline at end of file +</style> diff --git a/layouts/default.vue b/layouts/default.vue index fef2da1..01e8484 100644 --- a/layouts/default.vue +++ b/layouts/default.vue @@ -1,5 +1,4 @@ -<script setup lang="ts"> -</script> +<script setup lang="ts"></script> <template> <SiteHeader /> @@ -23,4 +22,4 @@ #content { width: 100%; } -</style>
\ No newline at end of file +</style> diff --git a/layouts/editor.vue b/layouts/editor.vue index dbb53fa..a3dbf35 100644 --- a/layouts/editor.vue +++ b/layouts/editor.vue @@ -35,4 +35,4 @@ sessionStore.setTaskTypeAliases(taskDefinitions.aliases); #editor-pane { width: 100%; } -</style>
\ No newline at end of file +</style> diff --git a/lib/enchantments.ts b/lib/enchantments.ts index a770feb..9ee1994 100644 --- a/lib/enchantments.ts +++ b/lib/enchantments.ts @@ -1,3 +1,3 @@ import enchantments from '@/data/enchantments.json'; -export default enchantments;
\ No newline at end of file +export default enchantments; diff --git a/lib/itemflags.ts b/lib/itemflags.ts index a7ce433..d3b196d 100644 --- a/lib/itemflags.ts +++ b/lib/itemflags.ts @@ -1,3 +1,3 @@ import itemflags from '@/data/itemflags.json'; -export default itemflags;
\ No newline at end of file +export default itemflags; diff --git a/lib/materials.ts b/lib/materials.ts index 36c5aee..85f1094 100644 --- a/lib/materials.ts +++ b/lib/materials.ts @@ -1,3 +1,3 @@ import materials from '@/data/materials.json'; -export default materials;
\ No newline at end of file +export default materials; diff --git a/lib/questsLoader.ts b/lib/questsLoader.ts index f49dce5..28faeb7 100644 --- a/lib/questsLoader.ts +++ b/lib/questsLoader.ts @@ -14,12 +14,17 @@ export function loadQuestsFromJson(config: any): EditorQuest[] { }, type: quest.display.type, }, - tasks: Object.fromEntries(Object.keys(quest.tasks).map((taskId: string) => { - return [taskId, { - id: taskId, - config: quest.tasks[taskId], - } as EditorTask]; - })), + tasks: Object.fromEntries( + Object.keys(quest.tasks).map((taskId: string) => { + return [ + taskId, + { + id: taskId, + config: quest.tasks[taskId], + } as EditorTask, + ]; + }) + ), rewards: quest.rewards, ...(quest.startcommands && { startCommands: quest.startcommands }), ...(quest.startstring && { startString: quest.startstring }), @@ -42,9 +47,15 @@ export function loadQuestsFromJson(config: any): EditorQuest[] { }, sortOrder: quest.options['sort-order'] || 0, autostart: quest.options.autostart || false, - ...(quest.options['completed-display'] && { completedDisplay: quest.options['completed-display'] }), - ...(quest.options['cooldown-display'] && { cooldownDisplay: quest.options['cooldown-display'] }), - ...(quest.options['permission-display'] && { permissionDisplay: quest.options['permission-display'] }), + ...(quest.options['completed-display'] && { + completedDisplay: quest.options['completed-display'], + }), + ...(quest.options['cooldown-display'] && { + cooldownDisplay: quest.options['cooldown-display'], + }), + ...(quest.options['permission-display'] && { + permissionDisplay: quest.options['permission-display'], + }), ...(quest.options['locked-display'] && { lockedDisplay: quest.options['locked-display'] }), }, } as EditorQuest; @@ -82,14 +93,16 @@ export function loadItemsFromJson(config: any): EditorItem[] { //TODO don't write fields if they're unchanged export function mapJsonQuestToYamlObject(quest: EditorQuest): any { return { - tasks: Object.fromEntries(Object.keys(quest.tasks).map((taskId: string) => { - return [taskId, quest.tasks[taskId].config] - })), + tasks: Object.fromEntries( + Object.keys(quest.tasks).map((taskId: string) => { + return [taskId, quest.tasks[taskId].config]; + }) + ), display: { name: quest.display.name, - "lore-normal": quest.display.lore.normal, - "lore-started": quest.display.lore.started, - type: quest.display.type + 'lore-normal': quest.display.lore.normal, + 'lore-started': quest.display.lore.started, + type: quest.display.type, }, rewards: quest.rewards, ...(quest.startCommands && { startcommands: quest.startCommands }), @@ -99,26 +112,28 @@ export function mapJsonQuestToYamlObject(quest: EditorQuest): any { options: { category: quest.options.category, requires: quest.options.requirements, - "permission-required": quest.options.permissionRequired, + 'permission-required': quest.options.permissionRequired, cancellable: quest.options.cancellable, - "counts-towards-limit": quest.options.countsTowardsLimit, + 'counts-towards-limit': quest.options.countsTowardsLimit, repeatable: quest.options.repeatable, cooldown: { enabled: quest.options.cooldown.enabled, time: quest.options.cooldown.time, }, - "time-limit": { + 'time-limit': { enabled: quest.options.timeLimit.enabled, time: quest.options.timeLimit.time, }, - "sort-order": quest.options.sortOrder, + 'sort-order': quest.options.sortOrder, autostart: quest.options.autostart || false, ...(quest.options.completedDisplay && { completedDisplay: quest.options.completedDisplay }), ...(quest.options.cooldownDisplay && { cooldownDisplay: quest.options.cooldownDisplay }), - ...(quest.options.permissionDisplay && { permissionDisplay: quest.options.permissionDisplay }), + ...(quest.options.permissionDisplay && { + permissionDisplay: quest.options.permissionDisplay, + }), ...(quest.options.lockedDisplay && { lockedDisplay: quest.options.lockedDisplay }), }, - } + }; } export function mapJsonCategoryToYamlObject(category: EditorCategory): any { @@ -128,13 +143,13 @@ export function mapJsonCategoryToYamlObject(category: EditorCategory): any { type: category.display.type, lore: category.display.lore, }, - "permission-required": category.permissionRequired, - } + 'permission-required': category.permissionRequired, + }; } export function mapJsonItemToYamlObject(item: EditorItem): any { return { type: item.type, - item: item.config - } -}
\ No newline at end of file + item: item.config, + }; +} diff --git a/middleware/editor.global.ts b/middleware/editor.global.ts index 82df54b..5d72273 100644 --- a/middleware/editor.global.ts +++ b/middleware/editor.global.ts @@ -1,4 +1,4 @@ -import { useSessionStore } from "@/stores/session"; +import { useSessionStore } from '@/stores/session'; export default defineNuxtRouteMiddleware((to, from) => { const session = useSessionStore(); diff --git a/nuxt.config.ts b/nuxt.config.ts index f748dc2..93a04fd 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -9,24 +9,22 @@ export default defineNuxtConfig({ modules: [ // ... '@pinia/nuxt', - 'nuxt-prepare' - ], - css: [ - '@fortawesome/fontawesome-svg-core/styles.css' + 'nuxt-prepare', ], + css: ['@fortawesome/fontawesome-svg-core/styles.css'], build: { transpile: [ - "@fortawesome/vue-fontawesome", - "@fortawesome/fontawesome-svg-core", - "@fortawesome/free-solid-svg-icons", - "@fortawesome/free-regular-svg-icons", + '@fortawesome/vue-fontawesome', + '@fortawesome/fontawesome-svg-core', + '@fortawesome/free-solid-svg-icons', + '@fortawesome/free-regular-svg-icons', ], }, runtimeConfig: { public: { gitCommitHash: process.env.GIT_COMMIT_HASH, gitCommitHashShort: process.env.GIT_COMMIT_HASH_SHORT, - gitBranch: process.env.GIT_BRANCH - } - } -}) + gitBranch: process.env.GIT_BRANCH, + }, + }, +}); diff --git a/pages/editor/category/[id].vue b/pages/editor/category/[id].vue index cf79f61..5558c53 100644 --- a/pages/editor/category/[id].vue +++ b/pages/editor/category/[id].vue @@ -2,8 +2,8 @@ import { useSessionStore } from '@/stores/session'; definePageMeta({ - layout: 'editor' -}) + layout: 'editor', +}); const sessionStore = useSessionStore(); const route = useRoute(); @@ -44,4 +44,4 @@ const categoryName = sessionStore.getCategoryById(categoryId)?.display.name; header { border-bottom: 1px solid var(--color-border); } -</style>
\ No newline at end of file +</style> diff --git a/pages/editor/config.vue b/pages/editor/config.vue index 4dfc679..515cac9 100644 --- a/pages/editor/config.vue +++ b/pages/editor/config.vue @@ -1,7 +1,7 @@ <script setup lang="ts"> definePageMeta({ - layout: 'editor' -}) + layout: 'editor', +}); </script> <template> @@ -14,4 +14,4 @@ definePageMeta({ #title { text-align: center; } -</style>
\ No newline at end of file +</style> diff --git a/pages/editor/index.vue b/pages/editor/index.vue index 4e9815a..55c75f3 100644 --- a/pages/editor/index.vue +++ b/pages/editor/index.vue @@ -1,7 +1,7 @@ <script setup lang="ts"> definePageMeta({ - layout: 'editor' -}) + layout: 'editor', +}); </script> <template> @@ -22,4 +22,4 @@ definePageMeta({ font-size: 1.5rem; } } -</style>
\ No newline at end of file +</style> diff --git a/pages/editor/item/[id].vue b/pages/editor/item/[id].vue index 451a76d..d6656ab 100644 --- a/pages/editor/item/[id].vue +++ b/pages/editor/item/[id].vue @@ -2,8 +2,8 @@ import { useSessionStore } from '@/stores/session'; definePageMeta({ - layout: 'editor' -}) + layout: 'editor', +}); const sessionStore = useSessionStore(); const route = useRoute(); @@ -24,8 +24,7 @@ const item = sessionStore.getItemById(itemId); </span> </PageHeader> - <div id="options-container"> - </div> + <div id="options-container"></div> </template> <style scoped> @@ -41,4 +40,4 @@ const item = sessionStore.getItemById(itemId); header { border-bottom: 1px solid var(--color-border); } -</style>
\ No newline at end of file +</style> diff --git a/pages/editor/quest/[id].vue b/pages/editor/quest/[id].vue index 34dafa8..8886dc5 100644 --- a/pages/editor/quest/[id].vue +++ b/pages/editor/quest/[id].vue @@ -4,8 +4,8 @@ import { computed, ref } from 'vue'; import type EditorQuestModalYaml from '~/components/editor/quest/modal/EditorQuestModalYaml.vue'; definePageMeta({ - layout: 'editor' -}) + layout: 'editor', +}); const sessionStore = useSessionStore(); const route = useRoute(); @@ -13,7 +13,6 @@ const route = useRoute(); const questId = route.params.id as string; const quest = sessionStore.getQuestById(questId); - const categoryFromSelectedQuest = computed(() => { const quest = sessionStore.getQuestById(questId); if (quest) { @@ -48,7 +47,7 @@ const duplicateQuest = (oldId: string, newId: string) => { const showYaml = () => { yamlModal.value?.open(); -} +}; </script> <template> @@ -65,9 +64,17 @@ const showYaml = () => { </span> <span id="controls" class="control-group"> <Button :icon="['fas', 'fa-code']" :label="'YAML'" @click="showYaml"></Button> - <Button :icon="['fas', 'fa-copy']" :label="'Duplicate'" @click="showDuplicateModal = true"></Button> + <Button + :icon="['fas', 'fa-copy']" + :label="'Duplicate'" + @click="showDuplicateModal = true" + ></Button> <Button :icon="['fas', 'fa-pen']" :label="'Rename'" @click="showRenameModal = true"></Button> - <Button :icon="['fas', 'fa-trash']" :label="'Delete'" @click="showDeleteModal = true"></Button> + <Button + :icon="['fas', 'fa-trash']" + :label="'Delete'" + @click="showDeleteModal = true" + ></Button> <Button type="solid" :disabled="true" :icon="['fas', 'fa-save']" :label="'Save'"></Button> </span> </PageHeader> @@ -78,12 +85,24 @@ const showYaml = () => { </div> <EditorQuestModalYaml ref="yamlModal" :key="`yaml-quest-${questId}`" :questId="questId" /> - <EditorQuestModalDelete v-model="showDeleteModal" :key="`delete-quest-${questId}`" :questId="questId" - @delete="() => questId && deleteQuest(questId)" /> - <EditorQuestModalRename v-model="showRenameModal" :key="`rename-quest-${questId}`" :questId="questId" - @update="(newId: any) => questId && renameQuest(questId, newId)" /> - <EditorQuestModalDuplicate v-model="showDuplicateModal" :key="`duplicate-quest-${questId}`" :questId="questId" - @duplicate="(newId: any) => questId && duplicateQuest(questId, newId)" /> + <EditorQuestModalDelete + v-model="showDeleteModal" + :key="`delete-quest-${questId}`" + :questId="questId" + @delete="() => questId && deleteQuest(questId)" + /> + <EditorQuestModalRename + v-model="showRenameModal" + :key="`rename-quest-${questId}`" + :questId="questId" + @update="(newId: any) => questId && renameQuest(questId, newId)" + /> + <EditorQuestModalDuplicate + v-model="showDuplicateModal" + :key="`duplicate-quest-${questId}`" + :questId="questId" + @duplicate="(newId: any) => questId && duplicateQuest(questId, newId)" + /> </template> <style scoped> @@ -99,4 +118,4 @@ const showYaml = () => { header { border-bottom: 1px solid var(--color-border); } -</style>
\ No newline at end of file +</style> diff --git a/pages/import.vue b/pages/import.vue index 7397358..3cadb3d 100644 --- a/pages/import.vue +++ b/pages/import.vue @@ -2,8 +2,8 @@ import LoaderDiscardSessionModal from '~/components/loader/LoaderDiscardSessionModal.vue'; definePageMeta({ - layout: 'default' -}) + layout: 'default', +}); const session = useSessionStore(); @@ -64,8 +64,17 @@ const discardSessionModal = ref<InstanceType<typeof LoaderDiscardSessionModal> | </div> <div id="button-group"> - <Button :icon="['fas', 'arrow-left']" label="Return to Session" @click="navigateToEditorPane(null)" /> - <Button accent="danger" :icon="['fas', 'trash']" label="Discard" @click="discardSessionModal?.open" /> + <Button + :icon="['fas', 'arrow-left']" + label="Return to Session" + @click="navigateToEditorPane(null)" + /> + <Button + accent="danger" + :icon="['fas', 'trash']" + label="Discard" + @click="discardSessionModal?.open" + /> </div> </div> </div> @@ -144,4 +153,4 @@ const discardSessionModal = ref<InstanceType<typeof LoaderDiscardSessionModal> | hr { width: 100%; } -</style>
\ No newline at end of file +</style> diff --git a/pages/index.vue b/pages/index.vue index a64706a..d4a9b6d 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -1,7 +1,7 @@ <script setup lang="ts"> definePageMeta({ - layout: 'default' -}) + layout: 'default', +}); </script> <template> @@ -9,30 +9,35 @@ definePageMeta({ <div id="welcome"> <img id="welcome-image" src="@/assets/quests-logo.png" alt="Quests logo" /> <b id="title">Welcome to the Quests Web Editor</b> - <p>This is a work in progress build of the web editor for <a href="https://github.com/LMBishop/Quests">Quests</a>. - It is <b>not</b> ready for use. + <p> + This is a work in progress build of the web editor for + <a href="https://github.com/LMBishop/Quests">Quests</a>. It is <b>not</b> ready for use. </p> - <p>If you would like to try this out, use the buttons in the top right corner to start a session.</p> - <b id="subtitle">Cannot import from filesystem</b> <p> - If you cannot import from filesystem, then that means you are using a browser which does not implement the - <a href="https://wicg.github.io/file-system-access/">File System Access API</a>. At the time of writing, - only Chromium based browsers (Google Chrome, Microsoft Edge, Brave, etc.) implement this. This means if you - use Firefox or Safari, you must switch browser to use this feature. + If you would like to try this out, use the buttons in the top right corner to start a + session. </p> + <b id="subtitle">Cannot import from filesystem</b> <p> - You can instead load the test data instead by clicking the "Demo" button. + If you cannot import from filesystem, then that means you are using a browser which does not + implement the + <a href="https://wicg.github.io/file-system-access/">File System Access API</a>. At the time + of writing, only Chromium based browsers (Google Chrome, Microsoft Edge, Brave, etc.) + implement this. This means if you use Firefox or Safari, you must switch browser to use this + feature. </p> + <p>You can instead load the test data instead by clicking the "Demo" button.</p> <b id="subtitle">Light / dark theme</b> <p> - Note that the light theme of this website is a work in progress. Please use the dark theme. The colour scheme - will automatically adjust based on your browsers preferences. + Note that the light theme of this website is a work in progress. Please use the dark theme. + The colour scheme will automatically adjust based on your browsers preferences. </p> <b id="subtitle">Source code</b> <p> - Just like Quests itself, the source code is <a href="https://github.com/LMBishop/quests-web-editor">available - on GitHub</a>. - Due to the volatile state of this project, if you would like to get involved please get in touch with me first. + Just like Quests itself, the source code is + <a href="https://github.com/LMBishop/quests-web-editor">available on GitHub</a>. Due to the + volatile state of this project, if you would like to get involved please get in touch with + me first. </p> </div> </div> @@ -74,4 +79,4 @@ definePageMeta({ font-weight: bold; } } -</style>
\ No newline at end of file +</style> diff --git a/plugins/fontawesome.ts b/plugins/fontawesome.ts index f826370..f3b12d3 100644 --- a/plugins/fontawesome.ts +++ b/plugins/fontawesome.ts @@ -1,5 +1,5 @@ -import { library, config } from '@fortawesome/fontawesome-svg-core' -import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' +import { library, config } from '@fortawesome/fontawesome-svg-core'; +import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'; import { faAppleWhole, faArrowLeft, @@ -31,50 +31,50 @@ import { faTrowel, faUpload, faWrench, - faXmark -} from '@fortawesome/free-solid-svg-icons' -import { faCompass } from '@fortawesome/free-regular-svg-icons' + faXmark, +} from '@fortawesome/free-solid-svg-icons'; +import { faCompass } from '@fortawesome/free-regular-svg-icons'; -config.autoAddCss = false +config.autoAddCss = false; // fas -library.add(faPlus) -library.add(faCheck) -library.add(faCubes) -library.add(faCubesStacked) -library.add(faXmark) -library.add(faSave) -library.add(faFileExport) -library.add(faFileImport) -library.add(faDownload) -library.add(faUpload) -library.add(faFolder) -library.add(faPencil) -library.add(faCodeBranch) -library.add(faFolderOpen) -library.add(faFlaskVial) -library.add(faTrash) -library.add(faCaretDown) -library.add(faFileZipper) -library.add(faWrench) -library.add(faHardDrive) -library.add(faArrowLeft) -library.add(faChevronRight) -library.add(faCode) -library.add(faCopy) -library.add(faPen) -library.add(faTag) -library.add(faAppleWhole) -library.add(faCube) +library.add(faPlus); +library.add(faCheck); +library.add(faCubes); +library.add(faCubesStacked); +library.add(faXmark); +library.add(faSave); +library.add(faFileExport); +library.add(faFileImport); +library.add(faDownload); +library.add(faUpload); +library.add(faFolder); +library.add(faPencil); +library.add(faCodeBranch); +library.add(faFolderOpen); +library.add(faFlaskVial); +library.add(faTrash); +library.add(faCaretDown); +library.add(faFileZipper); +library.add(faWrench); +library.add(faHardDrive); +library.add(faArrowLeft); +library.add(faChevronRight); +library.add(faCode); +library.add(faCopy); +library.add(faPen); +library.add(faTag); +library.add(faAppleWhole); +library.add(faCube); // task type icons -library.add(faTrowel) -library.add(faBuildingColumns) -library.add(faBoxesStacked) +library.add(faTrowel); +library.add(faBuildingColumns); +library.add(faBoxesStacked); // far -library.add(faCompass) +library.add(faCompass); export default defineNuxtPlugin((nuxtApp) => { - nuxtApp.vueApp.component('font-awesome-icon', FontAwesomeIcon, {}) -}) + nuxtApp.vueApp.component('font-awesome-icon', FontAwesomeIcon, {}); +}); diff --git a/plugins/jszip.ts b/plugins/jszip.ts index edf0206..775df43 100644 --- a/plugins/jszip.ts +++ b/plugins/jszip.ts @@ -1,5 +1,5 @@ -import JSZip from "jszip" +import JSZip from 'jszip'; export default defineNuxtPlugin((nuxtApp) => { - nuxtApp.vueApp.use(JSZip) -}) + nuxtApp.vueApp.use(JSZip); +}); diff --git a/plugins/multiselect.ts b/plugins/multiselect.ts index 5f76652..e86718a 100644 --- a/plugins/multiselect.ts +++ b/plugins/multiselect.ts @@ -1,5 +1,5 @@ -import Multiselect from 'vue-multiselect' +import Multiselect from 'vue-multiselect'; export default defineNuxtPlugin((nuxtApp) => { - nuxtApp.vueApp.component('multiselect', Multiselect, {}) -}) + nuxtApp.vueApp.component('multiselect', Multiselect, {}); +}); diff --git a/server.prepare.ts b/server.prepare.ts index a858e69..875889c 100644 --- a/server.prepare.ts +++ b/server.prepare.ts @@ -1,5 +1,5 @@ import { execSync } from 'child_process'; -import { defineNuxtPrepareHandler } from 'nuxt-prepare/config' +import { defineNuxtPrepareHandler } from 'nuxt-prepare/config'; export default defineNuxtPrepareHandler(async () => { const gitCommitHash = execSync('git rev-parse HEAD').toString().trim(); @@ -11,12 +11,12 @@ export default defineNuxtPrepareHandler(async () => { public: { gitCommitHash: gitCommitHash, gitCommitHashShort: gitCommitHashShort, - gitBranch: gitBranch - } + gitBranch: gitBranch, + }, }, state: { foo: 'bar', }, - } -})
\ No newline at end of file + }; +}); diff --git a/stores/export.ts b/stores/export.ts index 3f48aa3..f0ff260 100644 --- a/stores/export.ts +++ b/stores/export.ts @@ -1,4 +1,4 @@ -import { defineStore } from 'pinia' +import { defineStore } from 'pinia'; export type ZipLoaderStatus = 'inactive' | 'preparing' | 'compressing' | 'ready' | 'failed'; @@ -7,7 +7,7 @@ export const useExportStore = defineStore('export', { zip: { status: 'inactive' as ZipLoaderStatus, contents: null as Blob | null, - } + }, }), getters: { getZipStatus: (state) => () => { @@ -27,5 +27,5 @@ export const useExportStore = defineStore('export', { setZipContents(contents: Blob) { this.zip.contents = contents; }, - } + }, }); diff --git a/stores/loader.ts b/stores/loader.ts index fe3742c..9d4b7ad 100644 --- a/stores/loader.ts +++ b/stores/loader.ts @@ -1,4 +1,4 @@ -import { defineStore } from 'pinia' +import { defineStore } from 'pinia'; import type { EditorCategory, EditorItem } from './session'; export type FileSystemLoaderStatus = 'inactive' | 'pending' | 'loaded' | 'invalid' | 'valid'; @@ -11,7 +11,7 @@ export const useLoaderStore = defineStore('loader', { quests: [] as EditorQuest[], categories: [] as EditorCategory[], items: [] as EditorItem[], - } + }, }), getters: { getFileSystemLoaderStatus: (state) => () => { @@ -50,6 +50,6 @@ export const useLoaderStore = defineStore('loader', { }, setItems(items: EditorItem[]) { this.fileSystem.items = items; - } - } + }, + }, }); diff --git a/stores/session.ts b/stores/session.ts index 50bcde7..c593b78 100644 --- a/stores/session.ts +++ b/stores/session.ts @@ -1,4 +1,4 @@ -import { defineStore } from 'pinia' +import { defineStore } from 'pinia'; export interface EditorQuest { id: string; @@ -7,9 +7,9 @@ export interface EditorQuest { lore: { normal: string[]; started: string[]; - } + }; type: string; - } + }; tasks: { [key: string]: EditorTask }; rewards: string[]; startCommands?: string[]; @@ -26,18 +26,18 @@ export interface EditorQuest { cooldown: { enabled: boolean; time: number; - } + }; timeLimit: { enabled: boolean; time: number; - } + }; sortOrder: number; autostart: boolean; completedDisplay?: string; cooldownDisplay?: string; permissionDisplay?: string; lockedDisplay?: string; - } + }; } export interface EditorTask { @@ -45,7 +45,7 @@ export interface EditorTask { config: { type: string; [key: string]: any; - } + }; } export interface EditorCategory { @@ -54,14 +54,14 @@ export interface EditorCategory { name: string; lore: string[]; type: string; - } + }; permissionRequired: string; } export interface EditorItem { id: string; type: string; - config: any + config: any; } export interface TaskDefinition { @@ -69,7 +69,7 @@ export interface TaskDefinition { icon: { style: string; name: string; - } + }; configuration: { [key: string]: { type: string | string[]; @@ -77,8 +77,8 @@ export interface TaskDefinition { default?: any; required?: boolean; note?: string; - } - } + }; + }; } export interface QuestItemDefinition { @@ -89,8 +89,8 @@ export interface QuestItemDefinition { [key: string]: { type: string | string[]; description: string; - } - } + }; + }; } export type SessionType = 'none' | 'filesystem' | 'demo'; @@ -104,69 +104,72 @@ export const useSessionStore = defineStore('session', { items: [] as EditorItem[], taskDefinitions: {} as { [key: string]: TaskDefinition }, taskTypeAliases: {} as { [key: string]: string }, - questItemDefinitions: {} as { [key: string]: QuestItemDefinition } - } + questItemDefinitions: {} as { [key: string]: QuestItemDefinition }, + }, }), getters: { getSessionType: (state) => () => { - return state.sessionType + return state.sessionType; }, getQuests: (state) => () => { - return state.session.quests + return state.session.quests; }, getCategories: (state) => () => { - return state.session.categories + return state.session.categories; }, getItems: (state) => () => { - return state.session.items + return state.session.items; }, getQuestById: (state) => (id: string) => { if (!id) return null; - return state.session.quests.find(quest => quest.id === id) + return state.session.quests.find((quest) => quest.id === id); }, getCategoryById: (state) => (id: string) => { if (!id) return null; - return state.session.categories.find(quest => quest.id === id) + return state.session.categories.find((quest) => quest.id === id); }, getItemById: (state) => (id: string) => { if (!id) return null; - return state.session.items.find(item => item.id === id); + return state.session.items.find((item) => item.id === id); }, getQuestsInCategory: (state) => (id: string) => { if (!id) return []; - return state.session.quests.filter(quest => quest.options.category === id) + return state.session.quests.filter((quest) => quest.options.category === id); }, getTaskDefinitions: (state) => { - return state.session.taskDefinitions + return state.session.taskDefinitions; }, getTaskDefinitionByTaskType: (state) => (type: string) => { - return state.session.taskDefinitions[type] || state.session.taskDefinitions[state.session.taskTypeAliases[type]] + return ( + state.session.taskDefinitions[type] || + state.session.taskDefinitions[state.session.taskTypeAliases[type]] + ); }, getKnownTaskTypes: (state) => () => { - return Object.keys(state.session.taskDefinitions) + return Object.keys(state.session.taskDefinitions); }, getQuestItemDefintions: (state) => { - return state.session.questItemDefinitions + return state.session.questItemDefinitions; }, getQuestItemDefinitionByTaskType: (state) => (type: string) => { - return state.session.questItemDefinitions[type] + return state.session.questItemDefinitions[type]; }, getKnownQuestItemTypes: (state) => () => { - return Object.keys(state.session.questItemDefinitions) - } + return Object.keys(state.session.questItemDefinitions); + }, }, actions: { setSessionType(type: SessionType) { - this.sessionType = type + this.sessionType = type; }, setQuests(quests: EditorQuest[]) { - this.session.quests = quests + this.session.quests = quests; }, setCategories(categories: EditorCategory[]) { - this.session.categories = categories + this.session.categories = categories; }, setItems(items: EditorItem[]) { - this.session.items = items + this.session.items = items; }, setTaskDefinitions(definitions: { [key: string]: TaskDefinition }) { this.session.taskDefinitions = definitions; @@ -181,12 +184,12 @@ export const useSessionStore = defineStore('session', { const quest = this.getQuestById(oldId); if (!quest) return; - quest.id = newId + quest.id = newId; }, deleteQuest(id: string) { - const index = this.session.quests.findIndex(quest => quest.id === id) + const index = this.session.quests.findIndex((quest) => quest.id === id); if (index === -1) return; - this.session.quests.splice(index, 1) + this.session.quests.splice(index, 1); }, duplicateQuest(id: string, newQuestId: string) { const quest = this.getQuestById(id); @@ -195,6 +198,6 @@ export const useSessionStore = defineStore('session', { const newQuest = JSON.parse(JSON.stringify(quest)); newQuest.id = newQuestId; this.session.quests.push(newQuest); - } - } + }, + }, }); diff --git a/utils/loader.ts b/utils/loader.ts index 56e29fa..adfa40f 100644 --- a/utils/loader.ts +++ b/utils/loader.ts @@ -1,5 +1,5 @@ -import { parse } from "yaml"; -import { loadCategoriesFromJson, loadItemsFromJson, loadQuestsFromJson } from "~/lib/questsLoader"; +import { parse } from 'yaml'; +import { loadCategoriesFromJson, loadItemsFromJson, loadQuestsFromJson } from '~/lib/questsLoader'; export async function openFileSystem() { try { @@ -31,50 +31,57 @@ export async function enumerateQuestDirectory(dirHandle: any) { throw Error('invalid quest directory'); } - const [questFiles, itemFiles] = await Promise.all([questsDirectory ? listAllFilesAndDirs(questsDirectory) : [], itemsDirectory ? listAllFilesAndDirs(itemsDirectory) : []]); - const [categories, quests, items] = await Promise.all([(async () => { - if (!categoryFile) { - return []; - } + const [questFiles, itemFiles] = await Promise.all([ + questsDirectory ? listAllFilesAndDirs(questsDirectory) : [], + itemsDirectory ? listAllFilesAndDirs(itemsDirectory) : [], + ]); + const [categories, quests, items] = await Promise.all([ + (async () => { + if (!categoryFile) { + return []; + } - const file: any = await categoryFile.getFile(); - const text: string = await file.text(); - const parsedYaml: any = parse(text); + const file: any = await categoryFile.getFile(); + const text: string = await file.text(); + const parsedYaml: any = parse(text); - return loadCategoriesFromJson(parsedYaml.categories); - })(), - (async () => { - if (!questFiles) { - return []; - } + return loadCategoriesFromJson(parsedYaml.categories); + })(), + (async () => { + if (!questFiles) { + return []; + } - const allQuests = await Promise.all(questFiles.filter(({ name, handle, kind }) => name.endsWith('.yml')).map(async ({ name, handle, kind }) => { - const file: any = await handle.getFile(); - const text: string = await file.text(); - return [ - name.replace('.yml', ''), - parse(text) - ]; - })) + const allQuests = await Promise.all( + questFiles + .filter(({ name, handle, kind }) => name.endsWith('.yml')) + .map(async ({ name, handle, kind }) => { + const file: any = await handle.getFile(); + const text: string = await file.text(); + return [name.replace('.yml', ''), parse(text)]; + }) + ); - return loadQuestsFromJson(Object.fromEntries(allQuests)); - })(), - (async () => { - if (!itemFiles) { - return []; - } + return loadQuestsFromJson(Object.fromEntries(allQuests)); + })(), + (async () => { + if (!itemFiles) { + return []; + } - const allItems = await Promise.all(itemFiles.filter(({ name, handle, kind }) => name.endsWith('.yml')).map(async ({ name, handle, kind }) => { - const file: any = await handle.getFile(); - const text: string = await file.text(); - return [ - name.replace('.yml', ''), - parse(text) - ]; - })) + const allItems = await Promise.all( + itemFiles + .filter(({ name, handle, kind }) => name.endsWith('.yml')) + .map(async ({ name, handle, kind }) => { + const file: any = await handle.getFile(); + const text: string = await file.text(); + return [name.replace('.yml', ''), parse(text)]; + }) + ); - return loadItemsFromJson(Object.fromEntries(allItems)); - })()]); + return loadItemsFromJson(Object.fromEntries(allItems)); + })(), + ]); return { categories, quests, items }; } @@ -84,10 +91,10 @@ async function listAllFilesAndDirs(dirHandle: any): Promise<any[]> { for await (const [name, handle] of dirHandle) { const { kind } = handle; if (handle.kind === 'directory') { - files.push(...await listAllFilesAndDirs(handle)); + files.push(...(await listAllFilesAndDirs(handle))); } else { files.push({ name, handle, kind }); } } return files; -}
\ No newline at end of file +} diff --git a/utils/util.ts b/utils/util.ts index 273191a..f5f7247 100644 --- a/utils/util.ts +++ b/utils/util.ts @@ -1,31 +1,34 @@ -const COLOR_CODE_REGEX = /&([0-9a-fk-or]|#[0-9A-F]{6})/ig; +const COLOR_CODE_REGEX = /&([0-9a-fk-or]|#[0-9A-F]{6})/gi; export function stripColorCodes(str: string): string { return str.replace(COLOR_CODE_REGEX, ''); } -export function navigateToEditorPane(type: 'quest' | 'category' | 'item' | 'config' | null, id?: string) { +export function navigateToEditorPane( + type: 'quest' | 'category' | 'item' | 'config' | null, + id?: string +) { if (id) { if (type === 'category') { - navigateTo({ path: '/editor/category/' + id }) + navigateTo({ path: '/editor/category/' + id }); } else if (type === 'quest') { - navigateTo({ path: '/editor/quest/' + id }) + navigateTo({ path: '/editor/quest/' + id }); } else if (type === 'item') { - navigateTo({ path: '/editor/item/' + id }) + navigateTo({ path: '/editor/item/' + id }); } } else if (type === 'config') { - navigateTo({ path: '/editor/config' }) + navigateTo({ path: '/editor/config' }); } else if (!id && !type) { - navigateTo({ path: '/editor' }) + navigateTo({ path: '/editor' }); } } export type BrowserCapabilities = { - canUseFsApi: boolean -} + canUseFsApi: boolean; +}; export function getBrowserCapabilities(): BrowserCapabilities { return { - canUseFsApi: typeof (window as any)?.showDirectoryPicker === 'function' - } -}
\ No newline at end of file + canUseFsApi: typeof (window as any)?.showDirectoryPicker === 'function', + }; +} diff --git a/utils/zipExporter.ts b/utils/zipExporter.ts index 635e89d..3aff4b3 100644 --- a/utils/zipExporter.ts +++ b/utils/zipExporter.ts @@ -1,33 +1,53 @@ -import JSZip from "jszip"; -import { stringify } from "yaml"; -import { mapJsonCategoryToYamlObject, mapJsonItemToYamlObject, mapJsonQuestToYamlObject } from "~/lib/questsLoader"; +import JSZip from 'jszip'; +import { stringify } from 'yaml'; +import { + mapJsonCategoryToYamlObject, + mapJsonItemToYamlObject, + mapJsonQuestToYamlObject, +} from '~/lib/questsLoader'; //TODO include the main configuration -export async function prepareZip(quests: EditorQuest[], categories: EditorCategory[], items: EditorItem[]) { - const transformedQuests = Object.fromEntries(quests.map((quest) => [quest.id, stringify(mapJsonQuestToYamlObject(quest))])); - const transformedItems = Object.fromEntries(items.map((item) => [item.id, stringify(mapJsonItemToYamlObject(item))])); - const transformedCategories = stringify(Object.fromEntries(categories.map((category) => [category.id, mapJsonCategoryToYamlObject(category)]))); +export async function prepareZip( + quests: EditorQuest[], + categories: EditorCategory[], + items: EditorItem[] +) { + const transformedQuests = Object.fromEntries( + quests.map((quest) => [quest.id, stringify(mapJsonQuestToYamlObject(quest))]) + ); + const transformedItems = Object.fromEntries( + items.map((item) => [item.id, stringify(mapJsonItemToYamlObject(item))]) + ); + const transformedCategories = stringify( + Object.fromEntries( + categories.map((category) => [category.id, mapJsonCategoryToYamlObject(category)]) + ) + ); return { transformedQuests, transformedItems, - transformedCategories - } + transformedCategories, + }; } -export async function createZip(quests: { [key: string]: string }, categories: string, items: { [key: string]: string }) { +export async function createZip( + quests: { [key: string]: string }, + categories: string, + items: { [key: string]: string } +) { const zip = new JSZip(); - zip.file("categories.yml", categories); + zip.file('categories.yml', categories); - const questsDirectory = zip.folder("quests"); + const questsDirectory = zip.folder('quests'); Object.entries(quests).forEach(([key, value]) => { - questsDirectory?.file(`${key}.yml`, value) - }) - const itemsDirectory = zip.folder("items"); + questsDirectory?.file(`${key}.yml`, value); + }); + const itemsDirectory = zip.folder('items'); Object.entries(items).forEach(([key, value]) => { - itemsDirectory?.file(`${key}.yml`, value) - }) + itemsDirectory?.file(`${key}.yml`, value); + }); - return await zip.generateAsync({ type: "blob" }); -}
\ No newline at end of file + return await zip.generateAsync({ type: 'blob' }); +} |
