feat: Introduce new Sidebar and ControlPanel components, and update styling for TUI Segment, Slider, Toggle, and Button components.
This commit is contained in:
411
src/components/ControlPanel.astro
Normal file
411
src/components/ControlPanel.astro
Normal file
@@ -0,0 +1,411 @@
|
|||||||
|
---
|
||||||
|
import TuiSlider from "./TuiSlider.astro";
|
||||||
|
import TuiSegment from "./TuiSegment.astro";
|
||||||
|
import TuiToggle from "./TuiToggle.astro";
|
||||||
|
import TuiButton from "./TuiButton.astro";
|
||||||
|
---
|
||||||
|
|
||||||
|
<footer id="tui-controls" class="control-panel">
|
||||||
|
<div class="control-panel-content">
|
||||||
|
<!-- Sliders Section -->
|
||||||
|
<div class="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>
|
||||||
|
|
||||||
|
<div class="panel-divider-v"></div>
|
||||||
|
|
||||||
|
<!-- Toggles & Segments Section -->
|
||||||
|
<div class="panel-section effects-section">
|
||||||
|
<div class="sub-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>
|
||||||
|
|
||||||
|
<div class="sub-section">
|
||||||
|
<div class="section-header">OUTPUT</div>
|
||||||
|
<div class="segments-col">
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel-divider-v"></div>
|
||||||
|
|
||||||
|
<!-- Right Column: Actions & Export -->
|
||||||
|
<div class="panel-section actions-section">
|
||||||
|
<div class="sub-section">
|
||||||
|
<div class="section-header">ACTIONS</div>
|
||||||
|
<div class="actions-grid">
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<div class="panel-divider-h"></div>
|
||||||
|
|
||||||
|
<div class="sub-section">
|
||||||
|
<div class="section-header">IMPORT / EXPORT</div>
|
||||||
|
<div class="actions-grid">
|
||||||
|
<!-- 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>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Main Container matching Sidebar visual style */
|
||||||
|
.control-panel {
|
||||||
|
flex-shrink: 0;
|
||||||
|
background: #000; /* Matching Sidebar */
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
padding: 2rem 3rem; /* Matching Sidebar padding style */
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.5rem;
|
||||||
|
z-index: 20;
|
||||||
|
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.5); /* Inverted shadow */
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-panel-content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 2rem;
|
||||||
|
flex-wrap: nowrap; /* Prevent wrapping on large screens */
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sections */
|
||||||
|
.panel-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sliders-section {
|
||||||
|
flex: 2;
|
||||||
|
min-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.effects-section {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 200px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.5rem;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions-section {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 200px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sub-sections */
|
||||||
|
.sub-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Headers */
|
||||||
|
.section-header {
|
||||||
|
font-size: 0.75rem; /* Matched to sidebar subtitle size approx */
|
||||||
|
font-weight: 700;
|
||||||
|
opacity: 0.5;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grids & Rows */
|
||||||
|
.sliders-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
||||||
|
gap: 1rem 1.5rem; /* More airy gap */
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggles-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.segments-col {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dividers */
|
||||||
|
.panel-divider-v {
|
||||||
|
width: 1px;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
align-self: stretch;
|
||||||
|
margin: 0 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-divider-h {
|
||||||
|
height: 1px;
|
||||||
|
width: 100%;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Queue Display */
|
||||||
|
.queue-display {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 6px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: rgba(255, 255, 255, 0.4);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 4px;
|
||||||
|
height: 32px; /* Match button height */
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
}
|
||||||
|
|
||||||
|
.queue-value {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Shortcuts Hint */
|
||||||
|
.shortcuts-hint {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 1.5rem;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
color: rgba(255, 255, 255, 0.3);
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
padding-top: 1rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shortcuts-hint span {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shortcuts-hint kbd {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 10px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
.control-panel {
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-panel-content {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-divider-v {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sliders-section,
|
||||||
|
.effects-section,
|
||||||
|
.actions-section {
|
||||||
|
flex: 1 1 100%; /* Stack on smaller screens */
|
||||||
|
}
|
||||||
|
|
||||||
|
.sliders-grid {
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
242
src/components/Sidebar.astro
Normal file
242
src/components/Sidebar.astro
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<aside class="sidebar">
|
||||||
|
<div class="sidebar-content">
|
||||||
|
<div class="brand-group">
|
||||||
|
<a href="/" class="brand-link">
|
||||||
|
<h1 class="brand-title">SYNTAXBULLET</h1>
|
||||||
|
</a>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.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;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
word-break: break-word; /* Prevent overflow */
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-link {
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive */
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.sidebar {
|
||||||
|
width: 100%;
|
||||||
|
max-width: none;
|
||||||
|
min-width: 0;
|
||||||
|
border-right: none;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
padding: 3rem 0; /* Increased padding */
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-content {
|
||||||
|
padding: 1rem 3rem; /* Increased horizontal padding */
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-subtitle,
|
||||||
|
.sidebar-actions {
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -33,67 +33,70 @@ const {
|
|||||||
.tui-button {
|
.tui-button {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 4px;
|
gap: 6px;
|
||||||
background: none;
|
background: rgba(255, 255, 255, 0.03);
|
||||||
border: 1px solid rgba(255, 103, 0, 0.4);
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
color: var(--text-color);
|
color: rgba(255, 255, 255, 0.7);
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
padding: 3px 10px;
|
padding: 4px 10px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
opacity: 0.8;
|
transition: all 0.2s;
|
||||||
transition: all 0.15s;
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-button:hover {
|
.tui-button:hover {
|
||||||
opacity: 1;
|
color: #fff;
|
||||||
border-color: var(--text-color);
|
border-color: rgba(255, 255, 255, 0.3);
|
||||||
background: rgba(255, 103, 0, 0.1);
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
transform: translateY(-1px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-button:active {
|
.tui-button:active {
|
||||||
background: rgba(255, 103, 0, 0.2);
|
background: rgba(255, 255, 255, 0.12);
|
||||||
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-button--primary {
|
.tui-button--primary {
|
||||||
border-color: var(--text-color);
|
background: rgba(255, 255, 255, 0.1);
|
||||||
background: rgba(255, 103, 0, 0.1);
|
border-color: rgba(255, 255, 255, 0.2);
|
||||||
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-button--primary:hover {
|
.tui-button--primary:hover {
|
||||||
background: var(--text-color);
|
background: rgba(255, 255, 255, 0.2);
|
||||||
color: #000;
|
border-color: rgba(255, 255, 255, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-button--subtle {
|
.tui-button--subtle {
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
|
background: transparent;
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-button--subtle:hover {
|
.tui-button--subtle:hover {
|
||||||
border-color: rgba(255, 103, 0, 0.3);
|
border-color: rgba(255, 255, 255, 0.1);
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-button-shortcut {
|
.tui-button-shortcut {
|
||||||
font-size: 9px;
|
font-size: 9px;
|
||||||
opacity: 0.6;
|
opacity: 0.5;
|
||||||
padding: 0 3px;
|
padding: 1px 4px;
|
||||||
border: 1px solid currentColor;
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
line-height: 1.2;
|
line-height: 1;
|
||||||
border-radius: 2px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-button:hover .tui-button-shortcut {
|
.tui-button:hover .tui-button-shortcut {
|
||||||
opacity: 1;
|
opacity: 0.8;
|
||||||
}
|
border-color: rgba(255, 255, 255, 0.4);
|
||||||
|
|
||||||
.tui-button--primary:hover .tui-button-shortcut {
|
|
||||||
border-color: #000;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-button-label {
|
.tui-button-label {
|
||||||
font-weight: bold;
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -47,33 +47,37 @@ const {
|
|||||||
.tui-segment {
|
.tui-segment {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 12px;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-segment-label {
|
.tui-segment-label {
|
||||||
min-width: 3ch;
|
min-width: 3ch;
|
||||||
font-weight: bold;
|
font-weight: 700;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
color: rgba(255, 255, 255, 0.4);
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-segment-options {
|
.tui-segment-options {
|
||||||
display: flex;
|
display: flex;
|
||||||
border: 1px solid rgba(255, 103, 0, 0.3);
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
background: rgba(255, 255, 255, 0.02);
|
||||||
|
border-radius: 2px;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-segment-option {
|
.tui-segment-option {
|
||||||
background: none;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
border-right: 1px solid rgba(255, 103, 0, 0.2);
|
border-right: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
color: var(--text-color);
|
color: rgba(255, 255, 255, 0.5);
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
padding: 2px 8px;
|
padding: 4px 10px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
opacity: 0.5;
|
transition: all 0.2s;
|
||||||
transition: all 0.15s;
|
|
||||||
min-width: 3ch;
|
min-width: 3ch;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
@@ -83,24 +87,24 @@ const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tui-segment-option:hover {
|
.tui-segment-option:hover {
|
||||||
opacity: 0.8;
|
color: #fff;
|
||||||
background: rgba(255, 103, 0, 0.1);
|
background: rgba(255, 255, 255, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-segment-option.active {
|
.tui-segment-option.active {
|
||||||
background: var(--text-color);
|
background: rgba(255, 255, 255, 0.15);
|
||||||
color: #000;
|
color: #fff;
|
||||||
opacity: 1;
|
font-weight: 600;
|
||||||
font-weight: bold;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hover the whole group */
|
/* Hover the whole group */
|
||||||
.tui-segment:hover .tui-segment-label {
|
.tui-segment:hover .tui-segment-label {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-segment:hover .tui-segment-options {
|
.tui-segment:hover .tui-segment-options {
|
||||||
border-color: var(--text-color);
|
border-color: rgba(255, 255, 255, 0.2);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -63,15 +63,19 @@ const segments = 12;
|
|||||||
.tui-slider {
|
.tui-slider {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 8px;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-slider-label {
|
.tui-slider-label {
|
||||||
min-width: 3ch;
|
min-width: 3ch;
|
||||||
font-weight: bold;
|
font-weight: 700;
|
||||||
opacity: 0.7;
|
opacity: 0.5;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
color: rgba(255, 255, 255, 0.4);
|
||||||
|
transition: opacity 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-slider-track-wrapper {
|
.tui-slider-track-wrapper {
|
||||||
@@ -89,22 +93,24 @@ const segments = 12;
|
|||||||
|
|
||||||
.tui-slider-track {
|
.tui-slider-track {
|
||||||
display: flex;
|
display: flex;
|
||||||
letter-spacing: -1px;
|
letter-spacing: 2px;
|
||||||
font-family: monospace;
|
font-family: var(--font-mono);
|
||||||
|
font-size: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-slider-segment {
|
.tui-slider-segment {
|
||||||
transition: color 0.1s;
|
transition: color 0.1s;
|
||||||
color: rgba(255, 103, 0, 0.25);
|
color: rgba(255, 255, 255, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-slider-segment.filled {
|
.tui-slider-segment.filled {
|
||||||
color: var(--text-color);
|
color: rgba(255, 255, 255, 0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-slider-segment.thumb {
|
.tui-slider-segment.thumb {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
text-shadow: 0 0 4px var(--text-color);
|
text-shadow: 0 0 8px rgba(255, 255, 255, 0.5);
|
||||||
|
scale: 1.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-slider-input {
|
.tui-slider-input {
|
||||||
@@ -120,23 +126,26 @@ const segments = 12;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tui-slider-value {
|
.tui-slider-value {
|
||||||
min-width: 3ch;
|
min-width: 4ch;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
font-weight: bold;
|
font-weight: 400;
|
||||||
opacity: 0.9;
|
opacity: 0.8;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hover effect */
|
/* Hover effect */
|
||||||
.tui-slider:hover .tui-slider-label {
|
.tui-slider:hover .tui-slider-label {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-slider:hover .tui-slider-segment {
|
.tui-slider:hover .tui-slider-segment {
|
||||||
color: rgba(255, 103, 0, 0.4);
|
color: rgba(255, 255, 255, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-slider:hover .tui-slider-segment.filled {
|
.tui-slider:hover .tui-slider-segment.filled {
|
||||||
color: var(--text-color);
|
color: rgba(255, 255, 255, 0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-slider:hover .tui-slider-segment.thumb {
|
.tui-slider:hover .tui-slider-segment.thumb {
|
||||||
|
|||||||
@@ -32,35 +32,36 @@ const {
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background: none;
|
background: rgba(255, 255, 255, 0.03);
|
||||||
border: 1px solid rgba(255, 103, 0, 0.3);
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
color: var(--text-color);
|
color: rgba(255, 255, 255, 0.5);
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
padding: 2px 8px;
|
padding: 4px 12px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
opacity: 0.5;
|
transition: all 0.2s;
|
||||||
transition: all 0.15s;
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
min-width: 3ch;
|
min-width: 3ch;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-toggle:hover {
|
.tui-toggle:hover {
|
||||||
opacity: 0.8;
|
color: #fff;
|
||||||
background: rgba(255, 103, 0, 0.1);
|
border-color: rgba(255, 255, 255, 0.3);
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-toggle.active {
|
.tui-toggle.active {
|
||||||
background: var(--text-color);
|
background: rgba(255, 255, 255, 0.15);
|
||||||
color: #000;
|
color: #fff;
|
||||||
opacity: 1;
|
border-color: rgba(255, 255, 255, 0.4);
|
||||||
font-weight: bold;
|
box-shadow: 0 0 10px rgba(255, 255, 255, 0.05);
|
||||||
border-color: var(--text-color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-toggle-label {
|
.tui-toggle-label {
|
||||||
font-weight: bold;
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
---
|
---
|
||||||
import Navbar from '../components/Navbar.astro';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string;
|
title: string;
|
||||||
showScroll?: boolean;
|
showScroll?: boolean;
|
||||||
@@ -15,29 +13,28 @@ const { title, showScroll = false } = Astro.props;
|
|||||||
<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" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
<link
|
<link
|
||||||
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@100..800&display=swap"
|
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@100..800&display=swap"
|
||||||
rel="stylesheet">
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
|
|
||||||
<style is:global>
|
<style is:global>
|
||||||
@import "../styles/global.css";
|
@import "../styles/global.css";
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style define:vars={{ overflow: showScroll ? 'auto' : 'hidden' }}>
|
<style define:vars={{ overflow: showScroll ? "auto" : "hidden" }}>
|
||||||
body {
|
body {
|
||||||
overflow: var(--overflow);
|
overflow: var(--overflow);
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
padding-top: 24px;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<Navbar />
|
|
||||||
<slot />
|
<slot />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,115 +1,13 @@
|
|||||||
---
|
---
|
||||||
import Layout from "../layouts/Layout.astro";
|
import Layout from "../layouts/Layout.astro";
|
||||||
import TuiSlider from "../components/TuiSlider.astro";
|
import Sidebar from "../components/Sidebar.astro";
|
||||||
import TuiSegment from "../components/TuiSegment.astro";
|
|
||||||
import TuiToggle from "../components/TuiToggle.astro";
|
|
||||||
import TuiButton from "../components/TuiButton.astro";
|
|
||||||
import Tooltip from "../components/Tooltip.astro";
|
import Tooltip from "../components/Tooltip.astro";
|
||||||
|
import ControlPanel from "../components/ControlPanel.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout title="Syntaxbullet - Digital Wizard">
|
<Layout title="Syntaxbullet - Digital Wizard">
|
||||||
<div class="split-layout">
|
<div class="split-layout">
|
||||||
<!-- LEFT SIDEBAR: Content -->
|
<Sidebar />
|
||||||
<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>
|
|
||||||
|
|
||||||
<!-- RIGHT MAIN: ASCII & Controls -->
|
<!-- RIGHT MAIN: ASCII & Controls -->
|
||||||
<main class="ascii-workspace hero-wrapper">
|
<main class="ascii-workspace hero-wrapper">
|
||||||
@@ -120,235 +18,7 @@ import Tooltip from "../components/Tooltip.astro";
|
|||||||
<canvas id="ascii-canvas"></canvas>
|
<canvas id="ascii-canvas"></canvas>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer class="controls-footer">
|
<ControlPanel />
|
||||||
<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>
|
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -462,127 +132,8 @@ import Tooltip from "../components/Tooltip.astro";
|
|||||||
background: var(--bg-color);
|
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%) */
|
/* Workspace (Right 75%) */
|
||||||
|
|
||||||
.ascii-workspace {
|
.ascii-workspace {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
@@ -636,92 +187,6 @@ import Tooltip from "../components/Tooltip.astro";
|
|||||||
border-radius: 4px;
|
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 */
|
/* Responsive */
|
||||||
@media (max-width: 1024px) {
|
@media (max-width: 1024px) {
|
||||||
.split-layout {
|
.split-layout {
|
||||||
@@ -729,27 +194,6 @@ import Tooltip from "../components/Tooltip.astro";
|
|||||||
overflow-y: auto;
|
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 {
|
.ascii-workspace {
|
||||||
height: 80vh; /* Give space for scroll */
|
height: 80vh; /* Give space for scroll */
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user