Whatever ClearWindow needs to be in json
All checks were successful
Build and Deploy Nuxt / build (push) Successful in 42s

This commit is contained in:
2026-04-26 22:57:29 +02:00
parent 475887420c
commit 2b07cc98a6
20 changed files with 509 additions and 59 deletions

View File

@@ -5,6 +5,7 @@ import WindowManager from './components/managers/WindowManager.vue';
import { CreateWindow } from '@/services/Windows' import { CreateWindow } from '@/services/Windows'
import { GetUser, HasAdmin } from './services/User'; import { GetUser, HasAdmin } from './services/User';
import TooltipManager from './components/managers/TooltipManager.vue';
async function start(){ async function start(){
if(GetUser()){ if(GetUser()){
@@ -39,6 +40,7 @@ onMounted(() => {
<template> <template>
<div class="viewer"> <div class="viewer">
<ToastManager></ToastManager> <ToastManager></ToastManager>
<TooltipManager></TooltipManager>
<WindowManager></WindowManager> <WindowManager></WindowManager>
<ContentManager></ContentManager> <ContentManager></ContentManager>
<!-- Managers --> <!-- Managers -->

View File

@@ -0,0 +1,41 @@
<script setup>
import { onMounted, watch, ref } from 'vue';
import { GetContentRef, SetupTooltip } from '../../services/Tooltip';
let contentRef = ref("");
onMounted(() => {
SetupTooltip();
let content = GetContentRef();
watch(GetContentRef(), () => {
contentRef.value = GetContentRef().value;
})
});
</script>
<template>
<div id="mouse-tooltip" class="mouse-tooltip">
<div class="document">
<span v-html="contentRef"></span>
</div>
</div>
</template>
<style scoped lang="scss">
.mouse-tooltip {
display: none;
position: absolute;
z-index: 214748364;
background-color: var(--tooltip-background);
padding: 3px 6px 3px 6px;
-webkit-box-shadow: 0px 0px 5px -2px rgba(0,0,0,0.75);
-moz-box-shadow: 0px 0px 5px -2px rgba(0,0,0,0.75);
box-shadow: 0px 0px 5px -2px rgba(0,0,0,0.75);
border: solid 1px var(--color-border);
}
</style>

View File

@@ -1,17 +1,14 @@
<script setup> <script setup>
import { TransitionGroup } from 'vue' import { TransitionGroup } from 'vue'
import { Windows, ReloadRef, WindowMap, getComponent } from '@/services/Windows'; import { windows, getComponent } from '@/services/Windows';
// Gestionem ventanas
const reload = ReloadRef();
const windows = Windows();
</script> </script>
<template> <template>
<div class="window-container" :key="reload"> <div class="window-container">
<TransitionGroup name="window"> <TransitionGroup name="window" tag="div">
<component v-for="win in windows" :is="getComponent(win.type)" :key="win.id" :data="win"></component> <div v-for="win in windows" :key="win.id">
<component :is="getComponent(win.type)" :data="win" />
</div>
</TransitionGroup> </TransitionGroup>
</div> </div>
</template> </template>

View File

@@ -4,10 +4,9 @@ import { GetUser, LogoutUser } from '@/services/User'
import Server from '@/services/Server' import Server from '@/services/Server'
import { ClearWindows, CreateWindow, CreateChildWindow, ClearWindow } from '../../services/Windows'; import { CreateWindow, CreateChildWindow, ClearWindow, GetFirstWindowId } from '../../services/Windows';
import { backendUrl } from '../../services/BackendURL'; import { backendUrl } from '../../services/BackendURL';
import Spinner from './Spinner.vue'; import Spinner from './Spinner.vue';
const emitter = useEmitter();
const loadedIcon = ref(false); const loadedIcon = ref(false);
const username = ref(""); const username = ref("");
@@ -45,13 +44,12 @@ function retrieveAvatar(){
function LogOut(){ function LogOut(){
LogoutUser(); LogoutUser();
ClearWindows({type: "main_menu"}); ClearWindow({type: "main_menu"});
CreateWindow('login'); CreateWindow('login');
} }
function EditProfile(){ function EditProfile(){
console.log("User:"); console.log(GetUser()); CreateChildWindow(GetFirstWindowId('main_menu'), 'edit_profile', {
CreateChildWindow('main_menu', 'edit_profile', {
user: GetUser() user: GetUser()
}); });
} }
@@ -104,7 +102,7 @@ onMounted(() => {
<Spinner v-show="!loadedIcon" :size="30"></Spinner> <Spinner v-show="!loadedIcon" :size="30"></Spinner>
</div> </div>
<div class="main-user-info"> <div class="main-user-info">
<b>{{ username }}</b><br>Hola <b>{{ username }}</b><br>
</div> </div>
<div class="main-user-actions"> <div class="main-user-actions">

View File

@@ -0,0 +1,84 @@
<script setup>
import { onMounted, ref } from 'vue';
import { AddTooltip } from '~/services/Tooltip';
const props = defineProps(['icon', 'action', 'size', 'toggled', 'tooltip']);
let icon = props.icon;
let action = props.action;
let size = props.size;
let toggled = props.toggled;
let tooltip = props.tooltip;
const button = ref(null);
onMounted(() => {
if(tooltip){
AddTooltip(button.value, tooltip);
}
})
</script>
<template>
<div class="icon-button sound-click" :class="size + ' ' + toggled" v-on:click.prevent="action" ref="button">
<img class="icon" draggable="false" :src="icon" :class="size">
</div>
</template>
<style scoped lang="scss">
.icon-button {
height: 32px;
width: 32px;
&.big {
height: 42px;
width: 42px;
}
&.small {
height: 24px;
width: 24px;
}
&.toggled {
filter: invert(0.9);
}
background-color: var(--color-background-soft);
border-radius: 6px;
display: flex;
justify-content: center;
align-items: center;
margin: 2px;
transition: .3s background-color;
border: 1px solid var(--color-border);
}
.icon-button:hover {
background-color: var(--color-background-softer);
}
.icon {
height: 24px;
width: 24px;
pointer-events: none;
&.big {
height: 38px;
width: 38px;
}
&.small {
height: 18px;
width: 18px;
}
}
</style>

View File

@@ -0,0 +1,89 @@
<script setup>
import { onMounted, ref } from 'vue';
import { SetupHandle, SetSize, ResetPosition } from '@/services/Windows';
import Server from '@/services/Server'
import { SetMinSize, SetResizable } from '@/services/Windows';
import { backendUrl } from '@/services/BackendURL';
import { GetUser } from '@/services/User';
import WindowHandle from './partials/WindowHandle.vue';
import BigIconTemplate from './partials/BigIconTemplate.vue';
import FixedBottomButtons from './partials/FixedBottomButtons.vue';
const props = defineProps(['data']);
const data = props.data;
const userIcon = ref("");
const handle = ref(null);
const isAdmin = ref(false);
let id = data.id;
console.log(data);
onMounted(() => {
SetupHandle(id, handle);
SetSize(id, {width: 500, height: 480});
ResetPosition(id, "center");
SetResizable(id, true);
SetMinSize(id, {width: 350, height: 280});
isAdmin.value = GetUser().admin;
Server().get('/user/retrieve-avatar?username=' + data.user.username).then((response) => {
if(response.data.image) userIcon.value = backendUrl + "public/" + response.data.image;
else userIcon.value = "public/img/def-avatar.jpg";
}).catch((err) => console.log("Internal error"));
});
function RemoveUser(){
alert("Remove")
}
</script>
<template>
<div class="window-wrapper" :id="'window-wrapper-' + id">
<WindowHandle :window="id" ref="handle"></WindowHandle>
<BigIconTemplate :title="data.user.username" :img="userIcon">
<div v-if="props.data.editable || isAdmin">
</div>
<div v-else>
</div>
</BigIconTemplate>
<FixedBottomButtons v-if="isAdmin" :remove="RemoveUser"></FixedBottomButtons>
</div>
</template>
<style scoped>
.window-wrapper {
display: flex;
align-items: center;
}
.splash-image {
width: 600px;
height: 250px;
}
.form-field {
padding: 10px;
display: flex;
align-items: left;
flex-direction: column;
justify-content: left;
width: 600px;
}
label {
text-align: left;
}
</style>

View File

@@ -9,9 +9,7 @@ const handle = ref(null);
const props = defineProps(['data']); const props = defineProps(['data']);
const data = props.data; const data = props.data;
let id = data.type; let id = data.id;
const test = ref(null)
onMounted(() => { onMounted(() => {
SetupHandle(id, handle); SetupHandle(id, handle);

View File

@@ -21,7 +21,7 @@ const handle = ref(null);
const props = defineProps(['data']); const props = defineProps(['data']);
const data = props.data; const data = props.data;
let id = data.type; let id = data.id;
const username = ref(""); const username = ref("");
const password = ref(""); const password = ref("");

View File

@@ -11,9 +11,7 @@ const handle = ref(null);
const props = defineProps(['data']); const props = defineProps(['data']);
const data = props.data; const data = props.data;
let id = data.type; let id = data.id;
const test = ref(null)
onMounted(() => { onMounted(() => {
SetupHandle(id, handle); SetupHandle(id, handle);

View File

@@ -13,8 +13,7 @@ const handle = ref(null);
const props = defineProps(['data']); const props = defineProps(['data']);
const data = props.data; const data = props.data;
let id = data.type; let id = data.id;
const username = ref(""); const username = ref("");
const password = ref(""); const password = ref("");

View File

@@ -0,0 +1,31 @@
<script setup>
import { onMounted, ref, watch } from 'vue';
const props = defineProps(['title', 'img']);
const imgSrc = ref("");
onMounted(() => {
imgSrc.value = props.img;
watch(() => props.img, () => {
imgSrc.value = props.img;
});
})
</script>
<template>
<div class="document centered">
<h1>{{props.title}}</h1>
<img :src="imgSrc" class="big-icon">
<br>
<slot></slot>
</div>
</template>
<style lang="scss" scoped>
.big-icon {
height: 80px;
width: 80px;
border: 1px solid var(--color-border);
margin:auto;
}
</style>

View File

@@ -0,0 +1,29 @@
<script setup>
import IconButton from '~/components/partials/IconButton.vue';
const props = defineProps(['plus', 'edit', 'view', 'remove']);
let plus = props.plus;
let edit = props.edit;
let view = props.view;
let remove = props.remove;
</script>
<template>
<div class="fixed-bottom-buttons">
<IconButton v-show="plus" icon="/icons/iconoir/regular/plus.svg" :action="plus"></IconButton>
<IconButton v-show="edit" icon="/icons/iconoir/regular/edit-pencil.svg" :action="edit"></IconButton>
<IconButton v-show="view" icon="/icons/iconoir/solid/eye.svg" :action="view"></IconButton>
<IconButton v-show="remove" icon="/icons/iconoir/solid/trash.svg" :action="remove"></IconButton>
</div>
</template>
<style lang="scss" scoped>
.fixed-bottom-buttons {
position: absolute;
bottom: 10px;
right: 10px;
z-index: 2;
display: flex;
}
</style>

View File

@@ -1,6 +1,6 @@
<script setup> <script setup>
import { onMounted, ref, watch } from 'vue'; import { onMounted, ref, watch } from 'vue';
import { GetWindowWithId, ClearWindow, Windows } from '@/services/Windows'; import { GetWindowWithId } from '@/services/Windows';
import ArrowLeftIcon from '/icons/iconoir/regular/arrow-left.svg'; import ArrowLeftIcon from '/icons/iconoir/regular/arrow-left.svg';
import XMarkIcon from '/icons/iconoir/regular/xmark.svg'; import XMarkIcon from '/icons/iconoir/regular/xmark.svg';

View File

@@ -0,0 +1,87 @@
import { ref } from 'vue';
import { animate } from 'motion';
let content = ref("");
let margin = 14;
let cursorX = 0;
let cursorY = 0;
let showed = false;
let hided = false;
function ShowTooltip(){
let tooltip = document.getElementById('mouse-tooltip');
tooltip.style.display = "block";
if(!showed){
animate(tooltip, {
opacity: [0, 1],
translateY: [20, 0]
}, {duration: 0.1, ease: 'ease-out'});
showed = true;
hided = false;
}
}
function HideTooltip(){
let tooltip = document.getElementById('mouse-tooltip');
if(!hided){
animate(tooltip, {
opacity: [1, 0],
translateY: [0, 20]
}, {duration: 0.1, ease: 'ease-in'}).finished.then(() => tooltip.style.display = "none")
hided = true;
showed = false;
}
}
function AddTooltip(element, val, data = {}){
element._dr_tooltip = {value: val, ...data};
}
function UpdateVisibilityThread(){
let tooltip = document.getElementById('mouse-tooltip');
let elements = document.elementsFromPoint(cursorX, cursorY);
let visible = false;
for(let i = 0; i < elements.length; i++){
let element = elements[i];
if(element._dr_tooltip){
ShowTooltip();
content.value = element._dr_tooltip.value;
if(element._dr_tooltip.max_width) tooltip.style.maxWidth = element._dr_tooltip.max_width + "px";
else tooltip.style.maxWidth = "none";
visible = true;
break;
}
}
if(!visible) HideTooltip();
setTimeout(UpdateVisibilityThread, 0);
}
function SetupTooltip(){
let tooltip = document.getElementById('mouse-tooltip');
document.addEventListener("mousemove", (event) => {
cursorX = event.clientX;
cursorY = event.clientY;
tooltip.style.top = (cursorY + margin) + "px";
tooltip.style.left = (cursorX + margin) + "px";
});
UpdateVisibilityThread();
}
let GetContentRef = () => content;
export {
SetupTooltip,
GetContentRef,
AddTooltip,
};

View File

@@ -1,7 +1,8 @@
import { ClearWindowsWithType, GetFirstWindowId } from './Windows'
/* /*
Put here all dragonroll windows Put here all dragonroll windows
*/ */
const defWindows = { const defWindows = {
login: { login: {
title: 'windows.login', title: 'windows.login',
@@ -20,7 +21,13 @@ const defWindows = {
example: { example: {
title: 'windows.example', title: 'windows.example',
component: () => import('~/components/windows/ExampleWindow.vue'), component: () => import('~/components/windows/ExampleWindow.vue'),
} },
edit_profile: {
title: "windows.edit-profile",
component: () => import('~/components/windows/EditProfileWindow.vue'),
close: () => ClearWindowsWithType(GetFirstWindowId('edit_profile')),
movable: true
},
} }
export { export {

View File

@@ -1,14 +1,20 @@
import { ref } from 'vue' import { ref } from 'vue'
import { defWindows } from './WindowDefinitions'; import { defWindows } from './WindowDefinitions';
import { defineAsyncComponent } from 'vue'
const componentCache = {}
const getComponent = (type) => {
if (!componentCache[type]) {
componentCache[type] = defineAsyncComponent(
defWindows[type].component
)
}
return componentCache[type]
}
const windows = ref([]); const windows = ref([]);
const getComponent = (type) => defineAsyncComponent(defWindows[type]?.component)
const reload = ref(0);
let ReloadRef = () => { return reload };
let Windows = () => { return windows };
let WindowMap = () => { return windowMap }; let WindowMap = () => { return windowMap };
let currentIndex = 10; let currentIndex = 10;
@@ -107,8 +113,9 @@ function SetupHandle(id, handle) {
// Should move eventually? // Should move eventually?
window.addEventListener('resize', (event) => { window.addEventListener('resize', (event) => {
if(!win.movable){ for(const w of windows.value){
ResetPosition(id, "center"); if(w.movable) continue;
ResetPosition(w.id, "center");
} }
}) })
@@ -182,6 +189,7 @@ function GetPosition(id) {
} }
function ResetPosition(id, pos) { function ResetPosition(id, pos) {
console.log("The id: ", id)
let win = GetWindowWithId(id); let win = GetWindowWithId(id);
let data = { x: win.x, y: win.y }; let data = { x: win.x, y: win.y };
@@ -194,42 +202,48 @@ function ResetPosition(id, pos) {
function CreateWindow(type, data = {}) { function CreateWindow(type, data = {}) {
console.log("Creating window")
console.log(windows.value);
let finalData = { ...{ type, id: currentId }, ...defWindows[type], ...data } let finalData = { ...{ type, id: currentId }, ...defWindows[type], ...data }
console.log(finalData); currentId++;
let contains = false; let contains = false;
for (let i = 0; i < windows.value.length; i++) { for (let i = 0; i < windows.value.length; i++) {
if (windows.value[i].type == finalData.type) { if (windows.value[i].type == finalData.type) {
contains = true; contains = true;
console.log("It contains")
break; break;
} }
} }
if (!contains) { if (!contains) {
windows.value.push(finalData); windows.value.push(finalData);
currentId++;
console.log(finalData); console.log(windows.value)
console.log("Pushed ", finalData.type);
// reload.value += 1;
console.log(windows.value);
setTimeout(() => { setTimeout(() => {
SetOnTop(finalData.type); SetOnTop(finalData.id);
if (finalData.create) finalData.create(); if (finalData.create) finalData.create();
}, 0); }, 0);
} }
} }
function CreateChildWindow(parentId, type, data = {}) { function CreateChildWindow(parentId, type, data = {}) {
console.log("Creating child window")
console.log(parentId, type, data);
let finalData = { ...{ type }, ...defWindows[type], ...data } let finalData = { ...{ type }, ...defWindows[type], ...data }
let parent = GetWindowWithId(parentId); let parent = GetWindowWithId(parentId);
console.log(parent);
if (parent.children) parent.children.push(finalData.type); if (parent.children) parent.children.push(finalData.type);
else parent.children = [finalData.type]; else parent.children = [finalData.type];
CreateWindow(type, data); CreateWindow(type, data);
} }
function GetFirstWindowId(type) {
for (let i = 0; i < windows.value.length; i++) {
if (windows.value[i].type == type) return windows.value[i].id;
}
}
function ClearAll() { function ClearAll() {
Object.keys(windows).forEach((key) => { Object.keys(windows).forEach((key) => {
windows.value = []; windows.value = [];
@@ -243,21 +257,25 @@ function ClearWindows(data) {
// reload.value += 1; // reload.value += 1;
} }
function ClearWindowsWithType(type) {
const index = windows.value.findIndex(w => w.type === type);
if (index !== -1) ClearWindow(windows.value[index].id)
// reload.value += 1;
}
function ClearWindow(id) { function ClearWindow(id) {
let win = GetWindowWithId(id); let win = GetWindowWithId(id);
console.log(win); console.log(win);
if (!win) return; if (!win) return;
if (win.children) for (let i = 0; i < win.children.length; i++) ClearWindow(win.children[i]); if (win.children) for (let i = 0; i < win.children.length; i++) ClearWindow(win.children[i].id);
windows.value = windows.value.filter((e) => { return e.type !== id }); const index = windows.value.findIndex(w => w.type === id)
if (index !== -1) windows.value.splice(index, 1)
// reload.value += 1; // reload.value += 1;
} }
function GetWindowWithId(id) { function GetWindowWithId(id) {
for (let i = 0; i < windows.value.length; i++) { const index = windows.value.findIndex(w => w.id === id);
if (windows.value[i].type == id) { if (index !== -1) return windows.value[index];
return windows.value[i];
}
}
} }
function CallWindow(id, callableName, arg) { function CallWindow(id, callableName, arg) {
@@ -283,6 +301,7 @@ function SetOnTop(id) {
} }
export { export {
windows,
SetupHandle, SetupHandle,
SetSize, SetSize,
SetResizable, SetResizable,
@@ -291,17 +310,17 @@ export {
SetPosition, SetPosition,
SetMovable, SetMovable,
ResetPosition, ResetPosition,
Windows,
WindowMap, WindowMap,
ReloadRef,
ClearWindows, ClearWindows,
CreateWindow, CreateWindow,
CreateChildWindow, CreateChildWindow,
GetFirstWindowId,
CallWindow, CallWindow,
GetWindowWithId, GetWindowWithId,
SaveWindowPos, SaveWindowPos,
GetPosition, GetPosition,
ClearWindow, ClearWindow,
ClearWindowsWithType,
ClearAll, ClearAll,
getComponent getComponent
} }

View File

@@ -3,7 +3,8 @@
"login": "Login", "login": "Login",
"register": "Register", "register": "Register",
"main-menu": "Dragonroll", "main-menu": "Dragonroll",
"example": "Example Window" "example": "Example Window",
"edit-profile": "Edit Profile"
}, },
"login": { "login": {
"username": "Username or email", "username": "Username or email",

View File

@@ -26,7 +26,8 @@ export default defineNuxtConfig({
'@vue/devtools-core', '@vue/devtools-core',
'@vue/devtools-kit', '@vue/devtools-kit',
'axios', 'axios',
'mitt' 'mitt',
'motion'
] ]
} }
}, },

View File

@@ -10,6 +10,7 @@
"@nuxtjs/i18n": "^10.3.0", "@nuxtjs/i18n": "^10.3.0",
"axios": "^1.15.2", "axios": "^1.15.2",
"mitt": "^3.0.1", "mitt": "^3.0.1",
"motion": "^12.38.0",
"nuxt": "^4.4.2", "nuxt": "^4.4.2",
"pixelarticons": "^2.1.0", "pixelarticons": "^2.1.0",
"sass": "^1.99.0", "sass": "^1.99.0",
@@ -7784,6 +7785,33 @@
"url": "https://github.com/sponsors/rawify" "url": "https://github.com/sponsors/rawify"
} }
}, },
"node_modules/framer-motion": {
"version": "12.38.0",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.38.0.tgz",
"integrity": "sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==",
"license": "MIT",
"dependencies": {
"motion-dom": "^12.38.0",
"motion-utils": "^12.36.0",
"tslib": "^2.4.0"
},
"peerDependencies": {
"@emotion/is-prop-valid": "*",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/is-prop-valid": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/fresh": { "node_modules/fresh": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
@@ -9042,6 +9070,47 @@
"integrity": "sha512-aF7yRQr/Q0O2/4pIXm6PZ5G+jAd7QS4Yu8m+WEeEHGnbo+7mE36CbLSDQiXYV8bVL3NfmdeqPJct0tUlnjVSnA==", "integrity": "sha512-aF7yRQr/Q0O2/4pIXm6PZ5G+jAd7QS4Yu8m+WEeEHGnbo+7mE36CbLSDQiXYV8bVL3NfmdeqPJct0tUlnjVSnA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/motion": {
"version": "12.38.0",
"resolved": "https://registry.npmjs.org/motion/-/motion-12.38.0.tgz",
"integrity": "sha512-uYfXzeHlgThchzwz5Te47dlv5JOUC7OB4rjJ/7XTUgtBZD8CchMN8qEJ4ZVsUmTyYA44zjV0fBwsiktRuFnn+w==",
"license": "MIT",
"dependencies": {
"framer-motion": "^12.38.0",
"tslib": "^2.4.0"
},
"peerDependencies": {
"@emotion/is-prop-valid": "*",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/is-prop-valid": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/motion-dom": {
"version": "12.38.0",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.38.0.tgz",
"integrity": "sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==",
"license": "MIT",
"dependencies": {
"motion-utils": "^12.36.0"
}
},
"node_modules/motion-utils": {
"version": "12.36.0",
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.36.0.tgz",
"integrity": "sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==",
"license": "MIT"
},
"node_modules/mrmime": { "node_modules/mrmime": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
@@ -11410,8 +11479,7 @@
"version": "2.8.1", "version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD", "license": "0BSD"
"optional": true
}, },
"node_modules/type-check": { "node_modules/type-check": {
"version": "0.4.0", "version": "0.4.0",

View File

@@ -13,6 +13,7 @@
"@nuxtjs/i18n": "^10.3.0", "@nuxtjs/i18n": "^10.3.0",
"axios": "^1.15.2", "axios": "^1.15.2",
"mitt": "^3.0.1", "mitt": "^3.0.1",
"motion": "^12.38.0",
"nuxt": "^4.4.2", "nuxt": "^4.4.2",
"pixelarticons": "^2.1.0", "pixelarticons": "^2.1.0",
"sass": "^1.99.0", "sass": "^1.99.0",