<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>QuillSQL • Interactive Demo</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=Space+Grotesk:wght@400;600&family=Source+Code+Pro:wght@400;600&display=swap" rel="stylesheet">
<style>
:root {
--bg: #03070c;
--card: rgba(4, 10, 18, 0.82);
--accent: #7ef7c7;
--accent-strong: #1fe6a1;
--muted: #91a9bd;
--text: #e1f7ff;
--terminal: rgba(2, 6, 9, 0.96);
--border: rgba(126, 247, 199, 0.28);
--error: #ff7b72;
--warn: #f7c948;
--pulse: rgba(126, 247, 199, 0.25);
}
* {
box-sizing: border-box;
}
body {
margin: 0;
min-height: 100vh;
background: radial-gradient(circle at 10% 20%, rgba(31, 230, 161, 0.15), transparent 55%),
radial-gradient(circle at 80% 0%, rgba(61, 119, 255, 0.18), transparent 65%),
var(--bg);
color: var(--text);
font-family: 'Space Grotesk', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
-webkit-font-smoothing: antialiased;
overflow-x: hidden;
}
body::before {
content: "";
position: fixed;
inset: -20%;
background: conic-gradient(from 90deg, rgba(126, 247, 199, 0.2), transparent 45%, rgba(63, 182, 255, 0.18));
filter: blur(120px);
opacity: 0.6;
pointer-events: none;
animation: aurora 26s ease-in-out infinite alternate;
}
body::after {
content: "";
position: fixed;
inset: 0;
background: linear-gradient(rgba(126, 247, 199, 0.08) 1px, transparent 1px);
background-size: 100% 2px;
opacity: 0.35;
pointer-events: none;
animation: scan 8s linear infinite;
}
.bg-particles,
.parallax-layer {
position: fixed;
inset: 0;
overflow: hidden;
pointer-events: none;
z-index: 0;
}
.parallax-layer {
mix-blend-mode: screen;
opacity: 0.25;
background: radial-gradient(circle at 30% 40%, rgba(126, 247, 199, 0.2), transparent 60%),
radial-gradient(circle at 70% 60%, rgba(63, 182, 255, 0.2), transparent 60%);
transform: translateZ(0);
animation: parallaxDrift 28s ease-in-out infinite alternate;
}
.parallax-layer.layer-two {
animation-duration: 36s;
animation-delay: 8s;
opacity: 0.2;
filter: blur(20px);
}
.bg-particles span {
position: absolute;
width: 12px;
height: 12px;
border-radius: 50%;
background: radial-gradient(circle, rgba(126, 247, 199, 0.9), transparent 70%);
box-shadow: 0 0 20px rgba(126, 247, 199, 0.45);
animation: floatParticle 18s linear infinite;
opacity: 0.35;
}
.bg-particles span:nth-child(odd) {
width: 8px;
height: 8px;
animation-duration: 22s;
background: radial-gradient(circle, rgba(63, 182, 255, 0.9), transparent 70%);
box-shadow: 0 0 20px rgba(63, 182, 255, 0.35);
}
#root {
width: min(1240px, 94vw);
margin: 0 auto;
padding: 48px 0 72px;
display: flex;
flex-direction: column;
gap: 36px;
position: relative;
z-index: 1;
}
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 18px 0 12px;
}
.navbar .brand {
letter-spacing: 0.5em;
text-transform: uppercase;
font-size: 0.85rem;
color: var(--muted);
}
.stats-bar {
display: flex;
flex-wrap: wrap;
gap: 12px;
align-items: center;
margin-top: 8px;
}
.stat-pill {
border: 1px solid rgba(126, 247, 199, 0.35);
border-radius: 999px;
padding: 6px 14px;
font-size: 0.75rem;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--accent);
background: rgba(3, 8, 12, 0.8);
display: inline-flex;
align-items: center;
gap: 6px;
}
.nav-links {
display: inline-flex;
gap: 12px;
}
.nav-links button {
border: 1px solid rgba(126, 247, 199, 0.25);
background: rgba(3, 8, 12, 0.7);
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.18em;
font-size: 0.7rem;
padding: 8px 14px;
border-radius: 999px;
cursor: pointer;
transition: border-color 0.2s ease, color 0.2s ease, background 0.2s ease;
}
.nav-links button.active {
color: var(--accent);
border-color: rgba(126, 247, 199, 0.6);
background: rgba(126, 247, 199, 0.12);
}
.page-section {
animation: fadeIn 0.6s ease;
display: flex;
flex-direction: column;
gap: 28px;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(12px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.hero {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
gap: 32px;
align-items: stretch;
perspective: 1400px;
}
.hero-card {
background: radial-gradient(circle at 25% 25%, rgba(36, 247, 160, 0.2), transparent 60%), var(--card);
border: 1px solid var(--border);
border-radius: 24px;
padding: 36px;
box-shadow: 0 30px 90px rgba(0, 0, 0, 0.6);
position: relative;
overflow: hidden;
transform-style: preserve-3d;
transition: transform 0.8s ease, box-shadow 0.6s ease;
}
.hero-card::before {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(130deg, rgba(126, 247, 199, 0.15), transparent 60%);
opacity: 0.6;
}
.hero-card::after {
content: "";
position: absolute;
inset: -20%;
background: radial-gradient(circle, rgba(126, 247, 199, 0.18), transparent 55%);
filter: blur(40px);
opacity: 0.35;
}
.hero-card:hover {
transform: translateY(-8px) scale(1.03);
box-shadow: 0 40px 120px rgba(0, 0, 0, 0.65), 0 0 45px rgba(126, 247, 199, 0.25);
}
.hero-copy {
position: relative;
z-index: 1;
}
.eyebrow {
letter-spacing: 0.45em;
text-transform: uppercase;
font-size: 0.82rem;
color: var(--accent);
}
h1 {
margin: 12px 0;
font-size: clamp(2rem, 4vw, 3rem);
letter-spacing: 0.02em;
}
.hero-copy p {
margin: 0 0 22px;
color: var(--muted);
font-size: 1.05rem;
}
.hero-actions {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.hero-actions button {
backdrop-filter: blur(6px);
border-radius: 999px;
border: 1px solid rgba(126, 247, 199, 0.5);
background: rgba(126, 247, 199, 0.12);
color: var(--accent);
padding: 10px 20px;
text-transform: uppercase;
letter-spacing: 0.2em;
font-weight: 600;
cursor: pointer;
}
.hero-actions button:hover {
border-color: var(--accent-strong);
background: rgba(126, 247, 199, 0.2);
}
.info-panel {
background: rgba(5, 14, 20, 0.75);
border: 1px solid rgba(126, 247, 199, 0.2);
border-radius: 24px;
padding: 28px;
position: relative;
overflow: hidden;
box-shadow: 0 25px 70px rgba(0, 0, 0, 0.55);
transition: transform 0.5s ease, box-shadow 0.5s ease;
}
.info-panel::before {
content: "";
position: absolute;
inset: 0;
background: radial-gradient(circle at top right, rgba(126, 247, 199, 0.18), transparent 60%);
opacity: 0.5;
}
.info-panel:hover {
transform: translateY(-6px);
box-shadow: 0 35px 90px rgba(0, 0, 0, 0.6), 0 0 30px rgba(126, 247, 199, 0.2);
}
.info-panel ul {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: 18px;
position: relative;
z-index: 1;
}
.info-row {
display: grid;
grid-template-columns: 56px 1fr;
align-items: center;
gap: 16px;
padding: 14px 16px;
border-radius: 18px;
background: rgba(3, 9, 14, 0.55);
border: 1px solid rgba(255, 255, 255, 0.04);
transition: transform 0.35s ease, border-color 0.3s ease;
}
.info-icon {
width: 56px;
height: 56px;
border-radius: 18px;
background: rgba(6, 16, 22, 0.85);
border: 1px solid rgba(126, 247, 199, 0.2);
display: flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
color: var(--accent);
}
.info-icon svg {
width: 32px;
height: 32px;
stroke: currentColor;
stroke-width: 1.6;
fill: none;
opacity: 0.85;
}
.info-row:hover {
transform: translateX(6px);
border-color: rgba(126, 247, 199, 0.3);
}
.info-meta h3 {
margin: 0;
letter-spacing: 0.12em;
font-size: 0.85rem;
color: var(--muted);
text-transform: uppercase;
}
.info-meta strong {
display: block;
font-size: 1.5rem;
color: var(--accent);
margin: 2px 0;
}
.info-meta p {
margin: 2px 0 8px;
color: var(--muted);
font-size: 0.95rem;
}
.info-progress {
width: 100%;
height: 6px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.08);
position: relative;
overflow: hidden;
}
.info-progress span {
position: absolute;
inset: 0;
background: linear-gradient(90deg, rgba(126, 247, 199, 0.8), rgba(35, 214, 159, 0.8));
border-radius: inherit;
transform-origin: left;
}
.feature-tabs {
margin-top: 12px;
border: 1px solid rgba(126, 247, 199, 0.25);
border-radius: 22px;
background: rgba(5, 12, 18, 0.8);
box-shadow: 0 25px 80px rgba(0, 0, 0, 0.55);
}
.feature-controls {
display: flex;
flex-wrap: wrap;
gap: 10px;
padding: 16px;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.feature-controls button {
flex: 1;
min-width: 140px;
border: 1px solid rgba(126, 247, 199, 0.2);
background: rgba(3, 9, 13, 0.9);
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.2em;
font-size: 0.7rem;
padding: 10px 12px;
border-radius: 14px;
cursor: pointer;
transition: border-color 0.3s ease, background 0.3s ease, color 0.3s ease;
}
.feature-controls button.active {
border-color: rgba(126, 247, 199, 0.6);
color: var(--accent);
background: linear-gradient(135deg, rgba(126, 247, 199, 0.25), rgba(63, 182, 255, 0.18));
}
.feature-pane {
padding: 24px;
display: grid;
grid-template-columns: minmax(240px, 1fr) minmax(220px, 300px);
gap: 20px;
align-items: stretch;
}
.feature-pane .copy h3 {
margin: 0;
font-size: 1.3rem;
letter-spacing: 0.1em;
color: var(--accent);
text-transform: uppercase;
}
.feature-pane .copy p {
margin: 10px 0 16px;
color: var(--muted);
line-height: 1.5;
}
.feature-pane ul {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: 10px;
}
.feature-pane li {
display: flex;
gap: 10px;
align-items: flex-start;
font-size: 0.95rem;
color: var(--text);
}
.feature-pane li::before {
content: '▸';
color: var(--accent);
margin-top: 2px;
}
.micro-graphic {
border-radius: 18px;
border: 1px solid rgba(255, 255, 255, 0.08);
padding: 16px;
position: relative;
overflow: hidden;
box-shadow: inset 0 0 45px rgba(0, 0, 0, 0.5);
}
.mvcc-timeline {
display: flex;
flex-direction: column;
gap: 12px;
}
.mvcc-event {
display: flex;
align-items: center;
gap: 10px;
}
.mvcc-event span {
flex: 1;
height: 6px;
border-radius: 999px;
background: rgba(126, 247, 199, 0.25);
position: relative;
overflow: hidden;
}
.mvcc-event span::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(90deg, rgba(126, 247, 199, 0.7), rgba(63, 182, 255, 0.7));
width: var(--fill, 60%);
}
.mvcc-event label {
font-size: 0.75rem;
color: var(--muted);
letter-spacing: 0.15em;
text-transform: uppercase;
}
.demo-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 18px;
margin-top: 16px;
perspective: 1000px;
}
.demo-card {
background: rgba(4, 11, 16, 0.8);
border: 1px solid rgba(126, 247, 199, 0.18);
border-radius: 18px;
padding: 18px;
position: relative;
overflow: hidden;
transform: translateZ(0);
transition: transform 0.4s ease, border-color 0.3s ease;
}
.demo-card::before {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(120deg, rgba(126, 247, 199, 0.18), transparent 65%);
opacity: 0;
transition: opacity 0.3s ease;
}
.demo-card:hover,
.demo-card.active {
transform: translateY(-6px) scale(1.02);
border-color: rgba(126, 247, 199, 0.35);
}
.demo-card:hover::before,
.demo-card.active::before {
opacity: 1;
}
.demo-card h5 {
margin: 0 0 8px;
color: var(--accent);
letter-spacing: 0.18em;
text-transform: uppercase;
font-size: 0.8rem;
}
.demo-card p {
margin: 0;
color: var(--muted);
font-size: 0.95rem;
line-height: 1.4;
}
.plan-visual {
margin-top: 20px;
border-radius: 20px;
border: 1px solid rgba(126, 247, 199, 0.2);
background: rgba(5, 12, 18, 0.85);
padding: 24px 18px;
position: relative;
overflow: hidden;
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5);
}
.plan-visual::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(120deg, rgba(126, 247, 199, 0.14), transparent 60%);
opacity: 0;
animation: shimmer 4s ease-in-out infinite;
}
.plan-sql {
margin-bottom: 18px;
border-radius: 12px;
background: rgba(2, 6, 10, 0.85);
border: 1px solid rgba(126, 247, 199, 0.18);
padding: 14px 16px;
font-family: 'Source Code Pro', monospace;
font-size: 0.9rem;
color: var(--text);
box-shadow: inset 0 0 25px rgba(0, 0, 0, 0.35);
white-space: pre-wrap;
}
.plan-tree {
display: flex;
flex-direction: column;
align-items: center;
gap: 28px;
position: relative;
}
.plan-node {
width: min(360px, 90%);
text-align: center;
padding: 14px 12px;
border-radius: 14px;
border: 1px solid rgba(126, 247, 199, 0.25);
background: rgba(2, 8, 12, 0.8);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
position: relative;
animation: pulseGlow 6s ease-in-out infinite;
}
.plan-node::before,
.plan-node::after {
content: "";
position: absolute;
width: 1px;
height: 24px;
background: rgba(126, 247, 199, 0.25);
left: 50%;
transform: translateX(-50%);
}
.plan-node::before {
top: -30px;
}
.plan-node::after {
bottom: -30px;
}
.plan-node:first-child::before {
display: none;
}
.plan-node:last-child::after {
display: none;
}
.blog-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 18px;
}
.blog-card {
background: rgba(5, 14, 20, 0.75);
border: 1px solid rgba(126, 247, 199, 0.2);
border-radius: 18px;
padding: 18px;
display: flex;
flex-direction: column;
gap: 10px;
box-shadow: 0 18px 55px rgba(0, 0, 0, 0.4);
}
.blog-card h4 {
margin: 0;
font-size: 1.1rem;
letter-spacing: 0.1em;
color: var(--accent);
text-transform: uppercase;
}
.blog-card p {
margin: 0;
color: var(--muted);
line-height: 1.5;
flex: 1;
}
.blog-card small {
color: rgba(255, 255, 255, 0.6);
letter-spacing: 0.2em;
text-transform: uppercase;
}
.plan-node h6 {
margin: 4px 0;
letter-spacing: 0.2em;
font-size: 0.75rem;
text-transform: uppercase;
color: var(--accent);
}
.plan-node p {
margin: 0;
color: var(--muted);
font-size: 0.9rem;
}
.insights-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 14px;
margin-top: 16px;
}
.insight-card {
background: rgba(5, 12, 18, 0.82);
border: 1px solid rgba(126, 247, 199, 0.18);
border-radius: 14px;
padding: 12px 14px;
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.4);
}
.insight-card h4 {
margin: 0 0 8px;
font-size: 0.9rem;
letter-spacing: 0.15em;
color: var(--accent);
text-transform: uppercase;
}
.insight-grid-row {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px;
margin: 8px 0;
}
.stat {
background: rgba(2, 6, 10, 0.8);
border: 1px solid rgba(126, 247, 199, 0.12);
border-radius: 10px;
padding: 6px 8px;
display: flex;
flex-direction: column;
gap: 2px;
font-size: 0.85rem;
color: var(--muted);
}
.stat strong {
color: var(--text);
font-size: 1rem;
}
.chip {
display: inline-block;
padding: 2px 8px;
border-radius: 999px;
border: 1px solid rgba(126, 247, 199, 0.2);
font-size: 0.75rem;
color: var(--accent);
background: rgba(126, 247, 199, 0.08);
}
.table-compact {
width: 100%;
border-collapse: collapse;
font-size: 0.82rem;
color: var(--text);
}
.table-compact th,
.table-compact td {
border-bottom: 1px solid rgba(126, 247, 199, 0.12);
padding: 4px 6px;
text-align: left;
}
.table-compact th {
color: var(--muted);
font-weight: 600;
letter-spacing: 0.05em;
text-transform: uppercase;
font-size: 0.75rem;
}
.tree {
list-style: none;
padding-left: 14px;
margin: 0;
border-left: 1px solid rgba(126, 247, 199, 0.2);
}
.tree li {
margin: 4px 0;
color: var(--muted);
font-size: 0.88rem;
}
.debug-bar {
display: flex;
align-items: center;
gap: 10px;
margin: 10px 0 0;
}
.debug-bar button {
border: 1px solid rgba(126, 247, 199, 0.3);
background: rgba(126, 247, 199, 0.1);
color: var(--accent);
border-radius: 10px;
padding: 6px 10px;
cursor: pointer;
}
.insight-card pre {
margin: 6px 0 0;
background: rgba(2, 6, 10, 0.7);
border: 1px solid rgba(126, 247, 199, 0.12);
border-radius: 10px;
padding: 8px;
font-family: 'Source Code Pro', monospace;
font-size: 0.8rem;
color: var(--text);
overflow-x: auto;
white-space: pre-wrap;
}
.tree {
list-style: none;
padding-left: 14px;
margin: 0;
border-left: 1px solid rgba(126, 247, 199, 0.2);
}
.tree li {
margin: 4px 0;
color: var(--muted);
font-size: 0.88rem;
}
.plan-grid {
grid-column: 1 / -1;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 12px;
}
.plan-card {
min-height: 260px;
display: flex;
flex-direction: column;
}
.plan-card .tree {
flex: 1;
overflow: auto;
padding-left: 8px;
}
.terminal-wrapper {
margin-top: 12px;
padding: 0;
border-radius: 20px;
border: 1px solid var(--border);
box-shadow: 0 30px 80px rgba(0, 0, 0, 0.6);
background: var(--terminal);
position: relative;
overflow: hidden;
}
.terminal-header {
display: flex;
align-items: center;
gap: 10px;
padding: 12px 18px;
border-bottom: 1px solid rgba(126, 247, 199, 0.1);
background: rgba(1, 3, 4, 0.8);
}
.lights {
display: inline-flex;
gap: 6px;
}
.dot {
width: 11px;
height: 11px;
border-radius: 50%;
background: rgba(126, 247, 199, 0.32);
box-shadow: 0 0 6px rgba(126, 247, 199, 0.7);
}
.dot.warn {
background: rgba(247, 201, 72, 0.4);
box-shadow: 0 0 6px rgba(247, 201, 72, 0.7);
}
.dot.error {
background: rgba(255, 123, 114, 0.4);
box-shadow: 0 0 6px rgba(255, 123, 114, 0.7);
}
.terminal-title {
letter-spacing: 0.3em;
font-size: 0.75rem;
color: var(--muted);
text-transform: uppercase;
display: inline-flex;
align-items: center;
gap: 8px;
}
.terminal-title img {
width: 48px;
height: 32px;
border-radius: 10px;
border: 1px solid rgba(126, 247, 199, 0.3);
box-shadow: 0 0 12px rgba(126, 247, 199, 0.25);
object-fit: cover;
}
.status-chip {
margin-left: auto;
padding: 4px 10px;
border-radius: 999px;
border: 1px solid rgba(126, 247, 199, 0.4);
letter-spacing: 0.3em;
font-size: 0.65rem;
text-transform: uppercase;
}
.status-chip.busy {
border-color: var(--warn);
color: var(--warn);
}
.status-chip.error {
border-color: var(--error);
color: var(--error);
}
.terminal-body {
height: 680px;
overflow-y: auto;
padding: 22px 26px;
font-family: 'Source Code Pro', monospace;
position: relative;
background: linear-gradient(145deg, rgba(9, 20, 29, 0.85), rgba(4, 11, 17, 0.9));
}
.terminal-body::-webkit-scrollbar {
width: 8px;
}
.terminal-body::-webkit-scrollbar-thumb {
background: rgba(126, 247, 199, 0.14);
border-radius: 8px;
}
.log-line {
white-space: pre-wrap;
margin-bottom: 9px;
color: var(--text);
font-size: 0.95rem;
}
.log-line.muted {
color: var(--muted);
}
.log-line.error {
color: var(--error);
}
.log-line.success {
color: var(--accent);
}
.log-prompt {
color: var(--accent);
margin-right: 8px;
}
.log-block {
background: rgba(3, 8, 12, 0.88);
border: 1px solid rgba(126, 247, 199, 0.22);
border-radius: 14px;
padding: 14px 16px;
font-size: 0.95rem;
line-height: 1.5;
margin-bottom: 16px;
overflow-x: auto;
box-shadow: inset 0 0 35px rgba(0, 0, 0, 0.45);
}
.terminal-input {
border-top: 1px solid rgba(126, 247, 199, 0.1);
padding: 16px 22px 20px;
background: rgba(0, 3, 5, 0.85);
}
.terminal-input form {
display: flex;
gap: 12px;
align-items: flex-start;
border: 1px solid rgba(126, 247, 199, 0.2);
border-radius: 12px;
padding: 12px 14px;
background: rgba(2, 6, 10, 0.6);
}
.terminal-input form:focus-within {
border-color: rgba(126, 247, 199, 0.5);
box-shadow: 0 0 12px rgba(126, 247, 199, 0.18);
}
.prompt-label {
color: var(--accent);
font-family: 'Source Code Pro', monospace;
padding-top: 4px;
}
textarea {
flex: 1;
border: none;
background: transparent;
font-family: 'Source Code Pro', monospace;
font-size: 0.95rem;
color: var(--text);
resize: none;
outline: none;
line-height: 1.5;
caret-color: var(--accent);
}
.terminal-hint {
margin-top: 8px;
font-size: 0.8rem;
color: var(--muted);
}
@media (max-width: 720px) {
#root {
padding: 32px 0 64px;
}
.terminal-body {
height: 480px;
}
.hero-card {
padding: 24px;
}
.feature-pane {
grid-template-columns: 1fr;
}
.terminal-input form {
flex-direction: column;
}
.prompt-label {
padding-top: 0;
}
}
@keyframes scan {
0% {
transform: translateY(0);
}
100% {
transform: translateY(2px);
}
}
@keyframes aurora {
0% {
transform: rotate(0deg) scale(1);
}
100% {
transform: rotate(20deg) scale(1.1);
}
}
@keyframes floatGlow {
0% {
transform: translateY(0);
box-shadow: 0 30px 90px rgba(0, 0, 0, 0.6);
}
50% {
transform: translateY(-6px);
box-shadow: 0 20px 70px rgba(0, 0, 0, 0.5), 0 0 25px rgba(126, 247, 199, 0.25);
}
100% {
transform: translateY(0);
box-shadow: 0 30px 90px rgba(0, 0, 0, 0.6);
}
}
@keyframes parallaxDrift {
0% {
transform: translate3d(-40px, -30px, 0);
}
100% {
transform: translate3d(40px, 30px, 0);
}
}
@keyframes shimmer {
0% {
opacity: 0;
transform: scale(0.8);
}
50% {
opacity: 0.7;
transform: scale(1.2);
}
100% {
opacity: 0;
transform: scale(1.5);
}
}
@keyframes drift {
0% {
transform: translateY(0) rotate(0deg);
}
100% {
transform: translateY(60px) rotate(4deg);
}
}
@keyframes floatParticle {
0% {
transform: translate3d(var(--x, 0), 120vh, 0) scale(0.9);
}
100% {
transform: translate3d(calc(var(--x, 0) + 40px), -10vh, 0) scale(1.1);
}
}
@keyframes pulseGlow {
0%,
100% {
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4), 0 0 12px rgba(126, 247, 199, 0.15);
}
50% {
box-shadow: 0 18px 40px rgba(0, 0, 0, 0.45), 0 0 25px rgba(126, 247, 199, 0.3);
}
}
</style>
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div class="parallax-layer layer-one"></div>
<div class="parallax-layer layer-two"></div>
<div class="bg-particles">
<span style="--x:5vw; animation-delay: 0s;"></span>
<span style="--x:25vw; animation-delay: 3s;"></span>
<span style="--x:45vw; animation-delay: 6s;"></span>
<span style="--x:65vw; animation-delay: 2s;"></span>
<span style="--x:85vw; animation-delay: 5s;"></span>
<span style="--x:15vw; animation-delay: 8s;"></span>
</div>
<div id="root"></div>
<script type="text/babel">
const { useState, useEffect, useMemo, useRef } = React;
const DOCS_URL = 'https://feichai0017.github.io/QuillSQL/';
const GITHUB_URL = 'https://github.com/feichai0017/QuillSQL';
const PROFILE_URL = 'https://github.com/feichai0017';
const STORAGE_KEY = 'quillsql:endpoint';
const DEFAULT_ENDPOINT = '/api/sql';
const HERO_STATS = [
{ label: 'Isolation', value: 'MVCC + 2PL', detail: 'read-uncommitted → serializable', icon: 'mvcc', progress: 100 },
{ label: 'Storage', value: 'Heap + B+Tree', detail: 'WAL + checkpoints, buffer pool', icon: 'storage', progress: 92 },
{ label: 'Executor', value: 'Volcano', detail: 'physical planner & runtime', icon: 'executor', progress: 88 },
{ label: 'Tests', value: 'sqllogictest', detail: 'suite-backed demo scripts', icon: 'tests', progress: 80 },
];
const HERO_ICONS = {
mvcc: (
<svg viewBox="0 0 32 32">
<circle cx="16" cy="16" r="10" opacity="0.4" />
<circle cx="16" cy="16" r="6" opacity="0.8" />
<path d="M8 16h16" />
<path d="M16 8v16" />
</svg>
),
storage: (
<svg viewBox="0 0 32 32">
<rect x="8" y="8" width="16" height="6" rx="2" />
<rect x="8" y="15" width="16" height="6" rx="2" />
<rect x="8" y="22" width="16" height="6" rx="2" />
</svg>
),
executor: (
<svg viewBox="0 0 32 32">
<circle cx="16" cy="10" r="3" />
<circle cx="10" cy="22" r="3" />
<circle cx="22" cy="22" r="3" />
<path d="M16 13v4" />
<path d="M16 17l-6 3" />
<path d="M16 17l6 3" />
</svg>
),
tests: (
<svg viewBox="0 0 32 32">
<path d="M6 10h20" />
<path d="M6 16h14" />
<path d="M6 22h10" />
<polyline points="20 20 23 23 28 16" />
</svg>
),
};
const formatNumber = (value) => {
if (value === null || value === undefined) {
return '––';
}
if (value >= 1_000_000) {
return `${(value / 1_000_000).toFixed(1)}M`;
}
if (value >= 1_000) {
return `${(value / 1_000).toFixed(1)}K`;
}
return String(value);
};
const FEATURE_SECTIONS = {
transactions: {
title: 'Transactions / MVCC',
body: 'Snapshot isolation, row/table locks, MVCC vacuum workers, transaction snapshots, and timeline tooling.',
bullets: [
'BEGIN / COMMIT / ROLLBACK with \\\\set transaction',
'Lock manager + deadlock detector hooks',
'Background vacuum honoring oldest active txn',
],
},
storage: {
title: 'Storage & WAL',
body: 'Heap with MVCC tuple headers, WAL with checkpoints, buffer pool featuring LRU-K + TinyLFU and io_uring scheduler.',
bullets: [
'ARIES-style redo/undo with control files',
'Prefetch API and streaming scans for large tables',
'B+Tree with OLC, B-link pages, WAL-coded operations',
],
},
execution: {
title: 'Planner & Executor',
body: 'Logical optimizer rules, Volcano-style executor, expression engine, and plan visualization for teaching.',
bullets: [
'Push-down rules (filter/limit), limit merge',
'Physical planner bridging scans/indexes/joins',
'Explain output for logical & physical plans',
],
},
teaching: {
title: 'Teaching Stack',
body: 'Module boundaries mirror CMU 15-445 for labs, docs surfaced in mdBook, CLI + web shell for demos.',
bullets: [
'sqllogictest based labs, ready-to-run scripts',
'Docs + CLI + HTTP API for integration',
'Pluggable components for experiments',
],
},
};
const DEMO_SCRIPTS = [
{
title: 'Analytics',
body: 'Orders per region with aggregation.',
sql: 'SELECT region, COUNT(*) FROM orders GROUP BY region ORDER BY 2 DESC;',
plan: [
{ label: 'Aggregate', desc: 'Hash Aggregate' },
{ label: 'Join', desc: 'Hash Join (orders ↔ regions)' },
{ label: 'Scan', desc: 'Seq Scan orders' },
{ label: 'Scan', desc: 'Seq Scan regions' },
],
},
{
title: 'MVCC Snapshot',
body: 'Repeatable-read account snapshot.',
sql: 'BEGIN ISOLATION LEVEL REPEATABLE READ; SELECT * FROM accounts WHERE balance > 1000;',
plan: [
{ label: 'Filter', desc: 'Predicate balance > 1000' },
{ label: 'Scan', desc: 'Seq Scan accounts (MVCC visible)' },
],
},
{
title: 'Index Probe',
body: 'Email LIKE search via index.',
sql: 'CREATE INDEX idx_users_email ON users(email); SELECT * FROM users WHERE email LIKE \'%quill%\';',
plan: [
{ label: 'Filter', desc: 'email LIKE %quill%' },
{ label: 'Index Scan', desc: 'idx_users_email' },
{ label: 'Table Fetch', desc: 'Heap lookup users' },
],
},
{
title: 'Join + Aggregate',
body: 'Customer revenue summary.',
sql: 'SELECT c.name, SUM(o.total) FROM customers c JOIN orders o ON c.id = o.customer_id GROUP BY c.name;',
plan: [
{ label: 'Aggregate', desc: 'Group by c.name' },
{ label: 'Join', desc: 'Nested Loop Join' },
{ label: 'Seq Scan', desc: 'orders' },
{ label: 'Index Scan', desc: 'customers idx_id' },
],
},
];
const BLOG_POSTS = [
{
title: 'Async Disk & io_uring',
excerpt: 'We rolled out a dedicated disk scheduler backed by io_uring, SQPOLL, and fixed buffer pools to keep QuillSQL’s I/O path non-blocking.',
date: '2025-10-03',
},
{
title: 'MVCC + 2PL Rollout',
excerpt: 'The storage/transaction layers now expose full MVCC snapshots plus lock-manager integrations, making isolation-level experiments far easier.',
date: '2025-11-18',
},
{
title: 'ARIES-style WAL Revamp',
excerpt: 'Index WAL payloads, refined redo/undo phases, and refreshed docs bring QuillSQL’s durability story closer to production-grade systems.',
date: '2025-11-21',
},
{
title: 'Optimizer & Executor Notes',
excerpt: 'We expanded the rule-based optimizer (filter/limit pushes, merge limit) and polished Volcano operators so students can instrument execution paths quickly.',
date: '2025-11-10',
},
{
title: 'Teaching-Friendly Tooling',
excerpt: 'New mdBook docs, cli/web shells, and sqllogictest-driven demos help instructors drop QuillSQL into lab exercises without extra scaffolding.',
date: '2025-12-01',
},
];
const HELP_TEXT = [
'Built-in commands:',
' \\help Show this message',
' \\docs Open the QuillSQL docs',
' \\github Open the GitHub repo',
' \\profile Maintainer profile',
' \\history List submitted SQL blocks',
' \\endpoint Show current SQL endpoint',
' \\endpoint <value> Override endpoint (use `default` to reset)',
' \\clear Clear the terminal output',
'',
'Regular input is treated as SQL. Statements are split by semicolons before hitting the HTTP API.',
].join('\n');
const ASCII_LOGO = [
' ██████╗ ██╗ ██╗██╗██╗ ██╗ ███████╗ ██████╗ ██╗ ',
' ██╔═══██╗██║ ██║██║██║ ██║ ██╔════╝██╔═══██╗██║ ',
' ██║ ██║██║ ██║██║██║ ██║ ███████╗██║ ██║██║ ',
' ██║▄▄ ██║██║ ██║██║██║ ██║ ╚════██║██║▄▄ ██║██║ ',
' ╚██████╔╝╚██████╔╝██║███████╗███████╗███████║╚██████╔╝███████╗ ',
' ╚══▀▀═╝ ╚═════╝ ╚═╝╚══════╝╚══════╝╚══════╝ ╚══▀▀═╝ ╚══════╝ ',
'-----------------------------------------------------------------',
' QuillSQL • Interactive Terminal • `\\help` ',
].join('\n');
const metaCommands = new Set(['help', 'docs', 'github', 'profile', 'history', 'clear', 'endpoint']);
const splitStatements = (text) => (
text
.split(';')
.map((stmt) => stmt.trim())
.filter(Boolean)
.map((stmt) => stmt.endsWith(';') ? stmt : `${stmt};`)
);
const formatRows = (rows) => {
if (!rows || rows.length === 0) {
return '✓ OK (0 rows)';
}
const widths = [];
rows.forEach((row) => (
row.forEach((cell, idx) => {
const len = String(cell ?? '').length;
widths[idx] = Math.max(widths[idx] || 0, len);
})
));
return rows
.map((row) => row.map((cell, idx) => String(cell ?? '').padEnd(widths[idx])).join(' | '))
.join('\n');
};
const getStoredEndpoint = () => localStorage.getItem(STORAGE_KEY) || DEFAULT_ENDPOINT;
function Hero() {
return (
<section className="hero">
<div className="hero-card">
<div className="hero-copy">
<span className="eyebrow">QuillSQL</span>
<h1>Rust RDBMS for CMU 15-445 style teaching.</h1>
<p>
Single-binary database engine featuring MVCC transactions, ARIES-inspired WAL/recovery, B+Tree indexes
and a Volcano executor. Use the live terminal below to run SQL against a real QuillSQL instance.
</p>
<div className="hero-actions">
<button onClick={() => window.open(DOCS_URL, '_blank', 'noopener')}>Docs</button>
<button onClick={() => window.open(GITHUB_URL, '_blank', 'noopener')}>GitHub</button>
<button onClick={() => window.open(PROFILE_URL, '_blank', 'noopener')}>Profile</button>
</div>
</div>
</div>
<aside className="info-panel">
<ul>
{HERO_STATS.map((stat) => (
<li className="info-row" key={stat.label}>
<div className="info-icon">{HERO_ICONS[stat.icon]}</div>
<div className="info-meta">
<h3>{stat.label}</h3>
<strong>{stat.value}</strong>
<p>{stat.detail}</p>
<div className="info-progress">
<span style={{ transform: `scaleX(${stat.progress / 100})` }} />
</div>
</div>
</li>
))}
</ul>
</aside>
</section>
);
}
function FeatureTabs() {
const keys = Object.keys(FEATURE_SECTIONS);
const [active, setActive] = useState(keys[0]);
const data = FEATURE_SECTIONS[active];
return (
<section className="feature-tabs">
<div className="feature-controls">
{keys.map((key) => (
<button
key={key}
className={key === active ? 'active' : ''}
onClick={() => setActive(key)}
>
{FEATURE_SECTIONS[key].title.split(' / ')[0]}
</button>
))}
</div>
<div className="feature-pane">
<div className="copy">
<h3>{data.title}</h3>
<p>{data.body}</p>
<ul>
{data.bullets.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</div>
<div className="micro-graphic">
{active === 'transactions' ? (
<div className="mvcc-timeline">
<div className="mvcc-event">
<label>T0</label>
<span style={{ '--fill': '30%' }}></span>
<small>Txn A read</small>
</div>
<div className="mvcc-event">
<label>T1</label>
<span style={{ '--fill': '60%' }}></span>
<small>Txn B update</small>
</div>
<div className="mvcc-event">
<label>T2</label>
<span style={{ '--fill': '90%' }}></span>
<small>Commit + vacuum</small>
</div>
</div>
) : (
<div className="mvcc-timeline">
{active === 'storage' && (
<>
<div className="mvcc-event">
<label>WAL</label>
<span style={{ '--fill': '70%' }}></span>
<small>durability</small>
</div>
<div className="mvcc-event">
<label>Buffer</label>
<span style={{ '--fill': '55%' }}></span>
<small>LRU-K / TinyLFU</small>
</div>
<div className="mvcc-event">
<label>B+Tree</label>
<span style={{ '--fill': '80%' }}></span>
<small>OLC + WAL</small>
</div>
</>
)}
{active === 'execution' && (
<>
<div className="mvcc-event">
<label>Optimizer</label>
<span style={{ '--fill': '50%' }}></span>
<small>rules</small>
</div>
<div className="mvcc-event">
<label>Planner</label>
<span style={{ '--fill': '65%' }}></span>
<small>physical tree</small>
</div>
<div className="mvcc-event">
<label>Executor</label>
<span style={{ '--fill': '85%' }}></span>
<small>Volcano</small>
</div>
</>
)}
{active === 'teaching' && (
<>
<div className="mvcc-event">
<label>Docs</label>
<span style={{ '--fill': '60%' }}></span>
<small>mdBook</small>
</div>
<div className="mvcc-event">
<label>Labs</label>
<span style={{ '--fill': '70%' }}></span>
<small>sqllogictest</small>
</div>
<div className="mvcc-event">
<label>CLI</label>
<span style={{ '--fill': '55%' }}></span>
<small>Rust REPL</small>
</div>
</>
)}
</div>
)}
</div>
</div>
</section>
);
}
function DemoShowcase() {
const [activeIdx, setActiveIdx] = useState(0);
const active = DEMO_SCRIPTS[activeIdx];
return (
<section>
<h2 style={{ letterSpacing: '0.2em', textTransform: 'uppercase', fontSize: '0.9rem', color: 'var(--muted)', margin: '0 0 12px' }}>
Demo Scripts
</h2>
<div className="demo-grid">
{DEMO_SCRIPTS.map((demo, idx) => (
<article
className={`demo-card ${idx === activeIdx ? 'active' : ''}`}
key={demo.title}
onMouseEnter={() => setActiveIdx(idx)}
onFocus={() => setActiveIdx(idx)}
tabIndex={0}
>
<h5>{demo.title}</h5>
<p>{demo.body}</p>
</article>
))}
</div>
<section className="plan-visual">
<h2 style={{ letterSpacing: '0.2em', textTransform: 'uppercase', fontSize: '0.85rem', color: 'var(--muted)', margin: '0 0 12px' }}>
Execution Plan Preview
</h2>
<div className="plan-sql">{active.sql}</div>
<div className="plan-tree">
{active.plan.map((node, idx) => (
<div className="plan-node" key={`${node.label}-${idx}`}>
<h6>{node.label}</h6>
<p>{node.desc}</p>
</div>
))}
</div>
</section>
</section>
);
}
function Terminal() {
const [endpoint, setEndpoint] = useState(getStoredEndpoint);
const [log, setLog] = useState(() => ([
{ type: 'block', text: ASCII_LOGO },
{ type: 'line', variant: 'muted', text: 'Welcome to the QuillSQL web shell. Type \\help for built-ins.' },
{ type: 'line', variant: 'muted', text: `Current SQL endpoint: ${getStoredEndpoint()}` },
]));
const [input, setInput] = useState('');
const [status, setStatus] = useState('ready');
const [insights, setInsights] = useState(null);
const historyRef = useRef([]);
const [historyIndex, setHistoryIndex] = useState(0);
const logRef = useRef(null);
useEffect(() => {
logRef.current?.scrollTo({ top: logRef.current.scrollHeight });
}, [log]);
const appendEntry = (entry) => {
setLog((prev) => [...prev, entry]);
};
const appendLine = (text, variant = '') => appendEntry({ type: 'line', variant, text });
const appendBlock = (text) => appendEntry({ type: 'block', text });
const appendPrompt = (text) => appendEntry({ type: 'prompt', text });
const resetLog = () => {
setLog([
{ type: 'block', text: ASCII_LOGO },
{ type: 'line', variant: 'muted', text: 'Welcome to the QuillSQL web shell. Type \\help for built-ins.' },
{ type: 'line', variant: 'muted', text: `Current SQL endpoint: ${endpoint}` },
]);
};
const [debugEnabled, setDebugEnabled] = useState(true);
const handleMeta = (raw) => {
const trimmed = raw.trim().replace(/^\\/, '');
const [cmd, ...rest] = trimmed.split(/\s+/);
const args = rest.join(' ').trim();
switch ((cmd || '').toLowerCase()) {
case 'help':
appendBlock(HELP_TEXT);
break;
case 'docs':
window.open(DOCS_URL, '_blank', 'noopener');
appendLine(`Opened docs: ${DOCS_URL}`, 'muted');
break;
case 'github':
window.open(GITHUB_URL, '_blank', 'noopener');
appendLine(`Opened repository: ${GITHUB_URL}`, 'muted');
break;
case 'profile':
window.open(PROFILE_URL, '_blank', 'noopener');
appendLine(`Opened maintainer profile: ${PROFILE_URL}`, 'muted');
break;
case 'history':
if (historyRef.current.length === 0) {
appendLine('History is empty.', 'muted');
} else {
appendBlock(historyRef.current.map((cmd, idx) => `${String(idx + 1).padStart(2, '0')}: ${cmd}`).join('\n'));
}
break;
case 'clear':
resetLog();
break;
case 'endpoint':
if (!args) {
appendLine(`Current SQL endpoint: ${endpoint}`, 'muted');
} else if (args === 'default' || args === 'reset') {
localStorage.removeItem(STORAGE_KEY);
setEndpoint(DEFAULT_ENDPOINT);
appendLine(`Endpoint reset to ${DEFAULT_ENDPOINT}`, 'success');
} else {
localStorage.setItem(STORAGE_KEY, args);
setEndpoint(args);
appendLine(`Endpoint set to ${args}`, 'success');
}
break;
default:
appendLine(`Unknown command: ${cmd}`, 'error');
}
};
const fetchDebugJson = async (path) => {
const res = await fetch(path);
if (!res.ok) {
const text = await res.text();
throw new Error(text || `HTTP ${res.status}`);
}
return res.json();
};
const pullDebugInsights = async () => {
if (!debugEnabled) return;
try {
const [wal, walPeek, walSegments, buffer, locks, txns, mvcc, trace, plan] = await Promise.all([
fetchDebugJson('/debug/wal/head'),
fetchDebugJson('/debug/wal/peek?limit=12'),
fetchDebugJson('/debug/wal/segments'),
fetchDebugJson('/debug/buffer/stats'),
fetchDebugJson('/debug/locks/snapshot'),
fetchDebugJson('/debug/txns'),
fetchDebugJson('/debug/mvcc/versions'),
fetchDebugJson('/debug/trace/last'),
fetchDebugJson('/debug/plan/last'),
]);
setInsights({ wal, walPeek, walSegments, buffer, locks, txns, mvcc, trace, plan });
} catch (err) {
appendLine(`debug fetch failed: ${err.message}`, 'muted');
}
};
const runSQL = async (sqlText) => {
const statements = splitStatements(sqlText);
if (!statements.length) {
appendLine('No executable SQL detected.', 'muted');
return;
}
historyRef.current.push(sqlText.trim());
setHistoryIndex(historyRef.current.length);
appendLine(`Running ${statements.length} statement(s) via ${endpoint}`, 'success');
setStatus('busy');
try {
for (let i = 0; i < statements.length; i += 1) {
const stmt = statements[i];
appendLine(`#${i + 1} ${stmt.trim()}`, 'muted');
const response = await fetch(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ sql: stmt }),
});
if (!response.ok) {
const errText = await response.text();
throw new Error(errText || `HTTP ${response.status}`);
}
const data = await response.json();
appendBlock(formatRows(Array.isArray(data.rows) ? data.rows : []));
}
appendLine('✓ Done', 'success');
await pullDebugInsights();
setStatus('ready');
} catch (err) {
appendLine(`❌ ${err.message}`, 'error');
setStatus('error');
}
};
const handleSubmit = () => {
const value = input;
appendPrompt(value || '(empty)');
if (!value.trim()) {
appendLine('(empty)', 'muted');
setInput('');
return;
}
if (value.trim().startsWith('\\')) {
handleMeta(value);
} else {
const keyword = value.trim().split(/\s+/)[0]?.toLowerCase();
if (metaCommands.has(keyword)) {
handleMeta(value);
} else {
runSQL(value);
}
}
setInput('');
};
const onSubmit = (event) => {
event.preventDefault();
handleSubmit();
};
const onKeyDown = (event) => {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
handleSubmit();
} else if (event.key === 'ArrowUp') {
event.preventDefault();
if (historyRef.current.length === 0) return;
const nextIndex = Math.max(0, historyIndex - 1);
setHistoryIndex(nextIndex);
setInput(historyRef.current[nextIndex] || '');
} else if (event.key === 'ArrowDown') {
event.preventDefault();
if (historyRef.current.length === 0) return;
const nextIndex = Math.min(historyRef.current.length, historyIndex + 1);
setHistoryIndex(nextIndex);
setInput(historyRef.current[nextIndex] || '');
}
};
const renderTree = (node) => {
if (!node) return null;
return (
<ul className="tree">
<li>
{node.op}
{node.children?.map((child, idx) => (
<div key={idx}>{renderTree(child)}</div>
))}
</li>
</ul>
);
};
const renderInsightCards = () => {
if (!insights) return null;
const { wal, walPeek, walSegments, buffer, locks, txns, mvcc, trace, plan } = insights;
return (
<div className="insights-grid">
<div className="insight-card">
<h4>WAL</h4>
<div className="insight-grid-row">
<div className="stat">
<span>Durable</span>
<strong>{wal.durable_lsn}</strong>
</div>
<div className="stat">
<span>Max</span>
<strong>{wal.max_assigned_lsn}</strong>
</div>
<div className="stat">
<span>Pending</span>
<strong>{wal.pending_records} rec / {wal.pending_bytes}B</strong>
</div>
<div className="stat">
<span>Checkpoint</span>
<strong>{wal.last_checkpoint_lsn}</strong>
</div>
</div>
<table className="table-compact">
<thead>
<tr>
<th>LSN</th>
<th>Prev</th>
<th>RMID</th>
<th>Info</th>
<th>Len</th>
</tr>
</thead>
<tbody>
{walPeek?.map((row, idx) => (
<tr key={idx}>
<td>{row.lsn}</td>
<td>{row.prev_lsn}</td>
<td>{row.rmid}</td>
<td>{row.info}</td>
<td>{row.body_len}</td>
</tr>
))}
</tbody>
</table>
<table className="table-compact">
<thead>
<tr>
<th>Segment</th>
<th>Size</th>
</tr>
</thead>
<tbody>
{walSegments?.segments?.map((seg) => (
<tr key={seg.id}>
<td>{seg.id}</td>
<td>{seg.size_bytes}</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="insight-card">
<h4>Buffer</h4>
<div className="insight-grid-row">
<div className="stat">
<span>Capacity</span>
<strong>{buffer.capacity}</strong>
</div>
<div className="stat">
<span>Free</span>
<strong>{buffer.free_frames}</strong>
</div>
<div className="stat">
<span>Pinned</span>
<strong>{buffer.pinned_frames}</strong>
</div>
<div className="stat">
<span>Dirty</span>
<strong>{buffer.dirty_frames} ({buffer.dirty_page_table} map)</strong>
</div>
</div>
</div>
<div className="insight-card">
<h4>Locks</h4>
<table className="table-compact">
<thead>
<tr>
<th>From</th>
<th>To</th>
</tr>
</thead>
<tbody>
{locks.wait_for?.map((edge, idx) => (
<tr key={idx}>
<td>{edge.from}</td>
<td>{edge.to}</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="insight-card">
<h4>Txns</h4>
<div className="stat">
<span>Oldest</span>
<strong>{txns.oldest_active ?? '–'}</strong>
</div>
<div className="stat">
<span>Next</span>
<strong>{txns.next_txn_id}</strong>
</div>
<table className="table-compact">
<thead>
<tr>
<th>Txn</th>
<th>Status</th>
<th>Tables</th>
<th>Rows</th>
</tr>
</thead>
<tbody>
{txns.active?.map((t) => (
<tr key={t.txn_id}>
<td>{t.txn_id}</td>
<td>{t.status}</td>
<td>{t.held_tables}</td>
<td>{t.held_rows}</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="insight-card" style={{ gridColumn: 'span 2' }}>
<h4>MVCC</h4>
<table className="table-compact">
<thead>
<tr>
<th>Table</th>
<th>RID</th>
<th>Ins</th>
<th>Del</th>
<th>Vis</th>
</tr>
</thead>
<tbody>
{mvcc.samples?.map((s, idx) => (
<tr key={idx}>
<td>{s.table}</td>
<td>{s.rid}</td>
<td>{s.insert_txn}</td>
<td>{s.delete_txn}</td>
<td>{s.visible ? '✓' : '✗'}</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="insight-card" style={{ gridColumn: 'span 2' }}>
<h4>Trace</h4>
<div className="insight-grid-row">
<div className="stat">
<span>Rows</span>
<strong>{trace?.rows ?? '–'}</strong>
</div>
<div className="stat">
<span>Duration</span>
<strong>{trace?.duration_ms ?? '–'} ms</strong>
</div>
</div>
</div>
<div className="plan-grid">
<div className="insight-card plan-card">
<h4>Logical Plan</h4>
{renderTree(plan?.logical)}
</div>
<div className="insight-card plan-card">
<h4>Physical Plan</h4>
{renderTree(plan?.physical)}
</div>
</div>
</div>
);
};
return (
<>
<section className="terminal-wrapper">
<header className="terminal-header">
<div className="lights">
<span className="dot error" />
<span className="dot warn" />
<span className="dot" />
</div>
<span className="terminal-title">
<img src="rust-db.png" alt="QuillSQL logo" />
QUILLSQL TERMINAL
</span>
<span className={`status-chip ${status !== 'ready' ? status : ''}`}>{status}</span>
</header>
<div className="terminal-body" ref={logRef}>
{log.map((entry, idx) => {
if (entry.type === 'block') {
return <pre className="log-block" key={idx}>{entry.text}</pre>;
}
if (entry.type === 'prompt') {
return (
<div className="log-line" key={idx}>
<span className="log-prompt">quill@tty:~$</span>
{entry.text}
</div>
);
}
return (
<div className={`log-line ${entry.variant || ''}`} key={idx}>
{entry.text}
</div>
);
})}
</div>
<div className="terminal-input">
<form onSubmit={onSubmit}>
<span className="prompt-label">quill@tty:~$</span>
<textarea
rows={1}
value={input}
onChange={(event) => setInput(event.target.value)}
onKeyDown={onKeyDown}
placeholder={"Type SQL or \\help · Enter to run · Shift+Enter for newline"}
/>
</form>
<div className="terminal-hint">
Commands: \help · \docs · \github · \profile · \history · \endpoint · Any other input executes SQL.
</div>
<div className="debug-bar">
<label style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
<input
type="checkbox"
checked={debugEnabled}
onChange={(e) => setDebugEnabled(e.target.checked)}
/>
Debug mode
</label>
<button onClick={() => fetch('/admin/rebuild', { method: 'POST' }).then(() => window.location.reload())}>
Rebuild DB
</button>
</div>
</div>
</section>
{renderInsightCards()}
</>
);
}
function HomePage() {
return (
<div className="page-section">
<Hero />
<FeatureTabs />
</div>
);
}
const GITHUB_API_HEADERS = {
Accept: 'application/vnd.github+json',
'X-GitHub-Api-Version': '2022-11-28',
};
function StatsBar() {
const [stars, setStars] = useState(null);
const [downloads, setDownloads] = useState(null);
useEffect(() => {
fetch('https://api.github.com/repos/feichai0017/QuillSQL', {
headers: GITHUB_API_HEADERS,
})
.then((res) => {
if (!res.ok) {
throw new Error('github');
}
return res.json();
})
.then((data) => setStars(data.stargazers_count))
.catch(() => setStars(null));
fetch('https://crates.io/api/v1/crates/quill-sql')
.then((res) => {
if (!res.ok) {
throw new Error('crates');
}
return res.json();
})
.then((data) => setDownloads(data.crate?.downloads))
.catch(() => setDownloads(null));
}, []);
return (
<div className="stats-bar">
<span className="stat-pill">
GitHub ⭐ {formatNumber(stars)}
</span>
<span className="stat-pill">
Crates ⬇ {formatNumber(downloads)}
</span>
</div>
);
}
function DemoPage() {
return (
<div className="page-section">
<Terminal />
</div>
);
}
function BlogPage() {
return (
<div className="page-section">
<section>
<h2
style={{
letterSpacing: '0.3em',
textTransform: 'uppercase',
fontSize: '0.95rem',
color: 'var(--muted)',
margin: '0 0 16px'
}}
>
Blog & Updates
</h2>
<div className="blog-grid">
{BLOG_POSTS.map((post) => (
<article className="blog-card" key={post.title}>
<small>{post.date}</small>
<h4>{post.title}</h4>
<p>{post.excerpt}</p>
</article>
))}
</div>
</section>
</div>
);
}
function App() {
const [page, setPage] = useState('home');
return (
<>
<nav className="navbar">
<div className="brand">QUILLSQL</div>
<div className="nav-links">
<button className={page === 'home' ? 'active' : ''} onClick={() => setPage('home')}>
Home
</button>
<button className={page === 'demo' ? 'active' : ''} onClick={() => setPage('demo')}>
Demo
</button>
<button className={page === 'blog' ? 'active' : ''} onClick={() => setPage('blog')}>
Blog
</button>
</div>
</nav>
<StatsBar />
{page === 'home' && <HomePage />}
{page === 'demo' && <DemoPage />}
{page === 'blog' && <BlogPage />}
</>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
</script>
</body>
</html>