feat: Introduce new Sidebar and ControlPanel components, and update styling for TUI Segment, Slider, Toggle, and Button components.

This commit is contained in:
syntaxbullet
2026-02-10 11:23:16 +01:00
parent 28bde53707
commit bb4ca0610d
8 changed files with 760 additions and 649 deletions

View File

@@ -1,115 +1,13 @@
---
import Layout from "../layouts/Layout.astro";
import TuiSlider from "../components/TuiSlider.astro";
import TuiSegment from "../components/TuiSegment.astro";
import TuiToggle from "../components/TuiToggle.astro";
import TuiButton from "../components/TuiButton.astro";
import Sidebar from "../components/Sidebar.astro";
import Tooltip from "../components/Tooltip.astro";
import ControlPanel from "../components/ControlPanel.astro";
---
<Layout title="Syntaxbullet - Digital Wizard">
<div class="split-layout">
<!-- LEFT SIDEBAR: Content -->
<aside class="sidebar">
<div class="sidebar-content">
<div class="brand-group">
<h1 class="brand-title">SYNTAXBULLET</h1>
<div class="brand-subtitle">
FULL STACK ENGINEER
<span class="muted">//</span>
CREATIVE TECHNOLOGIST
</div>
</div>
<p class="brand-bio">
Crafting high-performance digital experiences at the
intersection of code, art, and artificial intelligence.
</p>
<div class="sidebar-actions">
<a href="/projects" class="sidebar-link">
<span class="icon">⚡</span> PROJECTS
</a>
<a href="/blog" class="sidebar-link">
<span class="icon">📝</span> BLOG
</a>
<a
href="mailto:contact@syntaxbullet.me"
class="sidebar-link"
>
<span class="icon">✉️</span> CONTACT
</a>
</div>
<div class="sidebar-social">
<a
href="https://github.com/syntaxbullet"
target="_blank"
rel="noopener noreferrer"
aria-label="GitHub"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
><path
d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"
></path><path d="M9 18c-4.51 2-5-2.64-5-2.64"
></path></svg
>
</a>
<a
href="https://linkedin.com/in/syntaxbullet"
target="_blank"
rel="noopener noreferrer"
aria-label="LinkedIn"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
><path
d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z"
></path><rect width="4" height="12" x="2" y="9"
></rect><circle cx="4" cy="4" r="2"></circle></svg
>
</a>
<a
href="https://twitter.com/syntaxbullet"
target="_blank"
rel="noopener noreferrer"
aria-label="Twitter"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
><path
d="M22 4s-.7 2.1-2 3.4c1.6 10-9.4 17.3-18 11.6 2.2.1 4.4-.6 6-2C3 15.5.5 9.6 3 5c2.2 2.6 5.6 4.1 9 4-.9-4.2 4-6.6 7-3.8 1.1 0 3-1.2 3-1.2z"
></path></svg
>
</a>
</div>
</div>
</aside>
<Sidebar />
<!-- RIGHT MAIN: ASCII & Controls -->
<main class="ascii-workspace hero-wrapper">
@@ -120,235 +18,7 @@ import Tooltip from "../components/Tooltip.astro";
<canvas id="ascii-canvas"></canvas>
</div>
<footer class="controls-footer">
<div id="tui-controls">
<!-- Sliders Section -->
<div class="control-panel-section sliders-section">
<div class="section-header">ADJUSTMENTS</div>
<div class="sliders-grid">
<TuiSlider
id="exposure"
label="EXP"
min={0}
max={3}
step={0.01}
value={1.0}
title="Exposure / Brightness"
description="Adjusts the overall brightness level of the input image before processing."
/>
<TuiSlider
id="contrast"
label="CON"
min={0}
max={3}
step={0.01}
value={1.0}
title="Contrast"
description="Increases or decreases the difference between light and dark areas."
/>
<TuiSlider
id="saturation"
label="SAT"
min={0}
max={3}
step={0.01}
value={1.2}
title="Saturation"
description="Controls color intensity. Higher values make colors more vibrant in Color Mode."
/>
<TuiSlider
id="gamma"
label="GAM"
min={0}
max={3}
step={0.01}
value={1.0}
title="Gamma Correction"
description="Non-linear brightness adjustment. useful for correcting washed-out or too dark images."
/>
<TuiSlider
id="overlayStrength"
label="OVL"
min={0}
max={1}
step={0.01}
value={0.3}
title="Overlay Blend Strength"
description="Blends the original image over the ASCII output. 0 is pure ASCII, 1 is original image."
/>
<TuiSlider
id="resolution"
label="RES"
min={0.1}
max={2}
step={0.01}
value={1.0}
title="Resolution Scale"
description="Adjusts the density of characters. Higher values give more detail but may reduce performance."
/>
<TuiSlider
id="dither"
label="DTH"
min={0}
max={1}
step={0.01}
value={0}
title="Dither Strength"
description="Applies ordered dithering to simulate shading. Useful for low-contrast areas."
/>
</div>
</div>
<!-- Divider -->
<div class="control-panel-divider"></div>
<!-- Toggles & Segments Section -->
<div class="control-panel-section toggles-section">
<div class="section-header">EFFECTS</div>
<div class="toggles-row">
<TuiToggle
id="toggle-color"
label="CLR"
title="Color Output (HTML)"
description="Toggles between monochrome text and colored HTML spans."
/>
<TuiToggle
id="toggle-denoise"
label="DNZ"
title="Denoise Pre-processing"
description="Applies a bilateral filter to reduce image noise while preserving edges."
/>
</div>
<div class="section-header" style="margin-top: 8px;">
OUTPUT
</div>
<div class="segments-row">
<TuiSegment
id="segment-invert"
label="INV"
options={["AUTO", "ON", "OFF"]}
value="AUTO"
title="Invert Colors"
description="Inverts brightness mapping. AUTO detects dark/light mode."
/>
<TuiSegment
id="segment-edge"
label="EDG"
options={["OFF", "SPL", "SOB", "CNY"]}
value="OFF"
title="Edge Detection Mode"
description="Algorithm used to detect edges. SPL: Simple, SOB: Sobel, CNY: Canny."
/>
<TuiSegment
id="segment-charset"
label="SET"
options={[
"STD",
"EXT",
"BLK",
"MIN",
"DOT",
"SHP",
]}
value="STD"
title="Character Set"
description="The set of characters used for mapping brightness levels."
/>
</div>
</div>
<!-- Divider -->
<div class="control-panel-divider"></div>
<!-- Right Column: Actions & Export -->
<div class="control-col-right">
<div class="control-panel-section actions-section">
<div class="section-header">ACTIONS</div>
<div class="actions-row">
<TuiButton
id="btn-reset"
label="RESET"
shortcut="R"
title="Reset to Auto-detected Settings"
description="Resets all sliders and toggles to their default values."
/>
<TuiButton
id="btn-next"
label="NEXT"
shortcut="N"
variant="primary"
title="Load Next Image"
description="Discards current image and loads a new one from the queue."
/>
<div
class="queue-display"
data-tooltip-title="Buffered Images"
data-tooltip-desc="Number of images pre-loaded in background queue."
>
<span class="queue-label">Q:</span>
<span id="val-queue" class="queue-value"
>0</span
>
</div>
</div>
</div>
<!-- Horizontal Divider for inside column -->
<div class="control-panel-divider-h"></div>
<div class="control-panel-section export-section">
<div class="section-header">IMPORT / EXPORT</div>
<div class="actions-row">
<!-- Hidden File Input -->
<input
type="file"
id="file-upload"
accept="image/*"
style="display: none;"
/>
<TuiButton
id="btn-import"
label="IMP"
title="Import Image"
description="Upload your own image from your device."
/>
<TuiButton
id="btn-save-png"
label="PNG"
title="Save as Image"
description="Download high-res PNG capture of the current view."
/>
<TuiButton
id="btn-copy-text"
label="TXT"
title="Save as Text"
description="Download raw ASCII text file."
/>
<TuiButton
id="btn-copy-html"
label="HTML"
title="Save as HTML"
description="Download colored HTML file."
/>
</div>
</div>
</div>
</div>
<!-- Keyboard shortcuts hint -->
<div class="shortcuts-hint">
<span><kbd>N</kbd> Next</span>
<span><kbd>R</kbd> Reset</span>
<span><kbd>I</kbd> Invert</span>
<span><kbd>C</kbd> Color</span>
<span><kbd>D</kbd> Dither</span>
<span><kbd>E</kbd> Edges</span>
<span><kbd>S</kbd> Charset</span>
</div>
</footer>
<ControlPanel />
</main>
</div>
@@ -462,127 +132,8 @@ import Tooltip from "../components/Tooltip.astro";
background: var(--bg-color);
}
/* Sidebar (Left 25%) */
.sidebar {
width: 25%;
min-width: 320px;
max-width: 480px;
border-right: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
flex-direction: column;
justify-content: center;
background: #000;
position: relative;
z-index: 10;
box-shadow: 4px 0 20px rgba(0, 0, 0, 0.5);
}
.sidebar-content {
padding: 4rem 3rem;
display: flex;
flex-direction: column;
gap: 2.5rem;
}
.brand-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.brand-title {
font-size: 2.5rem;
font-weight: 900;
margin: 0;
line-height: 1;
letter-spacing: -1px;
color: #fff;
}
.brand-subtitle {
font-size: 0.75rem;
color: rgba(255, 255, 255, 0.5);
font-family: var(--font-mono);
letter-spacing: 1px;
display: flex;
gap: 8px;
align-items: center;
flex-wrap: wrap;
}
.muted {
color: rgba(255, 255, 255, 0.2);
}
.brand-bio {
font-size: 1rem;
line-height: 1.6;
color: rgba(255, 255, 255, 0.8);
max-width: 400px;
}
.sidebar-actions {
display: flex;
flex-direction: column;
gap: 0.75rem;
align-items: flex-start;
}
.sidebar-link {
display: flex;
align-items: center;
gap: 12px;
color: rgba(255, 255, 255, 0.6);
text-decoration: none;
font-family: var(--font-mono);
font-size: 0.9rem;
padding: 4px 0;
transition: all 0.2s;
}
.sidebar-link:hover {
color: var(--text-color);
transform: translateX(4px);
}
.sidebar-link .icon {
width: 20px;
display: inline-block;
text-align: center;
}
.control-panel-divider-h {
height: 1px;
width: 100%;
margin: 32px 0;
background: linear-gradient(
to right,
transparent 0%,
rgba(255, 255, 255, 0.1) 20%,
rgba(255, 255, 255, 0.3) 50%,
rgba(255, 255, 255, 0.1) 80%,
transparent 100%
);
}
.sidebar-social {
display: flex;
gap: 1.5rem;
margin-top: 1rem;
padding-top: 1.5rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.sidebar-social a {
color: rgba(255, 255, 255, 0.4);
transition: color 0.2s;
}
.sidebar-social a:hover {
color: #fff;
}
/* Workspace (Right 75%) */
.ascii-workspace {
flex-grow: 1;
height: 100vh;
@@ -636,92 +187,6 @@ import Tooltip from "../components/Tooltip.astro";
border-radius: 4px;
}
/* Controls Footer */
.controls-footer {
flex-shrink: 0;
background: rgba(0, 0, 0, 0.85);
border-top: 1px solid rgba(255, 255, 255, 0.1);
padding: 1rem 2rem;
backdrop-filter: blur(10px);
display: flex;
flex-direction: column;
gap: 1rem;
z-index: 20;
}
#tui-controls {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0;
background: none;
box-shadow: none;
border: none;
}
.control-panel-section {
padding: 0 20px;
}
.control-panel-divider {
background: rgba(255, 255, 255, 0.1);
}
.section-header {
font-size: 10px;
font-weight: 800;
opacity: 0.6;
letter-spacing: 2px;
margin-bottom: 8px;
text-transform: uppercase;
color: var(--text-color);
}
.sliders-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 8px 16px;
}
.toggles-row,
.segments-row,
.actions-row {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.queue-display {
display: flex;
align-items: center;
gap: 4px;
font-size: 11px;
opacity: 0.6;
padding: 0 8px;
border-left: 1px solid rgba(255, 255, 255, 0.2);
}
.shortcuts-hint {
display: flex;
justify-content: center;
gap: 16px;
font-size: 10px;
opacity: 0.4;
}
.shortcuts-hint span {
display: flex;
align-items: center;
gap: 6px;
}
.shortcuts-hint kbd {
border: 1px solid rgba(255, 255, 255, 0.2);
padding: 2px 4px;
border-radius: 3px;
font-size: 9px;
}
/* Responsive */
@media (max-width: 1024px) {
.split-layout {
@@ -729,27 +194,6 @@ import Tooltip from "../components/Tooltip.astro";
overflow-y: auto;
}
.sidebar {
width: 100%;
max-width: none;
min-width: 0;
border-right: none;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
padding: 2rem 0;
}
.sidebar-content {
padding: 1rem 2rem;
align-items: center;
text-align: center;
}
.brand-subtitle,
.sidebar-actions {
justify-content: center;
align-items: center;
}
.ascii-workspace {
height: 80vh; /* Give space for scroll */
}