Browse Source

add more styling to game

pull/1/head
Stephanie Gredell 7 months ago
parent
commit
e45bc45ea5
  1. 264
      game.html

264
game.html

@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>System Design Canvas Game</title> <title>System Design Game</title>
<style> <style>
* { * {
box-sizing: border-box; box-sizing: border-box;
@ -11,22 +11,22 @@
body { body {
margin: 0; margin: 0;
display: flex;
height: 100vh; height: 100vh;
font-family: 'Fira Code', monospace; font-family: 'JetBrains Mono', monospace;
background-color: #1e1e1e; background-color: #161b22;
color: #ccc; color: #ccc;
display: flex;
flex-direction: row;
height: 100vh;
} }
#sidebar { #sidebar {
width: 280px; width: 100%;
background-color: #111; background-color: #111;
border-right: 1px solid #333;
padding: 12px;
display: flex; display: flex;
flex-direction: column; flex-direction: row;
gap: 12px; gap: 12px;
overflow-y: auto; flex-wrap: wrap;
} }
.component-icon, #arrow-tool { .component-icon, #arrow-tool {
@ -37,13 +37,14 @@
text-align: center; text-align: center;
cursor: grab; cursor: grab;
user-select: none; user-select: none;
font-size: 13px; font-size: 16px;
transition: background-color 0.1s ease; transition: background-color 0.1s ease;
color: #eee; color: rgb(204, 204, 204);
} }
.component-icon:hover, #arrow-tool:hover { .component-icon:hover, #arrow-tool:hover {
background-color: #2a2a2a; background-color: #2a2a2a;
border: 1px solid #00ff88;
} }
.component-icon:active, #arrow-tool:active { .component-icon:active, #arrow-tool:active {
@ -59,13 +60,30 @@
#canvas-container { #canvas-container {
flex: 1; flex: 1;
position: relative; position: relative;
overflow: hidden; background: #121212;
background-color: #202020; box-sizing: border-box;
height: 100%;
margin: 16px 0 0;
} }
#canvas-wrapper {
flex: 1;
display: flex;
flex-direction: column;
border-radius: 8px;
border: 2px solid #30363d;
overflow: hidden;
background: #121212;
margin: 12px 12px 12px 0;
padding: 16px;
}
#canvas { #canvas {
width: 100%; width: 100%;
height: 100%; height: 100%;
background: #121212;
border: 2px dashed #30363d;
border-radius: 8px;
} }
.dropped { .dropped {
@ -154,8 +172,8 @@
#info-panel { #info-panel {
position: absolute; position: absolute;
top: 1rem; top: 12px;
right: 1rem; right: 12px;
background: #121212; background: #121212;
color: #ccc; color: #ccc;
padding: 1rem; padding: 1rem;
@ -193,9 +211,8 @@
.component-icon { .component-icon {
position: relative; position: relative;
padding: 8px; padding: 8px;
margin-bottom: 6px;
background: #1e1e1e; background: #1e1e1e;
color: white; color: rgb(204, 204, 204);
border: 1px solid #444; border: 1px solid #444;
border-radius: 6px; border-radius: 6px;
cursor: grab; cursor: grab;
@ -222,122 +239,205 @@
.component-icon:hover .tooltip { .component-icon:hover .tooltip {
visibility: visible; visibility: visible;
opacity: 1; opacity: 1;
z-index:1000;
} }
.component-icon.dragging .tooltip { .component-icon.dragging .tooltip {
display: none; display: none;
} }
#challenge-container {
width: 15%;
box-sizing: border-box;
margin-left: 16px;
margin-top: 24px;
}
.tabs {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
}
.tab-labels {
display: flex;
cursor: pointer;
}
.tab-labels label {
padding: 10px 20px;
background: #161b22;
margin-right: 4px;
margin-bottom: 20px;
border-radius: 4px;
}
.tab-content {
border-top: 1px solid #30363d;
padding: 20px 0 0;
display: none;
height: 100%;
}
input[name="tab"] {
display: none;
}
#tab1:checked ~ .tabs .tab-labels label[for="tab1"],
#tab2:checked ~ .tabs .tab-labels label[for="tab2"],
#tab3:checked ~ .tabs .tab-labels label[for="tab3"] {
background: #1a3d2a;
font-weight: bold;
color: #00ff88;
}
#tab1:checked ~ .tabs #content1,
#tab2:checked ~ .tabs #content2,
#tab3:checked ~ .tabs #content3 {
display: flex;
flex-direction: column;
height: 100%;
overflow:hidden;
}
</style> </style>
</head> </head>
<body> <body>
<div id="info-panel"> <div id="challenge-container">
<div id="constraints-panel"> Challenges
<div class="panel-title">Level Constraints</div>
<div class="panel-metric"><span class="label">🎯 Target RPS:</span> <span id="constraint-rps"></span></div>
<div class="panel-metric"><span class="label"> Max P95 Latency:</span> <span id="constraint-latency"></span></div>
<div class="panel-metric"><span class="label">💸 Max Cost:</span> <span id="constraint-cost"></span></div>
<div class="panel-metric"><span class="label">🔒 Availability:</span> <span id="constraint-availability"></span></div>
</div>
<div id="score-panel">
<div class="panel-title">Simulation Results</div>
<div class="panel-metric"><span class="label">✅ Cost:</span> <span id="score-cost"></span></div>
<div class="panel-metric"><span class="label">⚡ P95 Latency:</span> <span id="score-p95"></span></div>
<div class="panel-metric"><span class="label">📈 Achieved RPS:</span> <span id="score-rps"></span></div>
<div class="panel-metric"><span class="label">🛡 Availability:</span> <span id="score-availability"></span></div>
</div> </div>
<div id="canvas-wrapper">
<input type="radio" id="tab1" name="tab" checked>
<input type="radio" id="tab2" name="tab">
<input type="radio" id="tab3" name="tab">
<div class="tabs">
<div class="tab-labels">
<label for="tab1">Requirements</label>
<label for="tab2">Design</label>
<label for="tab3">Metrics</label>
</div> </div>
<!-- Requirements -->
<div id="content1" class="tab-content">This is Tab 1 content.</div>
<!-- Design-->
<div id="content2" class="tab-content">
<div id="sidebar"> <div id="sidebar">
<div class="component-icon" draggable="true" data-type="Client"> <div class="component-icon" draggable="true" data-type="client">
Client/User user
<span class="tooltip">Simulates user traffic</span> <span class="tooltip">simulates user traffic</span>
</div> </div>
<div class="component-icon" draggable="true" data-type="LoadBalancer"> <div class="component-icon" draggable="true" data-type="loadbalancer">
Load Balancer load balancer
<span class="tooltip">Cost: $5/mo<br>Distributes traffic evenly<br>Latency: 5 ms</span> <span class="tooltip">cost: $5/mo<br>distributes traffic evenly<br>latency: 5 ms</span>
</div> </div>
<div class="component-icon" draggable="true" data-type="WebServerSmall"> <div class="component-icon" draggable="true" data-type="webserversmall">
Web Server (Small) web server (small)
<span class="tooltip">Cost: $10/mo<br>Capacity: 100 RPS<br>Base Latency: 50 ms</span> <span class="tooltip">cost: $10/mo<br>capacity: 100 rps<br>base latency: 50 ms</span>
</div> </div>
<div class="component-icon" draggable="true" data-type="WebServerMedium"> <div class="component-icon" draggable="true" data-type="webservermedium">
Web Server (Medium) web server (medium)
<span class="tooltip">Cost: $20/mo<br>Capacity: 200 RPS<br>Base Latency: 40 ms</span> <span class="tooltip">cost: $20/mo<br>capacity: 200 rps<br>base latency: 40 ms</span>
</div> </div>
<div class="component-icon" draggable="true" data-type="Database"> <div class="component-icon" draggable="true" data-type="database">
Database database
<span class="tooltip">Cost: $20/mo<br>Read Capacity: 150 RPS<br>Base Latency: 80 ms<br>Supports replication</span> <span class="tooltip">cost: $20/mo<br>read capacity: 150 rps<br>base latency: 80 ms<br>supports replication</span>
</div> </div>
<div class="component-icon" draggable="true" data-type="CacheStandard"> <div class="component-icon" draggable="true" data-type="cachestandard">
Cache (Standard) cache (standard)
<span class="tooltip">Cost: $10/mo<br>Capacity: 100 RPS<br>Latency: 5 ms<br>80% hit rate with 1hr TTL</span> <span class="tooltip">cost: $10/mo<br>capacity: 100 rps<br>latency: 5 ms<br>80% hit rate with 1hr ttl</span>
</div> </div>
<div class="component-icon" draggable="true" data-type="CacheLarge"> <div class="component-icon" draggable="true" data-type="cachelarge">
Cache (Large) cache (large)
<span class="tooltip">Cost: $20/mo<br>Capacity: 200 RPS<br>Latency: 5 ms<br>Higher hit rate for large datasets</span> <span class="tooltip">cost: $20/mo<br>capacity: 200 rps<br>latency: 5 ms<br>higher hit rate for large datasets</span>
</div> </div>
<div class="component-icon" draggable="true" data-type="MessageQueue"> <div class="component-icon" draggable="true" data-type="messagequeue">
Message Queue message queue
<span class="tooltip">Cost: $15/mo<br>Decouples components<br>Useful for batching writes</span> <span class="tooltip">cost: $15/mo<br>decouples components<br>useful for batching writes</span>
</div> </div>
<div class="component-icon" draggable="true" data-type="CDN"> <div class="component-icon" draggable="true" data-type="cdn">
CDN/Edge Cache cdn/edge cache
<span class="tooltip">Cost: $0.03/GB<br>Improves global latency<br>Caches static content</span> <span class="tooltip">cost: $0.03/gb<br>improves global latency<br>caches static content</span>
</div> </div>
<div class="component-icon" draggable="true" data-type="Microservice"> <div class="component-icon" draggable="true" data-type="microservice">
Microservice Node microservice node
<span class="tooltip">Cost: $10/mo<br>Stateless container<br>Use for modular logic</span> <span class="tooltip">cost: $10/mo<br>stateless container<br>use for modular logic</span>
</div> </div>
<div class="component-icon" draggable="true" data-type="DataPipeline"> <div class="component-icon" draggable="true" data-type="datapipeline">
Data Pipeline data pipeline
<span class="tooltip">Cost: $25/mo<br>Stream or batch processing<br>Used for analytics / ETL</span> <span class="tooltip">cost: $25/mo<br>stream or batch processing<br>used for analytics / etl</span>
</div> </div>
<div class="component-icon" draggable="true" data-type="Monitoring"> <div class="component-icon" draggable="true" data-type="monitoring">
Monitoring/Alerting monitoring/alerting
<span class="tooltip">Cost: $5/mo<br>Health checks + logs<br>Alerts on failures</span> <span class="tooltip">cost: $5/mo<br>health checks + logs<br>alerts on failures</span>
</div> </div>
<div class="component-icon" draggable="true" data-type="ThirdParty"> <div class="component-icon" draggable="true" data-type="thirdparty">
Third-Party Service third-party service
<span class="tooltip">External APIs<br>Latency + cost vary<br>Examples: Payment, Email, Search</span> <span class="tooltip">external apis<br>latency + cost vary<br>examples: payment, email, search</span>
</div> </div>
<div id="arrow-tool">Arrow Tool</div> <div id="arrow-tool">arrow tool</div>
<button id="run-button" disabled>Run Simulation</button> <button id="run-button" disabled>run simulation</button>
</div> </div>
<div id="canvas-container"> <div id="canvas-container">
<div id="info-panel">
<div id="constraints-panel">
<div class="panel-title">level constraints</div>
<div class="panel-metric"><span class="label">🎯 target rps:</span> <span id="constraint-rps"></span></div>
<div class="panel-metric"><span class="label"> max p95 latency:</span> <span id="constraint-latency"></span></div>
<div class="panel-metric"><span class="label">💸 max cost:</span> <span id="constraint-cost"></span></div>
<div class="panel-metric"><span class="label">🔒 availability:</span> <span id="constraint-availability"></span></div>
</div>
<div id="score-panel">
<div class="panel-title">simulation results</div>
<div class="panel-metric"><span class="label">✅ cost:</span> <span id="score-cost"></span></div>
<div class="panel-metric"><span class="label">⚡ p95 latency:</span> <span id="score-p95"></span></div>
<div class="panel-metric"><span class="label">📈 achieved rps:</span> <span id="score-rps"></span></div>
<div class="panel-metric"><span class="label">🛡 availability:</span> <span id="score-availability"></span></div>
</div>
</div>
<svg id="canvas"> <svg id="canvas">
<defs> <defs>
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto"> <marker id="arrowhead" markerwidth="10" markerheight="7" refx="10" refy="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#333" /> <polygon points="0 0, 10 3.5, 0 7" fill="#333" />
</marker> </marker>
</defs> </defs>
</svg> </svg>
<div id="node-props-panel"> <div id="node-props-panel">
<h3>Node Properties</h3> <h3>node properties</h3>
<div id="label-group"> <div id="label-group">
<label>Label:<input type="text" name="label" /></label> <label>label:<input type="text" name="label" /></label>
</div> </div>
<div id="db-group" class="prop-group"> <div id="db-group" class="prop-group">
<label>Replication Factor:<input type="number" name="replication" min="1" step="1" /></label> <label>replication factor:<input type="number" name="replication" min="1" step="1" /></label>
</div> </div>
<div id="cache-group" class="prop-group"> <div id="cache-group" class="prop-group">
<label>Cache TTL (secs):<input type="number" name="cacheTTL" min="0" step="60" /></label> <label>cache ttl (secs):<input type="number" name="cachettl" min="0" step="60" /></label>
</div>
<button id="node-props-save" disabled>save</button>
</div> </div>
<button id="node-props-save" disabled>Save</button> </div>
</div>
<!-- Metrics-->
<div id="content3" class="tab-content">This is Tab 3 content.</div>
</div> </div>
</div> </div>
@ -370,8 +470,8 @@
x, y, x, y,
width: 0, width: 0,
height: app.componentSize.height, height: app.componentSize.height,
fill: '#e0e0e0', fill: '#121212',
stroke: '#333', stroke: '#00ff88',
'stroke-width': 1, 'stroke-width': 1,
rx: 4, ry: 4 rx: 4, ry: 4
}); });
@ -381,7 +481,7 @@
y: y + app.componentSize.height / 2 + 5, y: y + app.componentSize.height / 2 + 5,
'text-anchor': 'middle', 'text-anchor': 'middle',
'font-size': 14, 'font-size': 14,
fill: '#000' fill: '#ccc'
}); });
this.text.textContent = this.props.label; this.text.textContent = this.props.label;
this.app.canvas.appendChild(this.text); // temporarily append to measure this.app.canvas.appendChild(this.text); // temporarily append to measure

Loading…
Cancel
Save