This commit is contained in:
2026-04-25 19:18:43 +02:00
parent 254a645c25
commit b8999ca7ee
1607 changed files with 9637 additions and 0 deletions

View File

@@ -1,16 +1,33 @@
<script setup lang="ts">
import WindowManager from './components/managers/WindowManager.vue';
import Content from './components/viewer/content/Content.vue';
import StatusBar from './components/viewer/statusbar/StatusBar.vue';
import TopBar from './components/viewer/TopBar.vue';
import { CreateWindow } from '@/services/Windows'
async function start(){
CreateWindow('login');
// DisplayToast('aqua', 'All plugins loaded successfully');
}
onMounted(() => {
start();
})
</script>
<template>
<div class="viewer">
<WindowManager></WindowManager>
<TopBar></TopBar>
<Content></Content>
<StatusBar></StatusBar>
<!-- Managers -->
</div>
</template>

View File

@@ -0,0 +1,73 @@
@use "sass:map";
$themes: (
dark: (
background: #141414,
background-line: #202324,
background-fore: #10141f,
hover: #21262d,
selected: #4a4a4b,
border-color: #819796,
border: #202324,
text: #ebede9,
container-shadow: #151d28,
sticky-header-bg: #20202077
),
light: (
background: #ffffff,
background-line: #f0f0f0,
background-fore: #ffffff,
border-color: #e0e0e0,
border: #f0f0f0,
hover: #e9e9e9,
selected: #d4d4d4,
text: #1e1e1e,
container-shadow: #5f6774,
sticky-header-bg: #fff
)
);
$accents: (
katlum: (
link: #a4dddb,
),
solus: (
link: #e6a556,
),
silang: (
link: #c65197,
)
);
@mixin theme-vars($theme-map) {
@each $name, $value in $theme-map {
--color-#{$name}: #{$value};
}
}
@mixin accent-vars($accent-map) {
@each $name, $value in $accent-map {
--color-#{$name}: #{$value};
}
}
:root {
@include theme-vars(map.get($themes, dark));
@include accent-vars(map.get($accents, katlum));
}
[data-theme="light"] {
@include theme-vars(map.get($themes, light));
}
[data-accent="katlum"] {
@include accent-vars(map.get($accents, katlum));
}
[data-accent="solus"] {
@include accent-vars(map.get($accents, solus));
}
[data-accent="silang"] {
@include accent-vars(map.get($accents, silang));
}

View File

@@ -37,4 +37,18 @@ a {
.error-link {
color: var(--error-link);
}
.window-wrapper {
display: flex;
align-items: center;
border: solid 1px var(--color-border);
/* opacity: 0; */
user-select: none;
-webkit-box-shadow: 0px 0px 10px -2px var(--shadow-color);
-moz-box-shadow: 0px 0px 10px -2px var(--shadow-color);
box-shadow: 0px 0px 10px -2px var(--shadow-color);
}

View File

@@ -0,0 +1,42 @@
<script setup>
import { TransitionGroup } from 'vue'
import { Windows, ReloadRef, WindowMap } from '@/services/Windows';
// Gestionem ventanas
const reload = ReloadRef();
const windows = Windows();
</script>
<template>
<div class="window-container" :key="reload">
<TransitionGroup name="window">
<component v-for="win in windows" :is="WindowMap()[win.type]" :key="win.id" :data="win"></component>
</TransitionGroup>
</div>
</template>
<style>
.window-enter-active,
.window-leave-active {
transition: all 0.15s ease;
}
.window-enter-from,
.window-leave-to {
opacity: 0;
transform: translateY(15px);
}
.window-wrapper {
background-color: var(--window-background);
/* backdrop-filter: blur(10px); */
position: fixed;
display: flex;
flex-direction: column;
}
</style>

View File

@@ -0,0 +1,44 @@
<script setup>
import { onMounted, ref } from 'vue';
import { SetupHandle, SetSize, ResetPosition } from '@/services/Windows';
import WindowHandle from './partials/WindowHandle.vue';
const handle = ref(null);
const props = defineProps(['data']);
const data = props.data;
let id = data.id;
const test = ref(null)
onMounted(() => {
SetupHandle(id, handle);
SetSize(id, {width: 500, height: 380});
ResetPosition(id, "center");
});
</script>
<template>
<div class="window-wrapper" :id="'window-wrapper-' + id">
<WindowHandle :window="id" ref="handle"></WindowHandle>
<!-- Body -->
<div ref="test">
<p>Hola</p>
</div>
</div>
</template>
<style scoped>
.window-wrapper {
display: flex;
align-items: center;
}
</style>

View File

@@ -0,0 +1,139 @@
<script setup>
import { onMounted, ref, watch } from 'vue';
import { GetWindowWithId, ClearWindow, Windows } from '@/services/Windows';
import ArrowLeftIcon from '/icons/iconoir/regular/arrow-left.svg';
import XMarkIcon from '/icons/iconoir/regular/xmark.svg';
import ResizeHandleIcon from '/icons/ui/resize-handle.svg';
const props = defineProps(['window', 'handleHeight', 'custom', 'color']);
const id = props.window;
const handleHeight = props.handleHeight;
const closeButton = ref(null);
const backButton = ref(null);
const title = ref("");
const close = ref(false);
const hasBack = ref(false);
const def = ref(true);
const resizable = ref(false);
let closeAction;
let backFunction;
function setupHandle() {
let win = GetWindowWithId(id);
if(win.title) title.value = win.title;
if(win.close){
close.value = true;
closeAction = win.close;
}
if(win.back) {
hasBack.value = true;
backFunction = win.back;
}
if(handleHeight) {
let handle = document.getElementById('window-handle-' + id);
handle.style.height = handleHeight;
}
// Setup sounds
let currentWindowId = "window-wrapper-" + id;
let currentWindow = document.getElementById(currentWindowId);
}
function CloseButton(){
const audio = new Audio('/sounds/close.wav');
audio.type = "audio/wav"
audio.play();
if(typeof closeAction === 'function') closeAction();
// ClearWindow(id)
}
onMounted(() => {
if(props.custom) def.value = false;
let win = GetWindowWithId(id);
watch(GetWindowWithId(id), () => {
resizable.value = win.resizable;
})
});
defineExpose({
setupHandle
});
</script>
<template>
<div class="window-handle" :id="'window-handle-' + id">
<div class="left" v-if="def">
<img class="icon icon-add-margin" :src="ArrowLeftIcon" draggable="false" ref="backButton" v-if="hasBack" v-on:click="backFunction">
</div>
<div class="center" v-if="def">
<span>{{ title }}</span>
</div>
<div class="right">
<img class="icon" :src="XMarkIcon" draggable="false" ref="closeButton" v-if="close" v-on:click="CloseButton">
</div>
<!-- span>{{ title }}</span>
-->
</div>
<div v-show="resizable" class="window-resize-handle" :id="'window-resize-handle-' + id">
<img :src="ResizeHandleIcon" draggable="false">
</div>
</template>
<style scoped lang="scss">
.window-resize-handle {
position: absolute;
filter: invert(0.8);
opacity: 0.6;
right: 0px;
bottom: 0px;
width: 18px;
height: 18px;
img {
width: 18px;
height: 18px;
}
z-index: 100;
}
.window-handle {
.left, .right {
flex: 1;
display: flex;
}
.left {
justify-content: left;
}
.right {
height: 24px;
justify-content: right;
}
span {
font-family: MrEavesRemake;
}
user-select: none;
justify-content: center;
width: 100%;
display: flex;
background-color: var(--color-handler);
}
</style>

View File

@@ -0,0 +1,301 @@
import { ref } from 'vue'
const windows = ref([]);
import LoginWindow from '~/components/windows/LoginWindow.vue';
let windowMap = {
login: LoginWindow
};
async function InjectWindow(window_type, plugin, window_component) {
let systemWidows = {};
systemWidows[window_type] = (await import(`../../plugins/${plugin}/views/${window_component}.vue`)).default;
windowMap = { ...windowMap, ...systemWidows };
}
// Presets
const defValues = {
'login': {
id: 'login',
title: 'Login',
}
}
const reload = ref(0);
let ReloadRef = () => { return reload };
let Windows = () => { return windows };
let WindowMap = () => { return windowMap };
let currentIndex = 10;
function SetupHandle(id, handle) {
// Update window info with handle info
let win = GetWindowWithId(id);
let currentWindowId = "window-wrapper-" + id;
let currentWindowHandleId = "window-handle-" + id;
let currentWindowResizerId = "window-resize-handle-" + id;
let draggingWindow = false;
let resizingWindow = false;
let currentWindow = document.getElementById(currentWindowId);
let handler = document.getElementById(currentWindowHandleId);
let resizer = document.getElementById(currentWindowResizerId);
let offsetX = 0;
let offsetY = 0;
let resizeOffsetX = 0;
let resizeOffsetY = 0;
// Programar un resizer mitjanament competent
currentWindow.addEventListener("mousedown", (event) => {
SetOnTop(id);
});
handler.addEventListener("mousedown", (event) => {
draggingWindow = true;
let windowRect = currentWindow.getBoundingClientRect();
offsetX = windowRect.left - event.clientX;
offsetY = windowRect.top - event.clientY;
})
// Move window listeners
document.addEventListener("mousemove", (event) => {
if (!draggingWindow) return;
if (event.clientX + offsetX < -currentWindow.getBoundingClientRect().width + 20) currentWindow.style.left = (-currentWindow.getBoundingClientRect().width + 20) + "px";
else if (event.clientX + offsetX > window.innerWidth - 20) currentWindow.style.left = (window.innerWidth - 20) + "px";
else currentWindow.style.left = (event.clientX + offsetX) + "px";
if (event.clientY + offsetY < 0) currentWindow.style.top = (0) + "px";
else if (event.clientY + offsetY > window.innerHeight - 20) currentWindow.style.top = (window.innerHeight - 20) + "px";
else currentWindow.style.top = (event.clientY + offsetY) + "px";
})
document.addEventListener("mouseup", (event) => {
draggingWindow = false;
// ummm suposo que no pots tancar mentres mous?
SaveWindowPos({ id, x: parseInt(currentWindow.style.left, 10), y: parseInt(currentWindow.style.top, 10) });
});
// Resize window listeners
resizer.addEventListener("mousedown", (event) => {
resizingWindow = true;
let windowRect = currentWindow.getBoundingClientRect();
resizeOffsetX = parseInt(currentWindow.style.width) - event.clientX;
resizeOffsetY = parseInt(currentWindow.style.height) - event.clientY;
});
document.addEventListener("mousemove", (event) => {
if (!resizingWindow) return;
let newWidth = event.clientX + resizeOffsetX;
let newHeight = event.clientY + resizeOffsetY;
if (win.minHeight) if (win.minHeight > newHeight) newHeight = win.minHeight;
if (win.maxHeight) if (win.maxHeight < newHeight) newHeight = win.maxHeight;
if (win.minWidth) if (win.minWidth > newWidth) newWidth = win.minWidth;
if (win.maxWidth) if (win.maxWidth < newWidth) newWidth = win.maxWidth;
currentWindow.style.width = newWidth + "px";
currentWindow.style.height = newHeight + "px";
});
document.addEventListener("mouseup", (event) => {
resizingWindow = false;
win.width = parseInt(currentWindow.style.width, 10);
win.height = parseInt(currentWindow.style.height, 10);
});
handle.value.setupHandle();
}
function SetResizable(id, resizable) {
let win = GetWindowWithId(id);
win.resizable = resizable;
}
function SetSize(id, size) {
let currentWindowId = "window-wrapper-" + id;
let currentWindow = document.getElementById(currentWindowId);
let win = GetWindowWithId(id);
currentWindow.style.width = size.width + "px";
currentWindow.style.height = size.height + "px";
win.width = size.width;
}
function SetMaxSize(id, maxSize) {
let win = GetWindowWithId(id);
if (maxSize.width) win.maxWidth = maxSize.width;
else win.maxWidth = win.width;
if (maxSize.height) win.maxHeight = maxSize.height;
else win.maxHeight = win.height;
}
function SetMinSize(id, minSize) {
let win = GetWindowWithId(id);
if (minSize.width) win.minWidth = minSize.width;
else win.minWidth = win.width;
if (minSize.height) win.minHeight = minSize.height;
else win.minHeight = win.height;
}
function SetPosition(id, pos) {
let currentWindowId = "window-wrapper-" + id;
let currentWindow = document.getElementById(currentWindowId);
if (pos == "center") {
let x = window.innerWidth / 2;
let y = window.innerHeight / 2;
currentWindow.style.left = (x - currentWindow.getBoundingClientRect().width / 2) + "px";
currentWindow.style.top = (y - currentWindow.getBoundingClientRect().height / 2) + "px";
return;
}
currentWindow.style.top = pos.y + "px";
currentWindow.style.left = pos.x + "px";
SaveWindowPos({ id, x: pos.x, y: pos.y })
}
function GetPosition(id) {
let win = GetWindowWithId(id);
return { x: win.x, y: win.y };
}
function ResetPosition(id, pos) {
let win = GetWindowWithId(id);
let data = { x: win.x, y: win.y };
if (data.x && data.y) {
SetPosition(id, data);
return;
}
SetPosition(id, pos);
}
function CreateWindow(type, data = {}) {
let finalData = { ...{ type }, ...defValues[type], ...data }
console.log(finalData);
let contains = false;
for (let i = 0; i < windows.value.length; i++) {
if (windows.value[i].id == finalData.id) {
contains = true;
break;
}
}
if (!contains) {
windows.value.push(finalData);
console.log("Pushed ", finalData.id);
// reload.value += 1;
console.log(windows.value);
setTimeout(() => {
SetOnTop(finalData.id);
if (finalData.create) finalData.create();
}, 0);
}
}
function CreateChildWindow(parentId, type, data = {}) {
let finalData = { ...{ type }, ...defValues[type], ...data }
let parent = GetWindowWithId(parentId);
if (parent.children) parent.children.push(finalData.id);
else parent.children = [finalData.id];
CreateWindow(type, data);
}
function ClearAll() {
Object.keys(windows).forEach((key) => {
windows.value = [];
});
}
function ClearWindows(data) {
for (let i = 0; i < windows.value.length; i++) {
ClearWindow(windows.value[i].id);
}
// reload.value += 1;
}
function ClearWindow(id) {
let win = GetWindowWithId(id);
if (!win) return;
if (win.children) for (let i = 0; i < win.children.length; i++) ClearWindow(win.children[i]);
windows.value = windows.value.filter((e) => { return e.id !== id });
// reload.value += 1;
}
function GetWindowWithId(id) {
for (let i = 0; i < windows.value.length; i++) {
if (windows.value[i].id == id) {
return windows.value[i];
}
}
}
function CallWindow(id, callableName, arg) {
let win = GetWindowWithId(id);
win[callableName](arg);
}
function SaveWindowPos(data) {
let win = GetWindowWithId(data.id);
if (win === undefined) return;
win.x = data.x;
win.y = data.y;
}
function SetOnTop(id) {
let currentWindowId = "window-wrapper-" + id;
let currentWindow = document.getElementById(currentWindowId);
currentIndex += 1;
currentWindow.style.zIndex = currentIndex;
}
export {
SetupHandle,
SetSize,
SetResizable,
SetMaxSize,
SetMinSize,
SetPosition,
ResetPosition,
Windows,
WindowMap,
InjectWindow,
ReloadRef,
ClearWindows,
CreateWindow,
CreateChildWindow,
CallWindow,
GetWindowWithId,
SaveWindowPos,
GetPosition,
ClearWindow,
ClearAll
}