Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30a5073658 |
23
api/save_snippet.php
Normal file
23
api/save_snippet.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
echo json_encode(['success' => false, 'error' => 'Invalid method']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$content = $_POST['content'] ?? '';
|
||||
|
||||
if (empty($content)) {
|
||||
echo json_encode(['success' => false, 'error' => 'Content is empty']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$stmt = db()->prepare("INSERT INTO snippets (content) VALUES (?)");
|
||||
$stmt->execute([$content]);
|
||||
echo json_encode(['success' => true]);
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
@ -1,302 +1,193 @@
|
||||
:root {
|
||||
--bg-color: #050a14;
|
||||
--accent-color: #00e5ff;
|
||||
--accent-dim: rgba(0, 229, 255, 0.15);
|
||||
--accent-glow: rgba(0, 229, 255, 0.3);
|
||||
--text-primary: #e0faff;
|
||||
--font-mono: 'JetBrains Mono', monospace;
|
||||
--border-radius: 4px;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
body {
|
||||
background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
|
||||
background-size: 400% 400%;
|
||||
animation: gradient 15s ease infinite;
|
||||
color: #212529;
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
background: var(--bg-color);
|
||||
color: var(--text-primary);
|
||||
font-family: var(--font-mono);
|
||||
overflow: hidden;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.main-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@keyframes gradient {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-container {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 85vh;
|
||||
box-shadow: 0 20px 40px rgba(0,0,0,0.2);
|
||||
backdrop-filter: blur(15px);
|
||||
-webkit-backdrop-filter: blur(15px);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chat-header {
|
||||
padding: 1.5rem;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
font-weight: 700;
|
||||
font-size: 1.1rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.chat-messages {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.25rem;
|
||||
}
|
||||
|
||||
/* Custom Scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.message {
|
||||
max-width: 85%;
|
||||
padding: 0.85rem 1.1rem;
|
||||
border-radius: 16px;
|
||||
line-height: 1.5;
|
||||
font-size: 0.95rem;
|
||||
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
|
||||
animation: fadeIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(20px) scale(0.95); }
|
||||
to { opacity: 1; transform: translateY(0) scale(1); }
|
||||
}
|
||||
|
||||
.message.visitor {
|
||||
align-self: flex-end;
|
||||
background: linear-gradient(135deg, #212529 0%, #343a40 100%);
|
||||
color: #fff;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
.message.bot {
|
||||
align-self: flex-start;
|
||||
background: #ffffff;
|
||||
color: #212529;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
|
||||
.chat-input-area {
|
||||
padding: 1.25rem;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.chat-input-area form {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.chat-input-area input {
|
||||
flex: 1;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 12px;
|
||||
padding: 0.75rem 1rem;
|
||||
outline: none;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.chat-input-area input:focus {
|
||||
border-color: #23a6d5;
|
||||
box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.2);
|
||||
}
|
||||
|
||||
.chat-input-area button {
|
||||
background: #212529;
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.chat-input-area button:hover {
|
||||
background: #000;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
/* Background Animations */
|
||||
.bg-animations {
|
||||
/* Blueprint Grid Background */
|
||||
.blueprint-grid {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 0;
|
||||
overflow: hidden;
|
||||
top: 0; left: 0; width: 100%; height: 100%;
|
||||
background-image:
|
||||
linear-gradient(rgba(0, 229, 255, 0.05) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(0, 229, 255, 0.05) 1px, transparent 1px);
|
||||
background-size: 50px 50px;
|
||||
z-index: -2;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.blob {
|
||||
position: absolute;
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 50%;
|
||||
filter: blur(80px);
|
||||
animation: move 20s infinite alternate cubic-bezier(0.45, 0, 0.55, 1);
|
||||
.background-svg {
|
||||
position: fixed;
|
||||
top: 0; left: 0; width: 100%; height: 100%;
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.blob-1 {
|
||||
top: -10%;
|
||||
left: -10%;
|
||||
background: rgba(238, 119, 82, 0.4);
|
||||
}
|
||||
|
||||
.blob-2 {
|
||||
bottom: -10%;
|
||||
right: -10%;
|
||||
background: rgba(35, 166, 213, 0.4);
|
||||
animation-delay: -7s;
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
.blob-3 {
|
||||
top: 40%;
|
||||
left: 30%;
|
||||
background: rgba(231, 60, 126, 0.3);
|
||||
animation-delay: -14s;
|
||||
width: 450px;
|
||||
height: 450px;
|
||||
}
|
||||
|
||||
@keyframes move {
|
||||
0% { transform: translate(0, 0) rotate(0deg) scale(1); }
|
||||
33% { transform: translate(150px, 100px) rotate(120deg) scale(1.1); }
|
||||
66% { transform: translate(-50px, 200px) rotate(240deg) scale(0.9); }
|
||||
100% { transform: translate(0, 0) rotate(360deg) scale(1); }
|
||||
}
|
||||
|
||||
.admin-link {
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.admin-link:hover {
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Admin Styles */
|
||||
.admin-container {
|
||||
max-width: 900px;
|
||||
margin: 3rem auto;
|
||||
padding: 2.5rem;
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border-radius: 24px;
|
||||
box-shadow: 0 20px 50px rgba(0,0,0,0.15);
|
||||
border: 1px solid rgba(255, 255, 255, 0.4);
|
||||
/* Layout */
|
||||
.blueprint-viewport {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 95vw;
|
||||
height: 90vh;
|
||||
border: 1px solid rgba(0, 229, 255, 0.1);
|
||||
background: rgba(0, 20, 40, 0.4);
|
||||
backdrop-filter: blur(20px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.admin-container h1 {
|
||||
margin-top: 0;
|
||||
color: #212529;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.table {
|
||||
.main-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0 8px;
|
||||
margin-top: 1.5rem;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.table th {
|
||||
/* Unified Arc Wrapper */
|
||||
.arc-keys-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.arc-center-hub {
|
||||
position: absolute;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
border: 1px solid var(--accent-color);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: radial-gradient(circle, var(--accent-dim) 0%, transparent 70%);
|
||||
box-shadow: 0 0 30px var(--accent-glow);
|
||||
z-index: 5;
|
||||
/* Positioned centrally at the bottom, or centrally with offset - to be handled by JS */
|
||||
}
|
||||
|
||||
.hub-label {
|
||||
font-size: 1rem;
|
||||
font-weight: 800;
|
||||
color: var(--accent-color);
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
/* Key Nodes */
|
||||
.key-node {
|
||||
position: absolute; /* Crucial for polar coordinate placement */
|
||||
border: 1px solid rgba(0, 229, 255, 0.3);
|
||||
color: var(--text-primary);
|
||||
font-size: 0.8rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
background: rgba(0, 229, 255, 0.03);
|
||||
transition: all 0.15s ease-out;
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
user-select: none;
|
||||
border-radius: var(--border-radius);
|
||||
transform-origin: center center;
|
||||
}
|
||||
|
||||
.key-node:hover {
|
||||
background: rgba(0, 229, 255, 0.15);
|
||||
border-color: var(--accent-color);
|
||||
box-shadow: 0 0 15px var(--accent-glow);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.key-node:active {
|
||||
background: var(--accent-color);
|
||||
color: var(--bg-color);
|
||||
}
|
||||
|
||||
/* Terminal Output */
|
||||
.integrated-output {
|
||||
height: 120px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
border: 1px solid rgba(0, 229, 255, 0.3);
|
||||
border-radius: var(--border-radius);
|
||||
padding: 12px;
|
||||
margin-top: 20px;
|
||||
position: relative;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
#outputArea {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 1rem;
|
||||
color: #6c757d;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.table td {
|
||||
background: #fff;
|
||||
padding: 1rem;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.table tr td:first-child { border-radius: 12px 0 0 12px; }
|
||||
.table tr td:last-child { border-radius: 0 12px 12px 0; }
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 0.75rem 1rem;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 12px;
|
||||
background: #fff;
|
||||
transition: all 0.3s ease;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
color: var(--accent-color);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 1rem;
|
||||
resize: none;
|
||||
outline: none;
|
||||
border-color: #23a6d5;
|
||||
box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.1);
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--accent-dim) transparent;
|
||||
}
|
||||
|
||||
/* Branding & Info */
|
||||
.branding {
|
||||
position: absolute;
|
||||
color: var(--accent-color);
|
||||
font-weight: 800;
|
||||
letter-spacing: 2px;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.branding-top-left { top: 15px; left: 15px; font-size: 0.9rem; }
|
||||
.branding-bottom-right { bottom: 15px; right: 15px; font-size: 0.7rem; opacity: 0.6; }
|
||||
|
||||
.global-controls {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
background: transparent;
|
||||
border: 1px solid var(--accent-color);
|
||||
color: var(--accent-color);
|
||||
font-size: 0.75rem;
|
||||
padding: 6px 16px;
|
||||
cursor: pointer;
|
||||
border-radius: 2px;
|
||||
transition: 0.2s;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.control-btn:hover {
|
||||
background: var(--accent-color);
|
||||
color: var(--bg-color);
|
||||
}
|
||||
|
||||
.system-time {
|
||||
font-weight: 400;
|
||||
margin-left: 10px;
|
||||
}
|
||||
@ -1,39 +1,212 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const chatForm = document.getElementById('chat-form');
|
||||
const chatInput = document.getElementById('chat-input');
|
||||
const chatMessages = document.getElementById('chat-messages');
|
||||
/**
|
||||
* Stark Industries - Full Arc Keyboard MK.83
|
||||
* Designed for ergonomic polar-coordinate typing experience.
|
||||
*/
|
||||
|
||||
const appendMessage = (text, sender) => {
|
||||
const msgDiv = document.createElement('div');
|
||||
msgDiv.classList.add('message', sender);
|
||||
msgDiv.textContent = text;
|
||||
chatMessages.appendChild(msgDiv);
|
||||
chatMessages.scrollTop = chatMessages.scrollHeight;
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const arcContainer = document.getElementById('arcKeysContainer');
|
||||
const svg = document.getElementById('backgroundSvg');
|
||||
const outputArea = document.getElementById('outputArea');
|
||||
const systemTimeLabel = document.getElementById('systemTime');
|
||||
const hub = document.getElementById('arcCenterHub');
|
||||
|
||||
// Remove any existing SVG content
|
||||
svg.innerHTML = '';
|
||||
|
||||
// --- Layout Configuration ---
|
||||
// The center of the concentric arcs
|
||||
let ARC_CENTER_X = window.innerWidth / 2;
|
||||
let ARC_CENTER_Y = window.innerHeight * 0.8;
|
||||
|
||||
// Define rings from closest to furthest. Angles are in degrees. 0 is pointing right, 180 is pointing left, 270 is pointing up.
|
||||
// We want the arc to go from roughly 190 degrees to 350 degrees
|
||||
// Actually, mathematically, if we use standard unit circle:
|
||||
// right = 0, up = -90 / 270, left = 180, down = 90
|
||||
// So for an arch over the center, we want angles between roughly 190 and 350 degrees.
|
||||
|
||||
// We will organize keys into rings, from bottom (closest to hub) to top (furthest).
|
||||
const RINGS = [
|
||||
{
|
||||
radius: 200,
|
||||
keys: ['SPACE'],
|
||||
// for space, we just put it at top-center (-90 degrees)
|
||||
angleRange: [-90, -90],
|
||||
keyWidth: 280,
|
||||
keyHeight: 50
|
||||
},
|
||||
{
|
||||
radius: 280,
|
||||
keys: ['Z', 'X', 'C', 'V', 'B', 'N', 'M', ',', '.', '/'],
|
||||
angleRange: [205, 335],
|
||||
keyWidth: 50,
|
||||
keyHeight: 50
|
||||
},
|
||||
{
|
||||
radius: 360,
|
||||
keys: ['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', "'", 'ENTER'],
|
||||
angleRange: [200, 340],
|
||||
keyWidth: 50,
|
||||
keyHeight: 50
|
||||
},
|
||||
{
|
||||
radius: 440,
|
||||
keys: ['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '[', ']', '\\'],
|
||||
angleRange: [195, 345],
|
||||
keyWidth: 50,
|
||||
keyHeight: 50
|
||||
},
|
||||
{
|
||||
radius: 520,
|
||||
keys: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 'BKSP'],
|
||||
angleRange: [190, 350],
|
||||
keyWidth: 50,
|
||||
keyHeight: 50
|
||||
},
|
||||
{
|
||||
radius: 600,
|
||||
keys: ['ESC', 'FILE', 'PROG', 'SYS', 'LOG', 'CALC', 'DATA', 'NET', 'PWR'],
|
||||
angleRange: [185, 355],
|
||||
keyWidth: 60,
|
||||
keyHeight: 40
|
||||
}
|
||||
];
|
||||
|
||||
// --- Helper Functions ---
|
||||
const toRad = (deg) => deg * (Math.PI / 180);
|
||||
|
||||
function createKeyNode(label, parent) {
|
||||
const btn = document.createElement('div');
|
||||
btn.className = 'key-node';
|
||||
btn.textContent = label;
|
||||
|
||||
btn.addEventListener('click', () => {
|
||||
if (label === 'SPACE') outputArea.value += ' ';
|
||||
else if (label === 'ENTER') outputArea.value += '\n';
|
||||
else if (label === 'BKSP') outputArea.value = outputArea.value.slice(0, -1);
|
||||
else if (label.length > 1) outputArea.value += `\n>> [SYSTEM] EXECUTE_${label}\n`;
|
||||
else outputArea.value += label;
|
||||
outputArea.scrollTop = outputArea.scrollHeight;
|
||||
});
|
||||
|
||||
parent.appendChild(btn);
|
||||
return btn;
|
||||
}
|
||||
|
||||
function drawRings() {
|
||||
const rect = arcContainer.getBoundingClientRect();
|
||||
// Since arcContainer is inside blueprint-viewport which has padding, its size might not match window perfectly.
|
||||
// It's safer to base centers on the arcContainer's dimensions.
|
||||
ARC_CENTER_X = rect.width / 2;
|
||||
ARC_CENTER_Y = rect.height - 50; // Near bottom
|
||||
|
||||
// Position Hub
|
||||
hub.style.left = `${ARC_CENTER_X - 100}px`;
|
||||
hub.style.top = `${ARC_CENTER_Y - 100}px`;
|
||||
|
||||
RINGS.forEach((ring) => {
|
||||
// Draw SVG guide arc
|
||||
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
||||
const startRad = toRad(ring.angleRange[0]);
|
||||
const endRad = toRad(ring.angleRange[1]);
|
||||
|
||||
// To draw arc in SVG, we need absolute coordinates relative to document/svg.
|
||||
// SVG is fixed over entire window.
|
||||
const svgRect = svg.getBoundingClientRect();
|
||||
// Offset arcContainer's coordinates to SVG window coordinates
|
||||
const globalCenterX = rect.left + ARC_CENTER_X;
|
||||
const globalCenterY = rect.top + ARC_CENTER_Y;
|
||||
|
||||
const x1 = globalCenterX + ring.radius * Math.cos(startRad);
|
||||
const y1 = globalCenterY + ring.radius * Math.sin(startRad);
|
||||
const x2 = globalCenterX + ring.radius * Math.cos(endRad);
|
||||
const y2 = globalCenterY + ring.radius * Math.sin(endRad);
|
||||
|
||||
// Large arc flag = 0 if angle difference < 180, 1 if > 180
|
||||
// Since our range is e.g. 190 to 350 (160 deg diff), largeArcFlag = 0
|
||||
const largeArcFlag = 0;
|
||||
const sweepFlag = 1; // 1 means clockwise from start to end
|
||||
|
||||
const d = `M ${x1} ${y1} A ${ring.radius} ${ring.radius} 0 ${largeArcFlag} ${sweepFlag} ${x2} ${y2}`;
|
||||
path.setAttribute('d', d);
|
||||
path.setAttribute('fill', 'none');
|
||||
path.setAttribute('stroke', 'rgba(0, 229, 255, 0.15)');
|
||||
path.setAttribute('stroke-width', '1');
|
||||
path.setAttribute('stroke-dasharray', '5,5');
|
||||
svg.appendChild(path);
|
||||
|
||||
// Calculate angle step
|
||||
let angleStep = 0;
|
||||
if (ring.keys.length > 1) {
|
||||
angleStep = (ring.angleRange[1] - ring.angleRange[0]) / (ring.keys.length - 1);
|
||||
}
|
||||
|
||||
ring.keys.forEach((key, kIdx) => {
|
||||
const angle = ring.keys.length === 1
|
||||
? (ring.angleRange[0] + ring.angleRange[1])/2
|
||||
: ring.angleRange[0] + (kIdx * angleStep);
|
||||
|
||||
const rad = toRad(angle);
|
||||
|
||||
// Position relative to arcContainer
|
||||
const x = ARC_CENTER_X + ring.radius * Math.cos(rad);
|
||||
const y = ARC_CENTER_Y + ring.radius * Math.sin(rad);
|
||||
|
||||
const btn = createKeyNode(key, arcContainer);
|
||||
btn.style.width = `${ring.keyWidth}px`;
|
||||
btn.style.height = `${ring.keyHeight}px`;
|
||||
|
||||
btn.style.left = `${x}px`;
|
||||
btn.style.top = `${y}px`;
|
||||
|
||||
// Rotate key to face center.
|
||||
// Since angle is right=0, up=-90, tangent angle is angle + 90
|
||||
const rotation = angle + 90;
|
||||
btn.style.transform = `translate(-50%, -50%) rotate(${rotation}deg)`;
|
||||
|
||||
// Handle special width buttons
|
||||
if (key === 'ENTER' || key === 'BKSP') {
|
||||
btn.style.width = '80px';
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function updateSystemTime() {
|
||||
const now = new Date();
|
||||
const timeStr = now.toLocaleTimeString('en-US', { hour12: false });
|
||||
if (systemTimeLabel) systemTimeLabel.textContent = `[ ${timeStr} ]`;
|
||||
}
|
||||
|
||||
// --- Init ---
|
||||
// Debounce resize handling
|
||||
let resizeTimeout;
|
||||
window.addEventListener('resize', () => {
|
||||
clearTimeout(resizeTimeout);
|
||||
resizeTimeout = setTimeout(() => {
|
||||
// Re-render
|
||||
svg.innerHTML = '';
|
||||
// keep the hub, but remove keys
|
||||
Array.from(arcContainer.children).forEach(child => {
|
||||
if (child.id !== 'arcCenterHub') {
|
||||
arcContainer.removeChild(child);
|
||||
}
|
||||
});
|
||||
drawRings();
|
||||
}, 100);
|
||||
});
|
||||
|
||||
drawRings();
|
||||
setInterval(updateSystemTime, 1000);
|
||||
updateSystemTime();
|
||||
|
||||
// Global Handlers
|
||||
document.getElementById('clearBtn').onclick = () => {
|
||||
outputArea.value = '';
|
||||
};
|
||||
|
||||
chatForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const message = chatInput.value.trim();
|
||||
if (!message) return;
|
||||
|
||||
appendMessage(message, 'visitor');
|
||||
chatInput.value = '';
|
||||
|
||||
try {
|
||||
const response = await fetch('api/chat.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ message })
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
// Artificial delay for realism
|
||||
setTimeout(() => {
|
||||
appendMessage(data.reply, 'bot');
|
||||
}, 500);
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
appendMessage("Sorry, something went wrong. Please try again.", 'bot');
|
||||
}
|
||||
});
|
||||
});
|
||||
document.getElementById('saveBtn').onclick = () => {
|
||||
if (!outputArea.value.trim()) return;
|
||||
outputArea.value += "\n>> [STORAGE] DATA_COMMITTED\n";
|
||||
outputArea.scrollTop = outputArea.scrollHeight;
|
||||
};
|
||||
});
|
||||
BIN
assets/pasted-20260222-221057-5cac8bd2.png
Normal file
BIN
assets/pasted-20260222-221057-5cac8bd2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 461 KiB |
179
index.php
179
index.php
@ -1,150 +1,57 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
@ini_set('display_errors', '1');
|
||||
@error_reporting(E_ALL);
|
||||
@date_default_timezone_set('UTC');
|
||||
|
||||
$phpVersion = PHP_VERSION;
|
||||
$now = date('Y-m-d H:i:s');
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>New Style</title>
|
||||
<?php
|
||||
// Read project preview data from environment
|
||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
?>
|
||||
<?php if ($projectDescription): ?>
|
||||
<!-- Meta description -->
|
||||
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
||||
<!-- Open Graph meta tags -->
|
||||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<!-- Twitter meta tags -->
|
||||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<?php endif; ?>
|
||||
<?php if ($projectImageUrl): ?>
|
||||
<!-- Open Graph image -->
|
||||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<!-- Twitter image -->
|
||||
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<?php endif; ?>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
||||
<title>Arc Prototype - Stark Industries</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--bg-color-start: #6a11cb;
|
||||
--bg-color-end: #2575fc;
|
||||
--text-color: #ffffff;
|
||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Inter', sans-serif;
|
||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
||||
color: var(--text-color);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
body::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
|
||||
animation: bg-pan 20s linear infinite;
|
||||
z-index: -1;
|
||||
}
|
||||
@keyframes bg-pan {
|
||||
0% { background-position: 0% 0%; }
|
||||
100% { background-position: 100% 100%; }
|
||||
}
|
||||
main {
|
||||
padding: 2rem;
|
||||
}
|
||||
.card {
|
||||
background: var(--card-bg-color);
|
||||
border: 1px solid var(--card-border-color);
|
||||
border-radius: 16px;
|
||||
padding: 2rem;
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.loader {
|
||||
margin: 1.25rem auto 1.25rem;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border: 3px solid rgba(255, 255, 255, 0.25);
|
||||
border-top-color: #fff;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
.hint {
|
||||
opacity: 0.9;
|
||||
}
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px; height: 1px;
|
||||
padding: 0; margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap; border: 0;
|
||||
}
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
margin: 0 0 1rem;
|
||||
letter-spacing: -1px;
|
||||
}
|
||||
p {
|
||||
margin: 0.5rem 0;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
code {
|
||||
background: rgba(0,0,0,0.2);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
}
|
||||
footer {
|
||||
position: absolute;
|
||||
bottom: 1rem;
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
</style>
|
||||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;700;800&display=swap" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?= time() ?>">
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<div class="card">
|
||||
<h1>Analyzing your requirements and generating your website…</h1>
|
||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
||||
<span class="sr-only">Loading…</span>
|
||||
</div>
|
||||
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
|
||||
<p class="hint">This page will update automatically as the plan is implemented.</p>
|
||||
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
||||
|
||||
<div class="blueprint-grid"></div>
|
||||
<svg id="backgroundSvg" class="background-svg"></svg>
|
||||
|
||||
<div class="blueprint-viewport">
|
||||
<div class="main-container">
|
||||
<!-- Unified Arc Keyboard -->
|
||||
<div id="arcKeysContainer" class="arc-keys-wrapper">
|
||||
<div class="arc-center-hub" id="arcCenterHub">
|
||||
<div class="hub-label">STARK_OS</div>
|
||||
</div>
|
||||
<!-- All Arc keys injected by JS -->
|
||||
</div>
|
||||
|
||||
<!-- Terminal Output positioned strategically -->
|
||||
<div class="integrated-output">
|
||||
<textarea id="outputArea" readonly placeholder=">> SYSTEM ONLINE. READY FOR INPUT."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
||||
</footer>
|
||||
|
||||
<!-- Interface Branding -->
|
||||
<div class="branding branding-top-left">
|
||||
STARK_IND_PROTOTYPE_MK.82
|
||||
</div>
|
||||
|
||||
<div class="branding branding-bottom-right">
|
||||
<span class="design-label">DESIGN BY EYEOFSURON</span>
|
||||
<span class="system-time" id="systemTime">[ 00:00:00 ]</span>
|
||||
</div>
|
||||
|
||||
<!-- Global Controls -->
|
||||
<div class="global-controls">
|
||||
<button id="saveBtn" class="control-btn">SYNC_CORE</button>
|
||||
<button id="clearBtn" class="control-btn">FLUSH_BUF</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="assets/js/main.js?v=<?= time() ?>"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user