- Separated JavaScript into script.js for better organization - Added Server-Sent Events (SSE) webhook receiver for real-time results - Created Go service to receive AlpenQueue webhooks and broadcast via SSE - Removed manual webhook input - results stream automatically - Added live connection status indicator - Implemented real-time result cards with animations - Fixed AlpenQueue API field names (webhook_url, selector) - Added dark theme styling for result display - Results appear instantly without polling The portfolio now shows AlpenQueue results in real-time as they arrive!
389 lines
6.0 KiB
CSS
389 lines
6.0 KiB
CSS
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
font-family: 'Courier New', monospace;
|
|
line-height: 1.6;
|
|
padding: 2rem;
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
background: #0a0a0a;
|
|
color: #e0e0e0;
|
|
}
|
|
|
|
h1 {
|
|
font-size: 1.1rem;
|
|
font-weight: normal;
|
|
margin-bottom: 1.5rem;
|
|
color: #4a9e5f;
|
|
}
|
|
|
|
h2 {
|
|
font-size: 1.05rem;
|
|
font-weight: normal;
|
|
color: #4a9e5f;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.intro {
|
|
margin-bottom: 2rem;
|
|
font-size: 0.95rem;
|
|
line-height: 1.7;
|
|
}
|
|
|
|
.project {
|
|
margin: 2rem 0;
|
|
padding: 1.5rem;
|
|
border-left: 2px solid #333;
|
|
background: #111;
|
|
}
|
|
|
|
.project-name {
|
|
color: #4a9e5f;
|
|
font-weight: bold;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.project p {
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
.playground {
|
|
margin: 2.5rem 0;
|
|
padding: 2rem;
|
|
background: #111;
|
|
border: 1px solid #222;
|
|
}
|
|
|
|
.playground-intro {
|
|
margin-bottom: 1.5rem;
|
|
font-size: 0.95rem;
|
|
color: #b0b0b0;
|
|
}
|
|
|
|
#queue-form {
|
|
margin: 1.5rem 0;
|
|
}
|
|
|
|
.form-group {
|
|
margin-bottom: 1.25rem;
|
|
}
|
|
|
|
.form-group label {
|
|
display: block;
|
|
margin-bottom: 0.4rem;
|
|
font-size: 0.95rem;
|
|
color: #e0e0e0;
|
|
}
|
|
|
|
.form-group input {
|
|
width: 100%;
|
|
padding: 0.6rem 0.8rem;
|
|
background: #0a0a0a;
|
|
border: 1px solid #333;
|
|
color: #e0e0e0;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 0.95rem;
|
|
transition: border-color 0.2s;
|
|
}
|
|
|
|
.form-group input:focus {
|
|
outline: none;
|
|
border-color: #4a9e5f;
|
|
}
|
|
|
|
.form-group small {
|
|
display: block;
|
|
margin-top: 0.3rem;
|
|
font-size: 0.85rem;
|
|
color: #666;
|
|
}
|
|
|
|
.form-group small a {
|
|
color: #4a9e5f;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.form-group small a:hover {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
button[type="submit"] {
|
|
background: #4a9e5f;
|
|
color: #fff;
|
|
border: none;
|
|
padding: 0.7rem 1.5rem;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 0.95rem;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
button[type="submit"]:hover:not(:disabled) {
|
|
background: #5fb070;
|
|
}
|
|
|
|
button[type="submit"]:disabled {
|
|
opacity: 0.6;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.response-box {
|
|
margin: 1.5rem 0;
|
|
padding: 1rem;
|
|
border: 1px solid #333;
|
|
background: #0f0f0f;
|
|
min-height: 60px;
|
|
display: none;
|
|
}
|
|
|
|
.response-box.loading,
|
|
.response-box.success,
|
|
.response-box.error {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.response-box.loading {
|
|
border-color: #4a9e5f;
|
|
color: #4a9e5f;
|
|
}
|
|
|
|
.response-box.success {
|
|
border-color: #4a9e5f;
|
|
background: #0a1a0f;
|
|
}
|
|
|
|
.response-box.error {
|
|
border-color: #9e4a4a;
|
|
background: #1a0a0a;
|
|
}
|
|
|
|
.success-icon,
|
|
.error-icon {
|
|
font-size: 1.5rem;
|
|
margin-right: 1rem;
|
|
}
|
|
|
|
.success-icon {
|
|
color: #4a9e5f;
|
|
}
|
|
|
|
.error-icon {
|
|
color: #9e4a4a;
|
|
}
|
|
|
|
.response-content {
|
|
flex: 1;
|
|
}
|
|
|
|
.response-content code {
|
|
background: #222;
|
|
padding: 0.2rem 0.4rem;
|
|
margin: 0 0.2rem;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.spinner {
|
|
display: inline-block;
|
|
width: 16px;
|
|
height: 16px;
|
|
border: 2px solid #4a9e5f;
|
|
border-top-color: transparent;
|
|
border-radius: 50%;
|
|
animation: spin 0.8s linear infinite;
|
|
margin-right: 0.5rem;
|
|
}
|
|
|
|
@keyframes spin {
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
|
|
.stats-live {
|
|
margin-top: 1.5rem;
|
|
display: flex;
|
|
justify-content: space-around;
|
|
border-top: 1px solid #222;
|
|
padding-top: 1rem;
|
|
}
|
|
|
|
.stat-item {
|
|
text-align: center;
|
|
}
|
|
|
|
.stat-label {
|
|
font-size: 0.85rem;
|
|
color: #666;
|
|
display: block;
|
|
}
|
|
|
|
.stat-value {
|
|
font-size: 1.1rem;
|
|
color: #4a9e5f;
|
|
font-weight: bold;
|
|
display: block;
|
|
margin-top: 0.3rem;
|
|
}
|
|
|
|
.stack {
|
|
margin: 2rem 0;
|
|
font-size: 0.95rem;
|
|
}
|
|
|
|
.link {
|
|
margin-top: 1.5rem;
|
|
font-size: 0.95rem;
|
|
}
|
|
|
|
.link a {
|
|
color: #4a9e5f;
|
|
text-decoration: none;
|
|
border-bottom: 1px solid #4a9e5f;
|
|
}
|
|
|
|
.link a:hover {
|
|
color: #5fb070;
|
|
border-bottom-color: #5fb070;
|
|
}
|
|
|
|
/* Live results container */
|
|
.live-results-container {
|
|
margin: 2rem 0;
|
|
padding: 1.5rem;
|
|
background: #111;
|
|
border: 1px solid #222;
|
|
}
|
|
|
|
.results-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 1rem;
|
|
padding-bottom: 0.5rem;
|
|
border-bottom: 1px solid #222;
|
|
}
|
|
|
|
.results-header h3 {
|
|
margin: 0;
|
|
font-size: 1rem;
|
|
color: #4a9e5f;
|
|
font-weight: normal;
|
|
}
|
|
|
|
.connection-status {
|
|
font-size: 0.85rem;
|
|
color: #666;
|
|
}
|
|
|
|
.connection-status.connected {
|
|
color: #4a9e5f;
|
|
}
|
|
|
|
.live-results {
|
|
min-height: 200px;
|
|
max-height: 600px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.no-results {
|
|
text-align: center;
|
|
color: #666;
|
|
padding: 2rem;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.result-item {
|
|
margin-bottom: 1rem;
|
|
padding: 1rem;
|
|
background: #0a0a0a;
|
|
border: 1px solid #222;
|
|
border-left: 3px solid #4a9e5f;
|
|
}
|
|
|
|
.result-item.error {
|
|
border-left-color: #9e4a4a;
|
|
}
|
|
|
|
.result-item.blocked {
|
|
border-left-color: #9e9e4a;
|
|
}
|
|
|
|
.result-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-bottom: 0.5rem;
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
.result-status {
|
|
font-weight: bold;
|
|
padding: 0.2rem 0.5rem;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.result-status.ok {
|
|
background: #1a3a1f;
|
|
color: #4a9e5f;
|
|
}
|
|
|
|
.result-status.error {
|
|
background: #3a1a1a;
|
|
color: #9e4a4a;
|
|
}
|
|
|
|
.result-status.blocked {
|
|
background: #3a3a1a;
|
|
color: #9e9e4a;
|
|
}
|
|
|
|
.result-time {
|
|
color: #666;
|
|
}
|
|
|
|
.result-url {
|
|
font-size: 0.9rem;
|
|
color: #4a9e5f;
|
|
margin-bottom: 0.5rem;
|
|
word-break: break-all;
|
|
}
|
|
|
|
.result-content {
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
.result-content pre {
|
|
margin: 0;
|
|
padding: 0.5rem;
|
|
background: #000;
|
|
border: 1px solid #1a1a1a;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 0.85rem;
|
|
color: #b0b0b0;
|
|
white-space: pre-wrap;
|
|
word-wrap: break-word;
|
|
overflow-x: auto;
|
|
}
|
|
|
|
.result-content em {
|
|
color: #666;
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
@keyframes slideIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(-10px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
/* Hide the old results explanation */
|
|
.results-explanation {
|
|
display: none;
|
|
} |