crude properties and explorer

This commit is contained in:
Evert Prants 2023-06-04 15:19:04 +03:00
parent b90e43bf79
commit 4e7bdd7fc0
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
13 changed files with 562 additions and 23 deletions

View File

@ -13,7 +13,7 @@
<SidebarPanel>
<template #title>Properties</template>
asd
<SidebarForm :selection="selection" @update="update" />
</SidebarPanel>
</div>
</template>
@ -26,8 +26,10 @@ import SidebarPanel from './sidebar/SidebarPanel.vue';
import { GameObject } from '@freeblox/engine';
import { Object3D } from 'three';
import SidebarRow from './sidebar/SidebarRow.vue';
import SidebarForm from './sidebar/SidebarForm.vue';
const items = shallowRef<Object3D[]>([]);
const items = shallowRef<GameObject[]>([]);
const selection = shallowRef<GameObject[]>([]);
const selectionMap = ref<string[]>([]);
const props = defineProps<{
@ -39,11 +41,18 @@ const { register } = useEditorEvents(props.editor);
const createSceneMap = () => {
if (!props.editor?.running) return;
const sceneTree = props.editor.getSceneTree();
items.value = [sceneTree.world, sceneTree.environment];
items.value = [];
nextTick(() => {
items.value = [sceneTree.world, sceneTree.environment];
});
};
const updateSelectionMap = (event: SelectionEvent) => {
selection.value = [];
selectionMap.value = event.selection.map((item) => item.uuid);
nextTick(
() => (selection.value = props.editor.getSelection() as GameObject[])
);
};
const selectItem = (item: Object3D, ctrl: boolean) => {
@ -53,9 +62,30 @@ const selectItem = (item: Object3D, ctrl: boolean) => {
});
};
const update = (item: GameObject, property: string, value: unknown) => {
props.editor.events.emit('change', {
object: item,
property,
value,
edited: true,
});
};
const rerenderSelection = () => {
selection.value = [];
nextTick(
() => (selection.value = props.editor.getSelection() as GameObject[])
);
};
register('initialized', () => createSceneMap());
register('selected', (event) => updateSelectionMap(event));
register('deselected', (event) => updateSelectionMap(event));
register('change', (event) => {
console.log(event);
if (['name', 'visible'].includes(event.property)) createSceneMap();
rerenderSelection();
});
onMounted(() => createSceneMap());
</script>

View File

@ -0,0 +1,56 @@
<template>
<div class="form-field-check">
<label class="form-field-check-label" :for="id">{{ name }}</label>
<div class="form-field-check-wrap">
<input
class="form-field-check-input"
type="checkbox"
:id="id"
v-model="modelValue"
:name="name"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
const props = defineProps<{
name: string;
value: boolean;
}>();
const modelValue = ref(props.value);
const emit = defineEmits<{
(e: 'update', value: boolean): void;
}>();
const id = computed(() => `form-${props.name}`);
watch(modelValue, (value, oldValue) => {
if (value !== oldValue) emit('update', value);
});
</script>
<style lang="scss">
.form-field-check {
display: grid;
grid-template-columns: 1fr 1fr;
&-wrap {
padding: 8px;
}
&-input {
border: 0;
background-color: #efefef;
}
&-label {
padding: 8px;
text-transform: capitalize;
}
}
</style>

View File

@ -0,0 +1,97 @@
<template>
<div class="form-field-color">
<label class="form-field-color-label" :for="id">{{ name }}</label>
<div class="form-field-color-input">
<div class="form-field-color-wrapper">
<input
type="color"
:value="modelValueColor"
@change="changed($event)"
:id="id"
ref="secretInput"
/>
</div>
<button
type="button"
class="form-field-color-button"
@click="secretInput.click()"
>
<span
class="form-field-color-preview"
:style="{ backgroundColor: modelValueColor }"
></span
>{{ modelValueColor }}
</button>
</div>
</div>
</template>
<script setup lang="ts">
import { Color, Vector3 } from 'three';
import { computed, ref } from 'vue';
const secretInput = ref();
const props = defineProps<{
name: string;
value: Color;
}>();
const modelValueColor = ref('#' + props.value.getHexString());
const emit = defineEmits<{
(e: 'update', value: Vector3 | Color): void;
}>();
const id = computed(() => `form-${props.name}`);
const changed = ($event: Event) => {
const value = ($event.target as HTMLInputElement).value;
modelValueColor.value = value;
emit('update', new Color(value));
};
</script>
<style lang="scss">
.form-field-color {
display: grid;
grid-template-columns: 1fr 1fr;
&-label {
padding: 8px;
text-transform: capitalize;
}
&-input {
position: relative;
display: flex;
}
&-wrapper {
position: absolute;
pointer-events: none;
opacity: 0;
}
&-button {
display: flex;
justify-content: flex-start;
align-items: center;
appearance: none;
padding: 4px;
cursor: pointer;
border: 0;
width: 100%;
text-align: left;
}
&-preview {
width: 1.5rem;
height: 1.5rem;
display: inline-block;
border-radius: 100%;
border: 1px solid #ddd;
margin-right: 4px;
}
}
</style>

View File

@ -0,0 +1,59 @@
<template>
<div class="form-field">
<label class="form-field-label" :for="id">{{ name }}</label>
<input
class="form-field-input"
type="text"
:id="id"
v-model="modelValue"
@change="changed"
:name="name"
/>
</div>
</template>
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
const props = defineProps<{
name: string;
value: unknown;
type?: 'string' | 'number';
}>();
const modelValue = ref(props.value);
const emit = defineEmits<{
(e: 'update', value: unknown): void;
}>();
const id = computed(() => `form-${props.name}`);
const changed = () => {
let value = modelValue.value;
if (props.type === 'number') {
value = Number(value);
if (isNaN(value as number)) return;
}
emit('update', value);
};
</script>
<style lang="scss">
.form-field {
display: grid;
grid-template-columns: 1fr 1fr;
&-input {
border: 0;
background-color: #efefef;
padding: 8px;
width: 100%;
}
&-label {
padding: 8px;
text-transform: capitalize;
}
}
</style>

View File

@ -0,0 +1,129 @@
<template>
<div class="form-field-vec">
<label class="form-field-vec-label" :for="id + '-x'">{{ name }}</label>
<div class="form-field-vec-pieces">
<div class="form-field-vec-piece">
<label class="form-field-vec-label-sub" :for="id + '-x'">{{
names[0]
}}</label>
<input
class="form-field-vec-input"
type="text"
:id="id + '-x'"
v-model="modelValueX"
:name="name + '-x'"
@blur="changed"
/>
</div>
<div class="form-field-vec-piece">
<label class="form-field-vec-label-sub" :for="id + '-y'">{{
names[1]
}}</label>
<input
class="form-field-vec-input"
type="text"
:id="id + '-y'"
v-model="modelValueY"
:name="name + '-y'"
@blur="changed"
/>
</div>
<div class="form-field-vec-piece">
<label class="form-field-vec-label-sub" :for="id + '-z'">{{
names[2]
}}</label>
<input
class="form-field-vec-input"
type="text"
:id="id + '-z'"
v-model="modelValueZ"
:name="name + '-z'"
@blur="changed"
/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { Color, Vector3 } from 'three';
import { computed, ref } from 'vue';
const props = defineProps<{
name: string;
value: Vector3 | Color;
type?: 'vector' | 'color';
}>();
const toVector = computed(() =>
props.value instanceof Color
? new Vector3(props.value.r * 255, props.value.g * 255, props.value.b * 255)
: props.value.clone()
);
const modelValueX = ref(toVector.value.x);
const modelValueY = ref(toVector.value.y);
const modelValueZ = ref(toVector.value.z);
const emit = defineEmits<{
(e: 'update', value: Vector3 | Color): void;
}>();
const names = computed(() =>
props.type === 'color' ? ['R', 'G', 'B'] : ['X', 'Y', 'Z']
);
const id = computed(() => `form-${props.name}`);
const changed = () => {
const [x, y, z] = [
Number(modelValueX.value),
Number(modelValueY.value),
Number(modelValueZ.value),
];
if (isNaN(x) || isNaN(y) || isNaN(z)) return;
if (props.type === 'color') {
emit('update', new Color(x / 255, y / 255, z / 255));
return;
}
emit('update', new Vector3(x, y, z));
};
</script>
<style lang="scss">
.form-field-vec {
display: grid;
grid-template-columns: 1fr 1fr;
&-label {
padding: 8px;
text-transform: capitalize;
}
&-label-sub {
padding: 8px 4px;
width: 16px;
}
&-pieces {
display: grid;
position: relative;
grid-template-columns: repeat(3, 1fr);
}
&-piece {
display: flex;
flex-direction: row;
}
&-input {
min-width: 0;
display: block;
width: 32px;
border: 0;
background-color: #efefef;
text-align: center;
}
}
</style>

View File

@ -0,0 +1,92 @@
<template>
<div class="sidebar-form">
<template v-for="form of formFields">
<component
:is="form.component"
:name="form.name"
:value="form.value"
:type="form.type"
@update="(value: string) => propertyChanged(form.name, value)"
></component>
</template>
</div>
</template>
<script setup lang="ts">
import { GameObject } from '@freeblox/engine';
import type { Component } from 'vue';
import { computed } from 'vue';
import Field from '../form/Field.vue';
import { Color, Vector3 } from 'three';
import Vector3Field from '../form/Vector3Field.vue';
import Checkbox from '../form/Checkbox.vue';
import ColorPicker from '../form/ColorPicker.vue';
interface FormItem {
name: string;
value: unknown;
type?: unknown;
component: Component;
}
const props = defineProps<{
selection: GameObject[];
}>();
const emit = defineEmits<{
(e: 'update', item: GameObject, property: string, value: string): void;
}>();
const propertyChanged = (property: string, value: string) => {
const object = props.selection[0];
emit('update', object, property, value);
};
const formFields = computed(() => {
// TODO: multi-edit
const object = props.selection[0];
if (!object) return [];
const fields: FormItem[] = [];
Object.values(object.editorProperties)
.filter((item) => item.exposed)
.forEach((property) => {
if (property.type === String || property.type === Number) {
fields.push({
name: property.name,
value: (object as unknown as Record<string, unknown>)[property.name],
type: property.type === String ? 'string' : 'number',
component: Field,
});
}
if (property.type === Vector3) {
fields.push({
name: property.name,
value: (object as unknown as Record<string, unknown>)[property.name],
component: Vector3Field,
});
}
if (property.type === Color) {
fields.push({
name: property.name,
value: (object as unknown as Record<string, unknown>)[property.name],
component: ColorPicker,
});
}
if (property.type === Boolean) {
fields.push({
name: property.name,
value: (object as unknown as Record<string, unknown>)[property.name],
component: Checkbox,
});
}
});
return fields;
});
</script>
<style lang="scss"></style>

View File

@ -8,6 +8,7 @@
>
{{ item.name }}
</button>
<div class="sidebar-row-children">
<SidebarRow
v-for="object of filtered(item.children)"
@ -30,6 +31,7 @@ const props = defineProps<{
selectionMap: string[];
depth: number;
}>();
const emit = defineEmits<{
(e: 'update'): void;
(e: 'select', item: Object3D, ctrl: boolean): void;

View File

@ -79,4 +79,8 @@ export class Editor extends GameRunner {
public export(name: string) {
return this.level.serializeLevelSave(name);
}
public getSelection() {
return this.workspace.selection;
}
}

View File

@ -46,6 +46,7 @@ export class WorkspaceComponent extends EngineComponent {
public transformPosition?: Vector3;
public transformRotation?: Euler;
public transformScale?: Vector3;
public transforming = false;
public selection: Object3D[] = [];
public cleanUpEvents?: Function;
@ -339,6 +340,7 @@ export class WorkspaceComponent extends EngineComponent {
this.orbitControls.enabled = false;
const target = this.transformControls.object;
if (!target) return;
this.transforming = true;
this.transformPosition = target.position.clone();
this.transformRotation = target.rotation.clone();
this.transformScale = target.scale.clone();
@ -352,6 +354,7 @@ export class WorkspaceComponent extends EngineComponent {
this.transformControls.addEventListener('mouseUp', () => {
this.orbitControls.enabled = true;
this.transforming = false;
const target = this.transformControls.object;
if (!target) return;
this.events.emit('transformEnd', {
@ -364,7 +367,7 @@ export class WorkspaceComponent extends EngineComponent {
this.transformControls.addEventListener('change', () => {
const target = this.transformControls.object;
if (!target) return;
if (!target || !this.transforming) return;
this.events.emit('transformChange', {
lastPosition: this.transformPosition!,
@ -384,8 +387,9 @@ export class WorkspaceComponent extends EngineComponent {
object: target,
property: 'position',
value: target.position.clone(),
transformed: true,
applied: true,
});
this.transformPosition = target.position.clone();
}
if (
@ -396,8 +400,9 @@ export class WorkspaceComponent extends EngineComponent {
object: target,
property: 'rotation',
value: target.rotation.clone(),
transformed: true,
applied: true,
});
this.transformRotation = target.rotation.clone();
}
if (this.transformScale && !this.transformScale.equals(target.scale)) {
@ -405,8 +410,9 @@ export class WorkspaceComponent extends EngineComponent {
object: target,
property: 'scale',
value: target.scale.clone(),
transformed: true,
applied: true,
});
this.transformScale = target.scale.clone();
}
});
}

View File

@ -1,6 +1,6 @@
import { Renderer } from '../core/renderer';
import { EngineComponent } from '../types/engine-component';
import { EngineEvents } from '../types/events';
import { ChangeEvent, EngineEvents } from '../types/events';
import { EventEmitter } from '../utils/events';
import { Environment } from '../gameobjects/environment.object';
import { assetManager } from '../assets/manager';
@ -53,6 +53,17 @@ export class LevelComponent extends EngineComponent {
}
private bindEvents() {
return () => {};
const changeEvent = (event: ChangeEvent) => {
if (event.applied || !event.object) return;
const prop = (event.object as any)[event.property];
if (prop?.copy) prop.copy(event.value);
else (event.object as any)[event.property] = event.value;
};
this.events.addListener('change', changeEvent);
return () => {
this.events.removeEventListener('change', changeEvent);
};
}
}

View File

@ -4,4 +4,8 @@ export class World extends GameObject {
public objectType = World.name;
public name = 'World';
public virtual = true;
constructor() {
super({});
}
}

View File

@ -41,7 +41,7 @@ export interface ChangeEvent {
property: string;
value: any;
edited?: boolean;
transformed?: boolean;
applied?: boolean;
}
export interface SceneTreeEvent {

View File

@ -37,7 +37,7 @@ importers:
version: 5.0.4
vite:
specifier: ^4.3.9
version: 4.3.9
version: 4.3.9(sass@1.62.1)
vue-tsc:
specifier: ^1.4.2
version: 1.4.2(typescript@5.0.4)
@ -63,6 +63,9 @@ importers:
'@vitejs/plugin-vue':
specifier: ^4.1.0
version: 4.1.0(vite@4.3.9)(vue@3.2.47)
sass:
specifier: ^1.62.1
version: 1.62.1
tslib:
specifier: ^2.5.3
version: 2.5.3
@ -71,7 +74,7 @@ importers:
version: 5.0.4
vite:
specifier: ^4.3.9
version: 4.3.9
version: 4.3.9(sass@1.62.1)
vue-tsc:
specifier: ^1.4.2
version: 1.4.2(typescript@5.0.4)
@ -450,7 +453,7 @@ packages:
vite: ^4.0.0
vue: ^3.2.25
dependencies:
vite: 4.3.9
vite: 4.3.9(sass@1.62.1)
vue: 3.2.47
dev: true
@ -638,6 +641,13 @@ packages:
uri-js: 4.4.1
dev: false
/anymatch@3.1.3:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
dependencies:
normalize-path: 3.0.0
picomatch: 2.3.1
/argparse@1.0.10:
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
dependencies:
@ -647,6 +657,10 @@ packages:
/balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
/binary-extensions@2.2.0:
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
engines: {node: '>=8'}
/brace-expansion@2.0.1:
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
dependencies:
@ -657,7 +671,20 @@ packages:
engines: {node: '>=8'}
dependencies:
fill-range: 7.0.1
dev: false
/chokidar@3.5.3:
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
engines: {node: '>= 8.10.0'}
dependencies:
anymatch: 3.1.3
braces: 3.0.2
glob-parent: 5.1.2
is-binary-path: 2.1.0
is-glob: 4.0.3
normalize-path: 3.0.0
readdirp: 3.6.0
optionalDependencies:
fsevents: 2.3.2
/code-block-writer@12.0.0:
resolution: {integrity: sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==}
@ -760,7 +787,6 @@ packages:
engines: {node: '>=8'}
dependencies:
to-regex-range: 5.0.1
dev: false
/fs-extra@10.1.0:
resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
@ -796,7 +822,6 @@ packages:
engines: {node: '>= 6'}
dependencies:
is-glob: 4.0.3
dev: false
/graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
@ -814,11 +839,20 @@ packages:
hasBin: true
dev: true
/immutable@4.3.0:
resolution: {integrity: sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==}
/import-lazy@4.0.0:
resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==}
engines: {node: '>=8'}
dev: false
/is-binary-path@2.1.0:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'}
dependencies:
binary-extensions: 2.2.0
/is-core-module@2.12.1:
resolution: {integrity: sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==}
dependencies:
@ -828,19 +862,16 @@ packages:
/is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
dev: false
/is-glob@4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
dependencies:
is-extglob: 2.1.1
dev: false
/is-number@7.0.0:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'}
dev: false
/jju@1.4.0:
resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==}
@ -955,6 +986,10 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
/normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
/path-browserify@1.0.1:
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
dev: false
@ -969,7 +1004,6 @@ packages:
/picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
dev: false
/postcss@8.4.24:
resolution: {integrity: sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==}
@ -988,6 +1022,12 @@ packages:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
dev: false
/readdirp@3.6.0:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
dependencies:
picomatch: 2.3.1
/resolve@1.19.0:
resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==}
dependencies:
@ -1022,6 +1062,15 @@ packages:
queue-microtask: 1.2.3
dev: false
/sass@1.62.1:
resolution: {integrity: sha512-NHpxIzN29MXvWiuswfc1W3I0N8SXBd8UR26WntmDlRYf0bSADnwnOjsyMZ3lMezSlArD33Vs3YFhp7dWvL770A==}
engines: {node: '>=14.0.0'}
hasBin: true
dependencies:
chokidar: 3.5.3
immutable: 4.3.0
source-map-js: 1.0.2
/semver@7.3.8:
resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==}
engines: {node: '>=10'}
@ -1082,7 +1131,6 @@ packages:
engines: {node: '>=8.0'}
dependencies:
is-number: 7.0.0
dev: false
/ts-morph@18.0.0:
resolution: {integrity: sha512-Kg5u0mk19PIIe4islUI/HWRvm9bC1lHejK4S0oh1zaZ77TMZAEmQC0sHQYiu2RgCQFZKXz1fMVi/7nOOeirznA==}
@ -1137,14 +1185,14 @@ packages:
kolorist: 1.8.0
magic-string: 0.29.0
ts-morph: 18.0.0
vite: 4.3.9
vite: 4.3.9(sass@1.62.1)
transitivePeerDependencies:
- '@types/node'
- rollup
- supports-color
dev: false
/vite@4.3.9:
/vite@4.3.9(sass@1.62.1):
resolution: {integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
@ -1172,6 +1220,7 @@ packages:
esbuild: 0.17.19
postcss: 8.4.24
rollup: 3.23.0
sass: 1.62.1
optionalDependencies:
fsevents: 2.3.2