Files
THOS-Server/index.php

980 lines
33 KiB
PHP
Raw Normal View History

2025-06-05 13:44:25 +02:00
<img?php $statePath='/home/surillya/.thos_state.json' ; $state=file_exists($statePath) ?
json_decode(file_get_contents($statePath), true) : null; if (!file_exists($statePath)) { header('Location: oobe.php');
exit(); } ?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SurillyaOS</title>
<style>
:root {
--accent: #4CAF50;
--window-bg: rgba(28, 28, 28, 0.6);
--window-border: rgba(255, 255, 255, 0.1);
--window-blur: blur(20px);
--header-bg: rgba(30, 30, 30, 0.6);
--header-color: white;
--header-hover: var(--accent);
}
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
.theme-glassy-dark {
--window-bg: rgba(28, 28, 28, 0.6);
--window-border: rgba(255, 255, 255, 0.1);
--window-blur: blur(20px);
--header-bg: rgba(30, 30, 30, 0.6);
--header-color: white;
--font-color: white;
}
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
.theme-frosted-blue {
--window-bg: rgba(0, 40, 80, 0.4);
--window-border: rgba(173, 216, 230, 0.2);
--window-blur: blur(25px);
--header-bg: rgba(10, 20, 50, 0.4);
--header-color: #e3f8ff;
--font-color: #e3f8ff;
}
.theme-dreamy-pink {
--window-bg: rgba(80, 30, 60, 0.5);
--window-border: rgba(255, 182, 193, 0.3);
--window-blur: blur(16px);
--header-bg: rgba(100, 40, 80, 0.5);
--header-color: #ffddee;
--font-color: #ffe9f4;
}
.theme-xp {
--window-bg: #ece9d8;
--window-border: #3a6ea5;
--window-blur: none;
--header-bg: linear-gradient(to bottom, #0a246a, #1c4dbd);
--header-color: white;
--font-color: black;
}
body {
margin: 0;
font-family: 'Inter', sans-serif;
background: #121212;
color: white;
overflow: hidden;
}
#app-bar {
position: fixed;
top: 0;
left: 0;
width: 200px;
height: 100vh;
background: var(--window-bg);
backdrop-filter: var(--window-blur);
-webkit-backdrop-filter: var(--window-blur);
border: 1px solid var(--window-border);
padding: 10px;
box-sizing: border-box;
z-index: 9999;
}
.app-btn {
display: block;
margin: 10px 0;
padding: 10px;
background: #292929;
border: none;
color: white;
cursor: pointer;
border-radius: 8px;
transition: background 0.3s ease;
}
.app-btn:hover {
background: var(--accent);
}
.window {
position: absolute;
width: 600px;
height: 400px;
background: var(--window-bg);
backdrop-filter: var(--window-blur);
-webkit-backdrop-filter: var(--window-blur);
border: 1px solid var(--window-border);
border-radius: 12px;
resize: both;
overflow: hidden;
z-index: 1;
transition: box-shadow 0.2s ease, backdrop-filter 0.3s ease;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
}
.window.active {
box-shadow: 0 0 20px var(--accent), 0 8px 24px rgba(0, 0, 0, 0.3);
}
.window-header {
background: var(--header-bg);
color: var(--header-color);
padding: 6px 12px;
cursor: grab;
display: flex;
align-items: center;
justify-content: space-between;
font-weight: bold;
backdrop-filter: var(--window-blur);
-webkit-backdrop-filter: var(--window-blur);
border-bottom: 1px solid var(--window-border);
}
.window-header:active {
cursor: grabbing;
}
.window-header button {
background: transparent;
border: none;
color: inherit;
font-weight: bold;
font-size: 1.1em;
cursor: pointer;
user-select: none;
padding: 0 6px;
transition: color 0.2s ease;
}
.window-header button:hover {
color: var(--header-hover);
}
.window-content {
background: var(--window-bg);
backdrop-filter: var(--window-blur);
-webkit-backdrop-filter: var(--window-blur);
border: 1px solid var(--window-border);
width: 100%;
height: calc(100% - 32px);
border: none;
}
.sparkle {
position: absolute;
width: 6px;
height: 6px;
background: radial-gradient(circle at center, var(--accent) 0%, transparent 70%);
border-radius: 50%;
filter: drop-shadow(0 0 6px var(--accent));
animation: sparkle 1.2s infinite ease-in-out;
pointer-events: none;
}
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
@keyframes sparkle {
0%,
100% {
opacity: 0;
transform: scale(0.5) rotate(0deg);
}
50% {
opacity: 1;
transform: scale(1) rotate(45deg);
}
}
#screensaver {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
border: none;
z-index: 9999;
}
#drag-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 99999;
display: none;
cursor: grabbing;
}
#fullscreen-toggle {
border: none;
background: transparent;
cursor: pointer;
backdrop-filter: blur(2px);
-webkit-backdrop-filter: blur(2px);
border-radius: 4px;
}
#fullscreen-toggle:hover {
color: var(--accent);
}
.grayscale-bg {
filter: grayscale(100%) brightness(0.9);
}
.notification {
display: flex;
flex-direction: column;
gap: 0.25rem;
padding: 1rem 1.25rem;
border-radius: 1rem;
border: 2px solid transparent;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
color: white;
min-width: 260px;
max-width: 340px;
pointer-events: auto;
animation: fadeInUp 0.3s ease-out;
position: relative;
transition: transform 0.2s ease, opacity 0.2s ease;
background: var(--window-bg);
backdrop-filter: var(--window-blur);
-webkit-backdrop-filter: var(--window-blur);
color: var(--accent);
}
.notification-dismiss {
background: transparent;
border: none;
cursor: pointer;
}
.notification.success {
border-color: #4caf50;
}
.notification.error {
border-color: #f44336;
}
.notification.info {
border-color: var(--accent, #00bcd4);
}
@keyframes slideIn {
from {
transform: translateX(100%) scale(0.95);
opacity: 0;
}
to {
transform: translateX(0) scale(1);
opacity: 1;
}
}
@keyframes fadeOut {
from {
opacity: 1;
transform: translateX(0) scale(1);
}
to {
opacity: 0;
transform: translateX(50%) scale(0.95);
}
}
</style>
<script src="tailwind.es"></script>
</head>
<body class="bg-black">
<div id="appWrapper" class="transition-all duration-300">
<div id="bg-image" class="fixed inset-0 z-[-10] bg-cover bg-center transition-all duration-300"></div>
<div id="app-bar" class="bg-[#1e1e1e] fixed top-0 left-0 w-52 h-screen p-2.5 z-50">
<div class="flex flex-col h-full">
<div class="relative mb-4">
<button id="powerButton" class="group relative transition duration-300">
<img src="THOS.png" alt="Power" class="w-12 h-12 transition-all duration-300 ease-in-out
group-hover:brightness-125
group-hover:scale-110
group-hover:drop-shadow-[0_0_10px_rgba(255,200,255,0.6)]
group-hover:saturate-150
cursor-pointer select-none">
2025-06-05 08:52:31 +02:00
</button>
2025-06-05 13:44:25 +02:00
</div>
<div class="grid grid-cols-3 gap-3 p-2 flex flex-col justify-between">
<button onclick="openApp('explorer.php', 'Explorer')" title="File Explorer"
class="p-2 rounded-lg bg-gray-800 hover:bg-gray-700 transition-all">
<svg class="w-6 h-6 text-blue-300 mx-auto" fill="none" stroke="currentColor" stroke-width="2"
viewBox="0 0 24 24">
<path d="M3 7l6-3 6 3 6-3v12l-6 3-6-3-6 3V7z" />
<path d="M9 4v12" />
<path d="M15 7v12" />
</svg>
</button>
<button onclick="openApp('tasks.php', 'Task Manager')" title="Task Manager"
class="p-2 rounded-lg bg-gray-800 hover:bg-gray-700 transition-all">
<svg class="w-6 h-6 text-red-300 mx-auto" fill="none" stroke="currentColor" stroke-width="2"
viewBox="0 0 24 24">
<rect x="3" y="3" width="7" height="7" rx="1" />
<rect x="14" y="3" width="7" height="7" rx="1" />
<rect x="3" y="14" width="7" height="7" rx="1" />
<rect x="14" y="14" width="7" height="7" rx="1" />
</svg>
</button>
<button onclick="openApp('https://surillya.com/thos/store', 'THOS Store')" title="THOS Store"
class="p-2 rounded-lg bg-gray-800 hover:bg-gray-700 transition-all">
<svg class="w-6 h-6 text-pink-300 mx-auto" fill="none" stroke="currentColor" stroke-width="2"
viewBox="0 0 24 24">
<path d="M4 9h16l-1.68 10.08a2 2 0 0 1-2 1.92H7.68a2 2 0 0 1-2-1.92L4 9Z" />
<path d="M8 11V8a4 4 0 0 1 8 0v3" />
</svg>
</button>
<button onclick="openApp('https://surillya.com/thos/search/thossearch.html', 'Internet', true)"
title="Internet" class="p-2 rounded-lg bg-gray-800 hover:bg-gray-700 transition-all">
<svg class="w-6 h-6 text-green-300 mx-auto" fill="none" stroke="currentColor" stroke-width="2"
viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10" />
<path d="M2 12h20" />
<path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
</svg>
</button>
<button onclick="openApp('settings.php', 'Settings')" title="Settings"
class="p-2 rounded-lg bg-gray-800 hover:bg-gray-700 transition-all">
<svg class="w-6 h-6 text-yellow-300 mx-auto" fill="none" stroke="currentColor" stroke-width="2"
viewBox="0 0 24 24">
<path d="M12 15a3 3 0 100-6 3 3 0 000 6z" />
<path
d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1
1.51V21a2 2 0 01-2 2 2 2 0 01-2-2v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0
010-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 01-2-2 2 2 0 012-2h.09A1.65 1.65 0 004.6 9a1.65
1.65 0 00-.33-1.82l-.06-.06a2 2 0 010-2.83 2 2 0 012.83 0l.06.06a1.65 1.65 0 001.82.33H9a1.65 1.65 0 001-1.51V3a2 2 0
012-2 2 2 0 012 2v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 0 2 2 0 010 2.83l-.06.06a1.65 1.65
0 00-.33 1.82V9a1.65 1.65 0 001.51 1H21a2 2 0 012 2 2 2 0 01-2 2h-.09a1.65 1.65 0 00-1.51 1z" />
</svg>
</button>
</div>
<div class="flex-grow"></div>
<div id="trdp"
class="grid grid-cols-2 gap-2 mt-4 p-2 bg-[#252525] rounded-lg overflow-y-auto p-2 justify-center items-center">
</div>
<div id="version-display-container" class="w-full flex justify-center items-center mt-4">
<div id="version-display" class="flex items-center gap-2 text-sm text-gray-400 select-none">
<button id="fullscreen-toggle" title="Toggle fullscreen"
class="text-white hover:text-[var(--accent)] transition-colors duration-200 text-xs p-1 rounded bg-transparent hover:bg-[var(--glass-bg-light)]">
</button>
<span class="text-[var(--accent)]">THOS</span><span id="version-number"></span>
</div>
2025-06-05 08:52:31 +02:00
</div>
</div>
</div>
2025-06-05 13:44:25 +02:00
<div id="desktop" class="ml-52 h-screen relative">
<div id="notification-container" class="fixed top-4 right-4 z-50 flex flex-col gap-3 pointer-events-none"></div>
</div>
<iframe id="screensaver" src="screensaver.html"></iframe>
<div id="drag-overlay"></div>
2025-06-05 08:52:31 +02:00
</div>
2025-06-05 13:44:25 +02:00
<div id="powerDialog" class="fixed inset-0 z-50 hidden flex items-center justify-center">
<div class="bg-black/30 rounded-2xl shadow-2xl p-6 w-80 backdrop-blur-sm text-center text-white">
<h2 class="text-xl font-semibold mb-6 tracking-wide">Power Options</h2>
<div class="space-y-3">
<button id="shutdownBtn"
class="w-full py-2 px-4 border border-dotted rounded-xl transition-all duration-200 text-sm bg-white/5 hover:bg-white/10 backdrop-blur-sm border-red-400 text-red-300 hover:text-red-100 hover:border-red-300">Shutdown</button>
<button id="restartBtn"
class="w-full py-2 px-4 border border-dotted rounded-xl transition-all duration-200 text-sm bg-white/5 hover:bg-white/10 backdrop-blur-sm border-yellow-400 text-yellow-300 hover:text-yellow-100 hover:border-yellow-300">Restart</button>
<button id="reloadBtn"
class="w-full py-2 px-4 border border-dotted rounded-xl transition-all duration-200 text-sm bg-white/5 hover:bg-white/10 backdrop-blur-sm border-green-400 text-green-300 hover:text-green-100 hover:border-green-300">Reload</button>
<button id="cancelBtn"
class="w-full py-2 px-4 border border-dotted rounded-xl transition-all duration-200 text-sm bg-white/5 hover:bg-white/10 backdrop-blur-sm border-white/40 text-white hover:text-white hover:border-white/80">Cancel</button>
</div>
2025-06-05 08:52:31 +02:00
</div>
</div>
2025-06-05 13:44:25 +02:00
<script>
window.THOS = {
version: '6 Build-25'
};
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
let timeoutSeconds = 180;
const thosState = <?= json_encode($state ?? ['cookies' => '', 'localData' => '{}']) ?>;
thosState.cookies?.split('; ').forEach(cookie => document.cookie = cookie);
const localItems = JSON.parse(thosState.localData || '{}');
for (let key in localItems) {
localStorage.setItem(key, localItems[key]);
2025-06-05 08:52:31 +02:00
}
2025-06-05 13:44:25 +02:00
if (!localStorage.getItem('thos_done')) {
window.location.href = 'oobe.php';
2025-06-05 08:52:31 +02:00
}
2025-06-05 13:44:25 +02:00
class WindowManager {
constructor(container) {
this.container = container;
this.zIndexCounter = 1;
this.draggingWindow = null;
this.resizingWindow = null;
this.resizeDir = null;
this.offsetX = 0;
this.offsetY = 0;
this.startWidth = 0;
this.startHeight = 0;
this.startLeft = 0;
this.startTop = 0;
this.isMaximized = new WeakMap();
this.activeWindow = null;
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
this.dragOverlay = document.getElementById('drag-overlay');
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
document.addEventListener('mousemove', this.onMouseMove.bind(this));
document.addEventListener('mouseup', this.onMouseUp.bind(this));
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
this.makeWindowsDraggable();
}
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
makeWindowsDraggable() {
const windows = this.container.querySelectorAll('.window');
windows.forEach(win => {
const header = win.querySelector('.window-header');
this.makeDraggable(win, header);
this.makeResizable(win);
this.setupButtons(win);
this.setupFocus(win);
});
}
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
setupFocus(win) {
win.addEventListener('mousedown', () => {
this.bringToFront(win);
this.setActiveWindow(win);
});
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
win.addEventListener('focus', () => {
this.bringToFront(win);
this.setActiveWindow(win);
});
}
setActiveWindow(win) {
if (this.activeWindow && this.activeWindow !== win) {
this.activeWindow.classList.remove('active');
2025-06-05 08:52:31 +02:00
}
2025-06-05 13:44:25 +02:00
this.activeWindow = win;
win.classList.add('active');
}
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
makeDraggable(win, header) {
header.style.cursor = 'grab';
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
header.addEventListener('mousedown', (e) => {
if (e.target.closest('button')) return;
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
e.preventDefault();
this.bringToFront(win);
this.setActiveWindow(win);
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
if (this.isMaximized.get(win)) {
win.style.transition = "all 0.2s ease";
win.style.left = win.dataset.prevLeft;
win.style.top = win.dataset.prevTop;
win.style.width = win.dataset.prevWidth;
win.style.height = win.dataset.prevHeight;
this.isMaximized.set(win, false);
}
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
this.draggingWindow = win;
this.offsetX = e.clientX - win.offsetLeft;
this.offsetY = e.clientY - win.offsetTop;
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
header.style.cursor = 'grabbing';
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
if (this.dragOverlay) {
this.dragOverlay.style.display = 'block';
this.dragOverlay.style.cursor = 'grabbing';
}
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
document.body.style.userSelect = 'none';
});
2025-06-05 08:52:31 +02:00
}
2025-06-05 13:44:25 +02:00
onMouseMove(e) {
if (this.draggingWindow) {
let newLeft = e.clientX - this.offsetX;
let newTop = e.clientY - this.offsetY;
const containerRect = this.container.getBoundingClientRect();
const winRect = this.draggingWindow.getBoundingClientRect();
newLeft = Math.max(containerRect.left, Math.min(newLeft, containerRect.right - winRect.width));
newTop = Math.max(containerRect.top, Math.min(newTop, containerRect.bottom - winRect.height));
this.draggingWindow.style.left = newLeft + 'px';
this.draggingWindow.style.top = newTop + 'px';
}
2025-06-05 08:52:31 +02:00
}
2025-06-05 13:44:25 +02:00
onMouseUp() {
if (this.draggingWindow) {
const header = this.draggingWindow.querySelector('.window-header');
if (header) header.style.cursor = 'grab';
this.draggingWindow = null;
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
if (this.dragOverlay) {
this.dragOverlay.style.display = 'none';
this.dragOverlay.style.cursor = '';
}
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
document.body.style.userSelect = '';
}
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
if (this.resizingWindow) {
this.resizingWindow = null;
this.resizeDir = null;
}
2025-06-05 08:52:31 +02:00
}
2025-06-05 13:44:25 +02:00
bringToFront(win) {
win.style.zIndex = ++this.zIndexCounter;
}
toggleMaximize(win) {
if (!this.isMaximized.get(win)) {
win.dataset.prevLeft = win.style.left;
win.dataset.prevTop = win.style.top;
win.dataset.prevWidth = win.style.width;
win.dataset.prevHeight = win.style.height;
win.style.left = "0px";
win.style.top = "0px";
win.style.width = "100%";
win.style.height = "100%";
win.style.transition = "all 0.2s ease";
this.isMaximized.set(win, true);
} else {
win.style.transition = "all 0.2s ease";
win.style.left = win.dataset.prevLeft;
win.style.top = win.dataset.prevTop;
win.style.width = win.dataset.prevWidth;
win.style.height = win.dataset.prevHeight;
this.isMaximized.set(win, false);
2025-06-05 08:52:31 +02:00
}
2025-06-05 13:44:25 +02:00
}
}
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
const tasks = [];
let z = 2;
const wm = new WindowManager(document.body);
const appBar = document.getElementById('trdp');
const contextMenu = document.createElement('div');
contextMenu.id = 'context-menu';
contextMenu.style.position = 'absolute';
contextMenu.style.display = 'none';
contextMenu.style.background = '#222';
contextMenu.style.border = '1px solid #444';
contextMenu.style.borderRadius = '6px';
contextMenu.style.padding = '6px';
contextMenu.style.zIndex = '9999';
document.body.appendChild(contextMenu);
document.addEventListener('click', () => contextMenu.style.display = 'none');
async function loadApps() {
const res = await fetch('apps.php');
const apps = await res.json();
console.log(apps);
appBar.innerHTML = '';
apps.forEach(app => {
const wrapper = document.createElement('div');
wrapper.classList.add('wrapper');
wrapper.style.alignItems = 'center';
wrapper.style.height = '50px';
wrapper.style.width = '50px';
wrapper.style.margin = '8px';
wrapper.style.transition = 'transform 0.3s ease';
const btn = document.createElement('img');
btn.src = app.icon;
btn.alt = app.name;
btn.title = app.name;
btn.style.height = '50px';
btn.style.width = '50px';
btn.style.borderRadius = '16px';
btn.style.border = '2px solid #ffffff22';
2025-06-05 08:52:31 +02:00
btn.style.boxShadow = '0 2px 10px rgba(255, 192, 203, 0.25)';
2025-06-05 13:44:25 +02:00
btn.style.background = 'linear-gradient(135deg, #222, #1a1a1a)';
btn.style.padding = '6px';
btn.style.transition = 'all 0.3s ease';
btn.style.cursor = 'pointer';
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
btn.onmouseenter = () => {
wrapper.style.transform = 'scale(1.08)';
btn.style.borderColor = 'var(--accent)';
btn.style.boxShadow = '0 4px 16px var(--accent), 0 0 8px var(--accent)';
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
for (let i = 0; i < 6; i++) {
const sparkle = document.createElement('div');
sparkle.classList.add('sparkle');
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
sparkle.style.top = `${Math.random() * 100}%`;
sparkle.style.left = `${Math.random() * 100}%`;
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
sparkle.style.animationDelay = `${Math.random() * 1.5}s`;
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
wrapper.appendChild(sparkle);
sparkle.addEventListener('animationiteration', () => sparkle.remove());
}
};
btn.onmouseleave = () => {
wrapper.style.transform = 'scale(1)';
btn.style.borderColor = '#ffffff22';
btn.style.boxShadow = '0 2px 10px rgba(255, 192, 203, 0.25)';
const sparkles = wrapper.querySelectorAll('.sparkle');
sparkles.forEach(s => s.remove());
};
btn.onclick = () => openApp(app.path, app.name);
wrapper.appendChild(btn);
wrapper.oncontextmenu = e => {
e.preventDefault();
showContextMenu(e.pageX, e.pageY, app);
};
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
appBar.appendChild(wrapper);
});
}
function showContextMenu(x, y, app) {
contextMenu.innerHTML = `
2025-06-05 08:52:31 +02:00
<div onclick="uninstallApp('${app.id}')">🗑️ Uninstall</div>
`;
2025-06-05 13:44:25 +02:00
contextMenu.style.left = `${x}px`;
contextMenu.style.top = `${y}px`;
contextMenu.style.display = 'block';
}
function openApp(url, title, sandbox = false) {
const id = 'win_' + Date.now();
const win = document.createElement('div');
win.className = 'window';
win.style.top = Math.random() * 300 + 'px';
win.style.left = Math.random() * 400 + 'px';
win.style.zIndex = z++;
win.setAttribute('data-id', id);
win.innerHTML = `
2025-06-05 08:52:31 +02:00
<div class="window-header">
<span class="window-title">${title}</span>
<div class="window-controls">
<button class="max-btn" title="Maximize/Restore">🗖</button>
<button class="close-btn" title="Close">×</button>
</div>
</div>
`;
2025-06-05 13:44:25 +02:00
if (!sandbox) {
win.innerHTML += `<iframe src="${url}" class="window-content" allowtransparency="true"></iframe>`;
} else {
win.innerHTML += `<iframe src="${url}" sandbox="allow-scripts allow-forms allow-same-origin" class="window-content" allowtransparency="true"></iframe>`;
2025-06-05 08:52:31 +02:00
}
2025-06-05 13:44:25 +02:00
document.getElementById('desktop').appendChild(win);
tasks.push({ id, title, url });
let header = win.querySelector(".window-header");
wm.makeDraggable(win, header);
header.querySelector('.max-btn').addEventListener('click', (e) => {
e.stopPropagation();
wm.toggleMaximize(win);
});
header.querySelector('.close-btn').addEventListener('click', (e) => {
e.stopPropagation();
closeApp(id);
});
2025-06-05 08:52:31 +02:00
}
2025-06-05 13:44:25 +02:00
function closeApp(id) {
const win = document.querySelector(`.window[data-id='${id}']`);
if (win) win.remove();
const idx = tasks.findIndex(t => t.id === id);
if (idx !== -1) tasks.splice(idx, 1);
}
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
function focusApp(id) {
const win = document.querySelector(`.window[data-id='${id}']`);
if (win) win.style.zIndex = z++;
}
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
function uninstallApp(appId) {
if (!confirm("Are you sure you want to uninstall this app?")) return;
fetch(`uninstaller.php?id=${encodeURIComponent(appId)}`)
.then(res => res.text())
.then(() => loadApps());
2025-06-05 08:52:31 +02:00
}
2025-06-05 13:44:25 +02:00
loadApps();
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
window.addEventListener('message', async (event) => {
const { type, appId, packageFileUrl } = event.data || {};
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
if (type === 'THOS_INSTALL_APP') {
try {
const isValid = /^https:\/\/surillya\.com\//.test(packageFileUrl);
const fileName = packageFileUrl.split('/').pop().split('?')[0];
const outputPath = `/home/surillya/.temp/${fileName}`;
// I HATE UNDERSCORES!!!!
// underscore =/= dash
const url = `/install_app.php?url=${encodeURIComponent(packageFileUrl)}&output=${encodeURIComponent(outputPath)}&verified=${isValid ? 1 : 0}`;
const response = await fetch(url);
const result = await response.json();
if (!result.success) throw new Error(result.message);
const appUrl = `thp.php?q=${encodeURIComponent(result.filename)}&v=${result.verified}`;
window.openApp(appUrl, 'THOS Package Installer');
} catch (err) {
console.error('[THOS] App install failed:', err);
alert('Failed to install app.');
}
}
});
window.getTaskList = () => JSON.parse(JSON.stringify(tasks));
window.focusApp = focusApp;
window.closeApp = closeApp;
window.openApp = openApp;
window.reloadApps = loadApps;
window.saveTHOSState = saveTHOSState;
function applySettings(settings) {
console.log(settings);
function applyTheme(theme) {
document.body.className = theme;
}
const savedTheme = settings.theme;
if (savedTheme) applyTheme(savedTheme);
document.documentElement.style.setProperty('--accent', settings.accentColor || '#ff69b4');
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
if (settings.wallpaper) {
document.getElementById('bg-image').style.background = `url('file.php?q=${settings.wallpaper}') center/cover`;
2025-06-05 08:52:31 +02:00
} else {
2025-06-05 13:44:25 +02:00
document.getElementById('bg-image').style.background = '';
2025-06-05 08:52:31 +02:00
}
2025-06-05 13:44:25 +02:00
const audio = document.getElementById('bgMusic');
if (audio) {
if (settings.musicEnabled === 'on') {
document.addEventListener("click", function () {
audio.src = `file.php?q=${settings.music}`;
audio.volume = parseFloat(settings.musicVolume || 0.3);
audio.play().catch(() => {
alert('An error occured while starting background music. Please turn background music off, or enable autoplay.');
});
}, { once: true });
} else {
audio.pause;
}
}
timeoutSeconds = settings.screenTimeout ?? timeoutSeconds;
window.THOS = Object.assign(window.THOS || {}, {
getAllSettings() {
return settings;
}
});
saveTHOSState();
2025-06-05 08:52:31 +02:00
}
2025-06-05 13:44:25 +02:00
const savedSettings = JSON.parse(localStorage.getItem('settings')) || {};
applySettings(savedSettings);
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
window.addEventListener('message', (event) => {
if (event.data?.type === 'applySettings') {
applySettings(event.data.settings);
2025-06-05 08:52:31 +02:00
}
});
2025-06-05 13:44:25 +02:00
function saveTHOSState() {
const cookies = document.cookie;
const localData = JSON.stringify(localStorage);
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
fetch('save_state.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ cookies, localData })
});
2025-06-05 08:52:31 +02:00
}
2025-06-05 13:44:25 +02:00
setInterval(saveTHOSState, 60000);
window.addEventListener('beforeunload', saveTHOSState);
const powerButton = document.getElementById('powerButton');
const powerDialog = document.getElementById('powerDialog');
const cancelBtn = document.getElementById('cancelBtn');
const shutdownBtn = document.getElementById('shutdownBtn');
const restartBtn = document.getElementById('restartBtn');
const reloadBtn = document.getElementById('reloadBtn');
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
powerButton.addEventListener('click', () => {
document.getElementById("appWrapper").classList.add("grayscale-bg");
powerDialog.classList.remove('hidden');
2025-06-05 08:52:31 +02:00
});
2025-06-05 13:44:25 +02:00
cancelBtn.addEventListener('click', () => {
2025-06-05 08:52:31 +02:00
document.getElementById("appWrapper").classList.remove("grayscale-bg");
powerDialog.classList.add('hidden');
2025-06-05 13:44:25 +02:00
});
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
shutdownBtn.addEventListener('click', () => {
sendCommand('shutdown');
document.getElementById("appWrapper").classList.remove("grayscale-bg");
powerDialog.classList.add('hidden');
});
restartBtn.addEventListener('click', () => {
sendCommand('reboot');
document.getElementById("appWrapper").classList.remove("grayscale-bg");
powerDialog.classList.add('hidden');
});
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
reloadBtn.addEventListener('click', () => {
location.reload();
});
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
powerDialog.addEventListener('click', (e) => {
if (e.target === powerDialog) {
document.getElementById("appWrapper").classList.remove("grayscale-bg");
powerDialog.classList.add('hidden');
}
});
function sendCommand(action) {
fetch(`server_command.php?action=${encodeURIComponent(action)}`)
.then(res => res.text())
.then(result => {
alert("Server response: " + result);
})
.catch(err => {
alert("Failed to send command: " + err);
});
2025-06-05 08:52:31 +02:00
}
2025-06-05 13:44:25 +02:00
document.getElementById('version-number').textContent = window.THOS.version;
document.getElementById("fullscreen-toggle").addEventListener("click", () => {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen().catch(err => {
console.error(`Failed to enter fullscreen: ${err.message}`);
});
} else {
document.exitFullscreen();
}
});
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
const screensaver = document.getElementById("screensaver");
let screensaverTimeout;
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
function showScreensaver() {
screensaver.style.display = "block";
2025-06-05 08:52:31 +02:00
}
2025-06-05 13:44:25 +02:00
function hideScreensaver() {
if (screensaver.style.display === "block") {
screensaver.contentWindow.postMessage({ type: 'screensaver_hidden' }, '*'); // optional
}
screensaver.style.display = "none";
}
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
function resetInactivityTimer() {
hideScreensaver();
clearTimeout(screensaverTimeout);
screensaverTimeout = setTimeout(showScreensaver, timeoutSeconds * 1000);
2025-06-05 08:52:31 +02:00
}
2025-06-05 13:44:25 +02:00
['mousemove', 'mousedown', 'keydown', 'scroll', 'touchstart', 'wheel'].forEach(event => {
document.addEventListener(event, resetInactivityTimer, { passive: true });
});
window.addEventListener("message", event => {
if (event.data?.type === "user_active") {
resetInactivityTimer();
}
});
resetInactivityTimer();
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
function notify(title, description = "", options = {}) {
const {
type = "info",
timeout = 5000,
icon = null,
id = `notif-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
} = options;
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
const container = document.getElementById("notification-container");
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
const notification = document.createElement("div");
notification.className = `notification ${type}`;
notification.id = id;
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
notification.innerHTML = `
2025-06-05 08:52:31 +02:00
${icon ? `<div class="text-xl">${icon}</div>` : ""}
<div class="flex-1">
2025-06-05 13:44:25 +02:00
<div class="notification-title font-semibold">${title}</div>
${description ? `<div class="notification-desc text-sm text-gray-400">${description}</div>` : ""}
2025-06-05 08:52:31 +02:00
</div>
<button class="notification-dismiss absolute top-2 right-2 text-white/50 hover:text-white transition-colors text-sm" aria-label="Dismiss">&times;</button>
`;
2025-06-05 13:44:25 +02:00
const dismiss = () => {
notification.style.animation = "fadeOut 0.25s ease-out forwards";
setTimeout(() => {
if (container.contains(notification)) container.removeChild(notification);
}, 250);
};
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
notification.querySelector(".notification-dismiss").onclick = dismiss;
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
container.appendChild(notification);
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
if (timeout > 0) {
setTimeout(() => {
if (container.contains(notification)) dismiss();
}, timeout);
}
2025-06-05 08:52:31 +02:00
}
2025-06-05 13:44:25 +02:00
window.notify = notify;
</script>
</body>
2025-06-05 08:52:31 +02:00
2025-06-05 13:44:25 +02:00
</html>