feat: Implement image import functionality, restructure control panel layout, and apply glassmorphism styling to controls.
This commit is contained in:
@@ -55,8 +55,11 @@ const { pathname } = Astro.url;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 24px;
|
||||
background: #000;
|
||||
border-bottom: 1px solid var(--text-color);
|
||||
background: rgba(10, 10, 10, 0.8);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
backdrop-filter: blur(12px);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.15);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
@@ -81,30 +84,32 @@ const { pathname } = Astro.url;
|
||||
align-items: center;
|
||||
color: var(--text-color);
|
||||
text-decoration: none;
|
||||
border-right: 1px solid rgba(255, 103, 0, 0.2);
|
||||
transition: all 0.1s;
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.1);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
background: var(--text-color);
|
||||
color: #000;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav-link:hover .nav-index {
|
||||
color: #000;
|
||||
color: #fff;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.status-item.active {
|
||||
background: var(--text-color);
|
||||
color: #000;
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
box-shadow: inset 0 -2px 0 var(--text-color);
|
||||
}
|
||||
|
||||
.status-item.brand {
|
||||
background: rgba(255, 103, 0, 0.1);
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
font-weight: 900;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.nav-index {
|
||||
@@ -122,7 +127,7 @@ const { pathname } = Astro.url;
|
||||
|
||||
.status-right .status-item {
|
||||
border-right: none;
|
||||
border-left: 1px solid rgba(255, 103, 0, 0.2);
|
||||
border-left: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.prefix {
|
||||
@@ -134,6 +139,7 @@ const { pathname } = Astro.url;
|
||||
#system-status {
|
||||
color: #0f0;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 0 5px rgba(0, 255, 0, 0.5);
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -174,7 +174,8 @@ import Tooltip from "../components/Tooltip.astro";
|
||||
<!-- Divider -->
|
||||
<div class="control-panel-divider"></div>
|
||||
|
||||
<!-- Actions Section -->
|
||||
<!-- 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">
|
||||
@@ -207,13 +208,27 @@ import Tooltip from "../components/Tooltip.astro";
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Divider -->
|
||||
<div class="control-panel-divider"></div>
|
||||
<!-- Horizontal Divider for inside column -->
|
||||
<div class="control-panel-divider-h"></div>
|
||||
|
||||
<!-- Export Section -->
|
||||
<div class="control-panel-section export-section">
|
||||
<div class="section-header">EXPORT</div>
|
||||
<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"
|
||||
@@ -235,6 +250,7 @@ import Tooltip from "../components/Tooltip.astro";
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Keyboard shortcuts hint -->
|
||||
<div class="shortcuts-hint">
|
||||
@@ -405,6 +421,12 @@ import Tooltip from "../components/Tooltip.astro";
|
||||
font-size: 1.5rem;
|
||||
display: none;
|
||||
z-index: 10;
|
||||
|
||||
text-shadow: 0 0 10px rgba(0, 0, 0, 0.8);
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
padding: 10px 20px;
|
||||
border-radius: 4px;
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
/* FOREGROUND LAYER */
|
||||
@@ -474,6 +496,7 @@ import Tooltip from "../components/Tooltip.astro";
|
||||
max-width: 400px;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
padding: 5px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* Footer / Controls */
|
||||
@@ -484,25 +507,40 @@ import Tooltip from "../components/Tooltip.astro";
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
position: relative;
|
||||
z-index: 50; /* Ensure on top */
|
||||
}
|
||||
|
||||
#tui-controls {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
align-items: stretch; /* Stretch to same height */
|
||||
gap: 0;
|
||||
background: rgba(0, 0, 0, 0.95);
|
||||
border: 1px solid var(--text-color);
|
||||
box-shadow: 0 0 30px rgba(0, 0, 0, 0.8);
|
||||
|
||||
/* Glassmorphism update */
|
||||
background: rgba(10, 10, 10, 0.8);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
|
||||
|
||||
max-width: 100%;
|
||||
border-radius: 8px; /* Slightly rounded for premium feel */
|
||||
overflow: hidden; /* For border radius */
|
||||
}
|
||||
|
||||
.control-panel-section {
|
||||
padding: 12px 16px;
|
||||
padding: 16px 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.control-col-right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.control-panel-divider {
|
||||
@@ -510,32 +548,47 @@ import Tooltip from "../components/Tooltip.astro";
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
transparent 0%,
|
||||
rgba(255, 103, 0, 0.3) 20%,
|
||||
rgba(255, 103, 0, 0.3) 80%,
|
||||
rgba(255, 255, 255, 0.1) 20%,
|
||||
rgba(255, 255, 255, 0.3) 50%,
|
||||
rgba(255, 255, 255, 0.1) 80%,
|
||||
transparent 100%
|
||||
);
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.control-panel-divider-h {
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
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%
|
||||
);
|
||||
}
|
||||
|
||||
.section-header {
|
||||
font-size: 9px;
|
||||
font-weight: bold;
|
||||
opacity: 0.5;
|
||||
font-size: 10px;
|
||||
font-weight: 800;
|
||||
opacity: 0.6;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 4px;
|
||||
margin-bottom: 2px;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.sliders-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 6px 16px;
|
||||
gap: 8px 20px;
|
||||
}
|
||||
|
||||
.toggles-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.segments-row {
|
||||
@@ -547,7 +600,8 @@ import Tooltip from "../components/Tooltip.astro";
|
||||
.actions-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.queue-display {
|
||||
@@ -557,7 +611,7 @@ import Tooltip from "../components/Tooltip.astro";
|
||||
font-size: 11px;
|
||||
opacity: 0.6;
|
||||
padding: 0 8px;
|
||||
border-left: 1px solid rgba(255, 103, 0, 0.2);
|
||||
border-left: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.queue-label {
|
||||
@@ -575,34 +629,34 @@ import Tooltip from "../components/Tooltip.astro";
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
font-size: 10px;
|
||||
opacity: 0.4;
|
||||
gap: 16px;
|
||||
font-size: 11px;
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.shortcuts-hint:hover {
|
||||
opacity: 0.7;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.shortcuts-hint span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.shortcuts-hint kbd {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 16px;
|
||||
height: 16px;
|
||||
padding: 0 4px;
|
||||
font-size: 9px;
|
||||
min-width: 18px;
|
||||
height: 18px;
|
||||
padding: 0 5px;
|
||||
font-size: 10px;
|
||||
font-family: inherit;
|
||||
border: 1px solid rgba(255, 103, 0, 0.4);
|
||||
border-radius: 2px;
|
||||
background: rgba(255, 103, 0, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 4px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* Legacy styles kept for compatibility */
|
||||
@@ -627,7 +681,7 @@ import Tooltip from "../components/Tooltip.astro";
|
||||
.tui-btn:hover {
|
||||
opacity: 1;
|
||||
border-color: var(--text-color);
|
||||
background: rgba(255, 103, 0, 0.1);
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.tui-val {
|
||||
@@ -648,20 +702,45 @@ import Tooltip from "../components/Tooltip.astro";
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.control-col-right {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.control-col-right > .control-panel-section {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* New vertical alignment for right col on mobile */
|
||||
.control-panel-divider-h {
|
||||
width: 1px;
|
||||
height: auto;
|
||||
align-self: stretch;
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
transparent 0%,
|
||||
rgba(255, 255, 255, 0.2) 20%,
|
||||
rgba(255, 255, 255, 0.2) 80%,
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
|
||||
.control-panel-divider {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
transparent 0%,
|
||||
rgba(255, 103, 0, 0.3) 20%,
|
||||
rgba(255, 103, 0, 0.3) 80%,
|
||||
rgba(255, 255, 255, 0.1) 20%,
|
||||
rgba(255, 255, 255, 0.3) 50%,
|
||||
rgba(255, 255, 255, 0.1) 80%,
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
|
||||
.control-panel-section {
|
||||
padding: 10px 14px;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -670,6 +749,21 @@ import Tooltip from "../components/Tooltip.astro";
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
.control-col-right {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.control-panel-divider-h {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
transparent,
|
||||
rgba(255, 255, 255, 0.2),
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
.controls-footer {
|
||||
padding: 1rem;
|
||||
}
|
||||
@@ -691,11 +785,11 @@ import Tooltip from "../components/Tooltip.astro";
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.control-panel-section {
|
||||
padding: 8px 10px;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
font-size: 8px;
|
||||
font-size: 9px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -67,6 +67,7 @@ export class UIBindings {
|
||||
this.setupKeyboard();
|
||||
this.setupZoom();
|
||||
this.setupResize();
|
||||
this.setupImport();
|
||||
|
||||
// Periodic queue update
|
||||
this.queueInterval = window.setInterval(() => this.updateQueueDisplay(), 1000);
|
||||
@@ -142,13 +143,20 @@ export class UIBindings {
|
||||
}
|
||||
|
||||
// Cleanup Export Buttons
|
||||
['btn-save-png', 'btn-copy-text', 'btn-copy-html'].forEach(id => {
|
||||
['btn-save-png', 'btn-copy-text', 'btn-copy-html', 'btn-import'].forEach(id => {
|
||||
const el = document.getElementById(id);
|
||||
const handler = this.buttonHandlers.get(id);
|
||||
if (el && handler) {
|
||||
el.removeEventListener('click', handler);
|
||||
}
|
||||
});
|
||||
|
||||
// Cleanup File Input
|
||||
const fileInput = document.getElementById('file-upload');
|
||||
const fileHandler = this.buttonHandlers.get('file-upload');
|
||||
if (fileInput && fileHandler) {
|
||||
fileInput.removeEventListener('change', fileHandler);
|
||||
}
|
||||
}
|
||||
|
||||
// ============= Sliders =============
|
||||
@@ -412,6 +420,52 @@ export class UIBindings {
|
||||
window.addEventListener('resize', this.resizeHandler);
|
||||
}
|
||||
|
||||
// ============= Import =============
|
||||
|
||||
private setupImport(): void {
|
||||
const btnImport = document.getElementById('btn-import');
|
||||
const fileInput = document.getElementById('file-upload') as HTMLInputElement;
|
||||
|
||||
if (btnImport && fileInput) {
|
||||
// Button triggers file input
|
||||
const btnHandler = (e: Event) => {
|
||||
e.stopPropagation();
|
||||
fileInput.click();
|
||||
};
|
||||
this.buttonHandlers.set('btn-import', btnHandler);
|
||||
btnImport.addEventListener('click', btnHandler);
|
||||
|
||||
// File input change
|
||||
const fileHandler = async (e: Event) => {
|
||||
const files = (e.target as HTMLInputElement).files;
|
||||
if (files && files.length > 0) {
|
||||
const file = files[0];
|
||||
const url = URL.createObjectURL(file);
|
||||
|
||||
// Reset value so same file can be selected again
|
||||
fileInput.value = '';
|
||||
|
||||
try {
|
||||
this.controller.showLoading("LOADING IMPORT...");
|
||||
// Use empty suggestions for user imports unless we want to auto-detect?
|
||||
// For now keep existing settings or use defaults.
|
||||
// Let's pass empty object to respect current user settings or controller defaults.
|
||||
this.controller.setCurrentImage(url, {});
|
||||
this.updateUI();
|
||||
await this.controller.generate();
|
||||
this.controller.hideLoading();
|
||||
} catch (err) {
|
||||
console.error("Import failed:", err);
|
||||
this.controller.hideLoading();
|
||||
alert("Failed to load image. Please try another file.");
|
||||
}
|
||||
}
|
||||
};
|
||||
this.buttonHandlers.set('file-upload', fileHandler);
|
||||
fileInput.addEventListener('change', fileHandler);
|
||||
}
|
||||
}
|
||||
|
||||
// ============= UI Sync =============
|
||||
|
||||
updateUI(): void {
|
||||
|
||||
Reference in New Issue
Block a user