mirror of
https://github.com/myronblair/web-dashboard
synced 2026-06-30 17:50:10 -05:00
610 lines
23 KiB
HTML
610 lines
23 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Myron's Dashboard</title>
|
||
<style>
|
||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||
|
||
:root {
|
||
--bg: #0f1117;
|
||
--surface: #1a1d27;
|
||
--surface2: #22263a;
|
||
--border: #2e3350;
|
||
--accent: #4f8ef7;
|
||
--accent2: #7c5af7;
|
||
--green: #22c55e;
|
||
--yellow: #eab308;
|
||
--red: #ef4444;
|
||
--orange: #f97316;
|
||
--pink: #ec4899;
|
||
--cyan: #06b6d4;
|
||
--text: #e2e4f0;
|
||
--muted: #8b90a8;
|
||
--radius: 12px;
|
||
}
|
||
|
||
body {
|
||
background: var(--bg);
|
||
color: var(--text);
|
||
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
|
||
min-height: 100vh;
|
||
padding: 2rem 1.5rem;
|
||
}
|
||
|
||
header {
|
||
text-align: center;
|
||
margin-bottom: 2.5rem;
|
||
}
|
||
header h1 {
|
||
font-size: 2rem;
|
||
font-weight: 700;
|
||
background: linear-gradient(135deg, var(--accent), var(--accent2));
|
||
-webkit-background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
background-clip: text;
|
||
letter-spacing: -0.5px;
|
||
}
|
||
header p {
|
||
color: var(--muted);
|
||
font-size: 0.85rem;
|
||
margin-top: 0.35rem;
|
||
letter-spacing: 0.5px;
|
||
}
|
||
|
||
.grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||
gap: 1.25rem;
|
||
max-width: 1400px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.card {
|
||
background: var(--surface);
|
||
border: 1px solid var(--border);
|
||
border-radius: var(--radius);
|
||
overflow: hidden;
|
||
transition: border-color 0.2s, transform 0.2s;
|
||
}
|
||
.card:hover { border-color: var(--accent); transform: translateY(-2px); }
|
||
|
||
.card-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.6rem;
|
||
padding: 0.85rem 1rem;
|
||
background: var(--surface2);
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
.card-header .icon {
|
||
font-size: 1.1rem;
|
||
width: 28px;
|
||
height: 28px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 6px;
|
||
flex-shrink: 0;
|
||
}
|
||
.card-header h2 {
|
||
font-size: 0.8rem;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
letter-spacing: 1px;
|
||
color: var(--muted);
|
||
}
|
||
|
||
.links { padding: 0.5rem 0; }
|
||
|
||
.link-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
padding: 0.6rem 1rem;
|
||
text-decoration: none;
|
||
color: var(--text);
|
||
font-size: 0.9rem;
|
||
transition: background 0.15s;
|
||
border-radius: 0;
|
||
}
|
||
.link-item:hover { background: var(--surface2); }
|
||
.link-item .dot {
|
||
width: 8px; height: 8px;
|
||
border-radius: 50%;
|
||
flex-shrink: 0;
|
||
}
|
||
.link-item .name { flex: 1; font-weight: 500; }
|
||
.link-item .sub {
|
||
font-size: 0.75rem;
|
||
color: var(--muted);
|
||
}
|
||
.link-item .arrow {
|
||
color: var(--muted);
|
||
font-size: 0.7rem;
|
||
opacity: 0;
|
||
transition: opacity 0.15s;
|
||
}
|
||
.link-item:hover .arrow { opacity: 1; }
|
||
|
||
.dot-blue { background: var(--accent); }
|
||
.dot-purple { background: var(--accent2); }
|
||
.dot-green { background: var(--green); }
|
||
.dot-yellow { background: var(--yellow); }
|
||
.dot-red { background: var(--red); }
|
||
.dot-orange { background: var(--orange); }
|
||
.dot-pink { background: var(--pink); }
|
||
.dot-cyan { background: var(--cyan); }
|
||
.dot-gray { background: var(--muted); }
|
||
|
||
.icon-blue { background: rgba(79,142,247,0.15); }
|
||
.icon-purple { background: rgba(124,90,247,0.15); }
|
||
.icon-green { background: rgba(34,197,94,0.15); }
|
||
.icon-yellow { background: rgba(234,179,8,0.15); }
|
||
.icon-orange { background: rgba(249,115,22,0.15); }
|
||
.icon-pink { background: rgba(236,72,153,0.15); }
|
||
.icon-cyan { background: rgba(6,182,212,0.15); }
|
||
.icon-red { background: rgba(239,68,68,0.15); }
|
||
|
||
.divider {
|
||
height: 1px;
|
||
background: var(--border);
|
||
margin: 0.25rem 1rem;
|
||
}
|
||
|
||
@media (max-width: 600px) {
|
||
body { padding: 1rem; }
|
||
.grid { grid-template-columns: 1fr; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<header>
|
||
<h1>⚡ Blair HQ</h1>
|
||
<p id="clock"></p>
|
||
</header>
|
||
|
||
<div class="grid">
|
||
|
||
<!-- INFRASTRUCTURE -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<div class="icon icon-blue">🖥️</div>
|
||
<h2>Infrastructure</h2>
|
||
</div>
|
||
<div class="links">
|
||
<a class="link-item" href="https://10.48.200.90:8006" target="_blank">
|
||
<span class="dot dot-blue"></span><span class="name">Proxmox PVE1</span><span class="sub">Primary</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://10.48.200.91:8006" target="_blank">
|
||
<span class="dot dot-blue"></span><span class="name">Proxmox PVE2</span><span class="sub">Secondary</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://10.48.200.201:81" target="_blank">
|
||
<span class="dot dot-cyan"></span><span class="name">Nginx Proxy Manager</span><span class="sub">10.48.200.201</span><span class="arrow">↗</span>
|
||
</a>
|
||
<div class="divider"></div>
|
||
<a class="link-item" href="http://10.48.200.210:11434" target="_blank">
|
||
<span class="dot dot-purple"></span><span class="name">Ollama</span><span class="sub">Local LLM</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://10.48.200.249:5000" target="_blank">
|
||
<span class="dot dot-gray"></span><span class="name">Synology NAS</span><span class="sub">10.48.200.249</span><span class="arrow">↗</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- JARVIS -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<div class="icon icon-blue">🤖</div>
|
||
<h2>JARVIS</h2>
|
||
</div>
|
||
<div class="links">
|
||
<a class="link-item" href="https://jarvis.orbishosting.com" target="_blank">
|
||
<span class="dot dot-blue"></span><span class="name">JARVIS Dashboard</span><span class="sub">Main UI</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://jarvis.orbishosting.com/admin" target="_blank">
|
||
<span class="dot dot-purple"></span><span class="name">JARVIS Admin</span><span class="sub">Admin panel</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://10.48.200.211/phpmyadmin" target="_blank">
|
||
<span class="dot dot-orange"></span><span class="name">phpMyAdmin</span><span class="sub">JARVIS DB</span><span class="arrow">↗</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- HOME AUTOMATION -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<div class="icon icon-green">🏠</div>
|
||
<h2>Home Automation</h2>
|
||
</div>
|
||
<div class="links">
|
||
<a class="link-item" href="https://hoa.orbishosting.com" target="_blank">
|
||
<span class="dot dot-green"></span><span class="name">Home Assistant</span><span class="sub">hoa.orbishosting.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://10.48.200.18:8581" target="_blank">
|
||
<span class="dot dot-orange"></span><span class="name">HomeBridge</span><span class="sub">10.48.200.18</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://10.48.200.97:8123" target="_blank">
|
||
<span class="dot dot-cyan"></span><span class="name">HA (Direct LAN)</span><span class="sub">10.48.200.97</span><span class="arrow">↗</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MEDIA -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<div class="icon icon-purple">🎬</div>
|
||
<h2>Media</h2>
|
||
</div>
|
||
<div class="links">
|
||
<a class="link-item" href="http://10.48.200.33:8096" target="_blank">
|
||
<span class="dot dot-purple"></span><span class="name">Jellyfin</span><span class="sub">Media server</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://10.48.200.35:8989" target="_blank">
|
||
<span class="dot dot-blue"></span><span class="name">Sonarr</span><span class="sub">TV shows</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://10.48.200.35:7878" target="_blank">
|
||
<span class="dot dot-yellow"></span><span class="name">Radarr</span><span class="sub">Movies</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://10.48.200.35:9696" target="_blank">
|
||
<span class="dot dot-orange"></span><span class="name">Prowlarr</span><span class="sub">Indexers</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://10.48.200.35:8080" target="_blank">
|
||
<span class="dot dot-green"></span><span class="name">qBittorrent</span><span class="sub">Downloads</span><span class="arrow">↗</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- HOSTING PANEL -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<div class="icon icon-cyan">⚙️</div>
|
||
<h2>NovaCPX Panel</h2>
|
||
</div>
|
||
<div class="links">
|
||
<a class="link-item" href="https://admin.novacpx.orbishosting.com" target="_blank">
|
||
<span class="dot dot-red"></span><span class="name">Admin Panel</span><span class="sub">Root access</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://reseller.novacpx.orbishosting.com" target="_blank">
|
||
<span class="dot dot-orange"></span><span class="name">Reseller Panel</span><span class="sub">Reseller access</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://panel.novacpx.orbishosting.com" target="_blank">
|
||
<span class="dot dot-green"></span><span class="name">Client Panel</span><span class="sub">End user access</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://novacpx.orbishosting.com" target="_blank">
|
||
<span class="dot dot-blue"></span><span class="name">Client Panel (alt)</span><span class="sub">novacpx.orbishosting.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- WEBSITES -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<div class="icon icon-pink">🌐</div>
|
||
<h2>Websites</h2>
|
||
</div>
|
||
<div class="links">
|
||
<a class="link-item" href="https://orbishosting.com" target="_blank">
|
||
<span class="dot dot-blue"></span><span class="name">Orbis Hosting</span><span class="sub">orbishosting.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://orbis.orbishosting.com" target="_blank">
|
||
<span class="dot dot-cyan"></span><span class="name">Orbis Portal</span><span class="sub">orbis.orbishosting.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://tomsjavajive.com" target="_blank">
|
||
<span class="dot dot-yellow"></span><span class="name">Tom's Java Jive</span><span class="sub">tomsjavajive.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://tomtomgames.com" target="_blank">
|
||
<span class="dot dot-purple"></span><span class="name">Tom Tom Games</span><span class="sub">tomtomgames.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://epictravelexpeditions.com" target="_blank">
|
||
<span class="dot dot-green"></span><span class="name">Epic Travel</span><span class="sub">epictravelexpeditions.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://parkerslingshotrentals.com" target="_blank">
|
||
<span class="dot dot-red"></span><span class="name">Parker Slingshot Rentals</span><span class="sub">parkerslingshotrentals.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- COMMUNICATIONS -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<div class="icon icon-orange">📞</div>
|
||
<h2>Communications</h2>
|
||
</div>
|
||
<div class="links">
|
||
<a class="link-item" href="https://fusion.orbishosting.com" target="_blank">
|
||
<span class="dot dot-orange"></span><span class="name">FusionPBX</span><span class="sub">fusion.orbishosting.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://fusion.orbishosting.com/app/settings" target="_blank">
|
||
<span class="dot dot-yellow"></span><span class="name">PBX Settings</span><span class="sub">Extensions & trunks</span><span class="arrow">↗</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- NETWORKING -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<div class="icon icon-yellow">🔒</div>
|
||
<h2>Networking</h2>
|
||
</div>
|
||
<div class="links">
|
||
<a class="link-item" href="https://10.48.200.1" target="_blank">
|
||
<span class="dot dot-yellow"></span><span class="name">FortiGate</span><span class="sub">10.48.200.1</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://10.48.200.80" target="_blank">
|
||
<span class="dot dot-cyan"></span><span class="name">WB610H Bridge</span><span class="sub">10.48.200.80</span><span class="arrow">↗</span>
|
||
</a>
|
||
<div class="divider"></div>
|
||
<a class="link-item" href="http://10.48.200.250" target="_blank">
|
||
<span class="dot dot-green"></span><span class="name">Goalake Switch 1</span><span class="sub">10.48.200.250</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://10.48.200.251" target="_blank">
|
||
<span class="dot dot-green"></span><span class="name">Goalake Switch 2</span><span class="sub">10.48.200.251</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://10.48.200.19" target="_blank">
|
||
<span class="dot dot-green"></span><span class="name">WireGuard CT</span><span class="sub">10.48.200.19</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://login.tailscale.com" target="_blank">
|
||
<span class="dot dot-blue"></span><span class="name">Tailscale</span><span class="sub">login.tailscale.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://dash.cloudflare.com" target="_blank">
|
||
<span class="dot dot-orange"></span><span class="name">Cloudflare</span><span class="sub">DNS & CDN</span><span class="arrow">↗</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- GITHUB -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<div class="icon icon-purple">🐙</div>
|
||
<h2>GitHub Repos</h2>
|
||
</div>
|
||
<div class="links">
|
||
<a class="link-item" href="https://github.com/myronblair/novacpx" target="_blank">
|
||
<span class="dot dot-cyan"></span><span class="name">NovaCPX</span><span class="sub">myronblair/novacpx</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://github.com/myronblair/jarvis" target="_blank">
|
||
<span class="dot dot-blue"></span><span class="name">JARVIS</span><span class="sub">myronblair/jarvis</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://github.com/myronblair/infra" target="_blank">
|
||
<span class="dot dot-gray"></span><span class="name">Infra</span><span class="sub">myronblair/infra</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://github.com/myronblair" target="_blank">
|
||
<span class="dot dot-purple"></span><span class="name">All Repos</span><span class="sub">github.com/myronblair</span><span class="arrow">↗</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- DO SERVER -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<div class="icon icon-blue">🌊</div>
|
||
<h2>DigitalOcean</h2>
|
||
</div>
|
||
<div class="links">
|
||
<a class="link-item" href="https://cloud.digitalocean.com" target="_blank">
|
||
<span class="dot dot-blue"></span><span class="name">DO Dashboard</span><span class="sub">cloud.digitalocean.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://165.22.1.228:8090" target="_blank">
|
||
<span class="dot dot-cyan"></span><span class="name">CyberPanel</span><span class="sub">165.22.1.228:8090</span><span class="arrow">↗</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<script>
|
||
function tick() {
|
||
const now = new Date();
|
||
document.getElementById('clock').textContent =
|
||
now.toLocaleDateString('en-US', {weekday:'long',year:'numeric',month:'long',day:'numeric'}) +
|
||
' · ' +
|
||
now.toLocaleTimeString('en-US', {hour:'2-digit',minute:'2-digit',second:'2-digit'});
|
||
}
|
||
tick(); setInterval(tick, 1000);
|
||
|
||
// ── Notes ──────────────────────────────────────────────────────────────
|
||
const NOTES_KEY = 'dashboard_notes';
|
||
|
||
function loadNotes() {
|
||
return JSON.parse(localStorage.getItem(NOTES_KEY) || '[]');
|
||
}
|
||
function saveNotes(notes) {
|
||
localStorage.setItem(NOTES_KEY, JSON.stringify(notes));
|
||
}
|
||
|
||
function renderNotes() {
|
||
const notes = loadNotes();
|
||
const list = document.getElementById('notes-list');
|
||
if (!list) return;
|
||
if (!notes.length) {
|
||
list.innerHTML = '<div style="color:#8b90a8;font-size:.85rem;padding:.5rem 0">No notes yet. Add one above.</div>';
|
||
return;
|
||
}
|
||
list.innerHTML = notes.map((n, i) => `
|
||
<div class="note-item ${n.done ? 'note-done' : ''}" data-i="${i}">
|
||
<button class="note-check" onclick="toggleNote(${i})" title="${n.done ? 'Mark incomplete' : 'Mark done'}">
|
||
${n.done ? '✅' : '<span class="note-circle"></span>'}
|
||
</button>
|
||
<div class="note-body">
|
||
<div class="note-text">${escHtml(n.text)}</div>
|
||
${n.detail ? `<div class="note-detail">${escHtml(n.detail)}</div>` : ''}
|
||
<div class="note-meta">${new Date(n.ts).toLocaleString('en-US',{month:'short',day:'numeric',hour:'2-digit',minute:'2-digit'})}</div>
|
||
</div>
|
||
<button class="note-del" onclick="deleteNote(${i})" title="Delete">✕</button>
|
||
</div>`).join('');
|
||
}
|
||
|
||
function escHtml(s) {
|
||
return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/\n/g,'<br>');
|
||
}
|
||
|
||
function addNote() {
|
||
const txt = document.getElementById('note-input').value.trim();
|
||
const det = document.getElementById('note-detail').value.trim();
|
||
if (!txt) { document.getElementById('note-input').focus(); return; }
|
||
const notes = loadNotes();
|
||
notes.unshift({ text: txt, detail: det, done: false, ts: Date.now() });
|
||
saveNotes(notes);
|
||
document.getElementById('note-input').value = '';
|
||
document.getElementById('note-detail').value = '';
|
||
document.getElementById('note-detail').style.display = 'none';
|
||
document.getElementById('note-detail-toggle').textContent = '+ Add details';
|
||
renderNotes();
|
||
}
|
||
|
||
function toggleNote(i) {
|
||
const notes = loadNotes();
|
||
notes[i].done = !notes[i].done;
|
||
saveNotes(notes);
|
||
renderNotes();
|
||
}
|
||
|
||
function deleteNote(i) {
|
||
const notes = loadNotes();
|
||
notes.splice(i, 1);
|
||
saveNotes(notes);
|
||
renderNotes();
|
||
}
|
||
|
||
function clearDone() {
|
||
saveNotes(loadNotes().filter(n => !n.done));
|
||
renderNotes();
|
||
}
|
||
|
||
function toggleDetail() {
|
||
const d = document.getElementById('note-detail');
|
||
const btn = document.getElementById('note-detail-toggle');
|
||
const show = d.style.display === 'none';
|
||
d.style.display = show ? 'block' : 'none';
|
||
btn.textContent = show ? '− Hide details' : '+ Add details';
|
||
if (show) d.focus();
|
||
}
|
||
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
renderNotes();
|
||
document.getElementById('note-input').addEventListener('keydown', e => {
|
||
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); addNote(); }
|
||
});
|
||
});
|
||
</script>
|
||
|
||
<!-- ── NOTES SECTION ─────────────────────────────────────────────────────── -->
|
||
<style>
|
||
.notes-wrap {
|
||
max-width:1400px; margin:2rem auto 0; padding:0 1.5rem 3rem;
|
||
}
|
||
.notes-header {
|
||
display:flex; align-items:center; justify-content:space-between;
|
||
margin-bottom:1.25rem;
|
||
}
|
||
.notes-title {
|
||
font-size:.75rem; font-weight:700; text-transform:uppercase;
|
||
letter-spacing:1.5px; color:#8b90a8;
|
||
}
|
||
.notes-add {
|
||
background:var(--surface); border:1px solid var(--border);
|
||
border-radius:var(--radius); padding:1rem; margin-bottom:1rem;
|
||
}
|
||
.notes-add-row {
|
||
display:flex; gap:.6rem; align-items:center;
|
||
}
|
||
#note-input {
|
||
flex:1; background:var(--surface2); border:1px solid var(--border);
|
||
border-radius:6px; color:var(--text); padding:.6rem .85rem;
|
||
font-size:.9rem; font-family:inherit; outline:none;
|
||
transition:border-color .15s;
|
||
}
|
||
#note-input:focus { border-color:var(--accent); }
|
||
#note-detail {
|
||
width:100%; margin-top:.6rem; background:var(--surface2);
|
||
border:1px solid var(--border); border-radius:6px; color:var(--text);
|
||
padding:.6rem .85rem; font-size:.85rem; font-family:inherit;
|
||
outline:none; resize:vertical; min-height:70px;
|
||
transition:border-color .15s; display:none;
|
||
}
|
||
#note-detail:focus { border-color:var(--accent); }
|
||
.btn-note-add {
|
||
background:var(--accent); color:#fff; border:none; border-radius:6px;
|
||
padding:.6rem 1.1rem; font-size:.85rem; cursor:pointer; font-weight:600;
|
||
white-space:nowrap; transition:opacity .15s;
|
||
}
|
||
.btn-note-add:hover { opacity:.85; }
|
||
#note-detail-toggle {
|
||
background:none; border:none; color:#8b90a8; font-size:.78rem;
|
||
cursor:pointer; padding:.25rem 0; margin-top:.4rem;
|
||
transition:color .15s;
|
||
}
|
||
#note-detail-toggle:hover { color:var(--text); }
|
||
.note-item {
|
||
display:flex; align-items:flex-start; gap:.75rem;
|
||
padding:.8rem .75rem; border-radius:8px;
|
||
background:var(--surface); border:1px solid var(--border);
|
||
margin-bottom:.5rem; transition:opacity .2s, border-color .2s;
|
||
}
|
||
.note-item:hover { border-color:var(--accent); }
|
||
.note-done { opacity:.55; }
|
||
.note-check {
|
||
background:none; border:none; cursor:pointer; padding:.1rem;
|
||
font-size:1.1rem; flex-shrink:0; margin-top:.1rem;
|
||
}
|
||
.note-circle {
|
||
display:inline-block; width:18px; height:18px; border-radius:50%;
|
||
border:2px solid #8b90a8; vertical-align:middle;
|
||
transition:border-color .15s;
|
||
}
|
||
.note-item:hover .note-circle { border-color:var(--accent); }
|
||
.note-body { flex:1; min-width:0; }
|
||
.note-text {
|
||
font-size:.9rem; color:var(--text); line-height:1.45;
|
||
word-break:break-word;
|
||
}
|
||
.note-done .note-text { text-decoration:line-through; color:#8b90a8; }
|
||
.note-detail {
|
||
font-size:.8rem; color:#8b90a8; margin-top:.3rem; line-height:1.5;
|
||
}
|
||
.note-meta {
|
||
font-size:.72rem; color:#5a5f78; margin-top:.35rem;
|
||
}
|
||
.note-del {
|
||
background:none; border:none; color:#5a5f78; cursor:pointer;
|
||
font-size:.85rem; padding:.1rem .2rem; flex-shrink:0;
|
||
opacity:0; transition:opacity .15s, color .15s;
|
||
}
|
||
.note-item:hover .note-del { opacity:1; }
|
||
.note-del:hover { color:#ef4444; }
|
||
.btn-clear {
|
||
background:none; border:1px solid var(--border); color:#8b90a8;
|
||
border-radius:5px; padding:.3rem .7rem; font-size:.75rem;
|
||
cursor:pointer; transition:all .15s;
|
||
}
|
||
.btn-clear:hover { border-color:#ef4444; color:#ef4444; }
|
||
@media(max-width:600px){
|
||
.notes-wrap { padding:0 1rem 2rem; }
|
||
.notes-add-row { flex-direction:column; align-items:stretch; }
|
||
}
|
||
</style>
|
||
|
||
<div class="notes-wrap">
|
||
<div class="notes-header">
|
||
<div class="notes-title">📝 Notes</div>
|
||
<button class="btn-clear" onclick="clearDone()">Clear completed</button>
|
||
</div>
|
||
|
||
<div class="notes-add">
|
||
<div class="notes-add-row">
|
||
<input id="note-input" type="text" placeholder="Add a note — press Enter to save…">
|
||
<button class="btn-note-add" onclick="addNote()">Add</button>
|
||
</div>
|
||
<button id="note-detail-toggle" onclick="toggleDetail()">+ Add details</button>
|
||
<textarea id="note-detail" placeholder="Additional details…"></textarea>
|
||
</div>
|
||
|
||
<div id="notes-list"></div>
|
||
</div>
|
||
|
||
</body>
|
||
</html>
|