Files
personal-website/src/pages/index.astro

202 lines
5.7 KiB
Plaintext

---
import Layout from "../layouts/Layout.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">
<Sidebar />
<!-- RIGHT MAIN: ASCII & Controls -->
<main class="ascii-workspace hero-wrapper">
<!-- Canvas Layer -->
<div class="canvas-layer">
<div id="loading">Loading...</div>
<pre id="ascii-result">Preparing art...</pre>
<canvas id="ascii-canvas"></canvas>
</div>
<ControlPanel />
</main>
</div>
<script>
import { AsciiController } from "../scripts/ascii-controller";
import { ImageQueue } from "../scripts/image-queue";
import { UIBindings } from "../scripts/ui-bindings";
// ============= Global Cleanup Protocol =============
// Fix for accumulating event listeners and render loops during HMR/Navigation
if (window.__ASCII_APP__) {
console.log("♻️ Disposing previous application instance...");
try {
window.__ASCII_APP__.dispose();
} catch (e) {
console.error("Failed to dispose previous instance:", e);
}
}
// ============= DOM Elements =============
const canvas = document.getElementById(
"ascii-canvas",
) as HTMLCanvasElement;
const asciiResult = document.getElementById(
"ascii-result",
) as HTMLPreElement;
const loadingIndicator = document.getElementById(
"loading",
) as HTMLDivElement;
if (!canvas || !asciiResult || !loadingIndicator) {
throw new Error("Critical UI elements missing");
}
// ============= Initialize =============
const controller = new AsciiController(
canvas,
asciiResult,
loadingIndicator,
);
const queue = new ImageQueue(2);
const ui = new UIBindings(controller, queue, loadNewImage);
// Store instances globally for cleanup
window.__ASCII_APP__ = {
controller,
queue,
ui,
dispose: () => {
controller.dispose();
ui.dispose();
queue.dispose();
window.__ASCII_APP__ = undefined;
},
};
// Link settings updates to UI sync
controller.onSettingsChanged(() => ui.updateUI());
let retryCount = 0;
const MAX_RETRIES = 3;
// ============= Image Loading =============
async function loadNewImage(): Promise<void> {
try {
let item;
if (queue.getLength() === 0) {
controller.showLoading("FETCHING...");
item = await queue.fetchDirect();
} else {
item = queue.pop()!;
queue.ensureFilled(); // Background refill
}
controller.setCurrentImage(item.url, item.suggestions);
retryCount = 0;
ui.updateUI();
await controller.generate();
controller.hideLoading();
} catch (e) {
console.error(e);
if (retryCount < MAX_RETRIES) {
retryCount++;
asciiResult.textContent = `SIGNAL LOST. RETRYING (${retryCount}/${MAX_RETRIES})...`;
setTimeout(loadNewImage, 2000);
} else {
asciiResult.textContent = "SIGNAL LOST. PLEASE REFRESH.";
controller.hideLoading();
}
}
}
// ============= Initialize UI and Load First Image =============
ui.init();
loadNewImage().then(() => {
queue.ensureFilled();
});
</script>
<Tooltip />
</Layout>
<style>
/* Split Layout */
.split-layout {
display: flex;
width: 100vw;
height: 100vh;
overflow: hidden;
background: var(--bg-color);
}
/* Workspace (Right 75%) */
.ascii-workspace {
flex-grow: 1;
height: 100vh;
position: relative;
display: flex;
flex-direction: column;
background: #050505;
overflow: hidden;
}
.canvas-layer {
flex-grow: 1;
position: relative;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
background: radial-gradient(circle at center, #111 0%, #000 100%);
}
#ascii-result {
font-size: 8px;
line-height: 1;
white-space: pre;
color: var(--text-color);
transform-origin: center;
}
#ascii-canvas {
width: 100%;
height: 100%;
object-fit: contain;
display: none;
image-rendering: pixelated;
opacity: 0;
transition: opacity 0.5s ease;
}
#loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-family: var(--font-mono);
color: #fff;
font-size: 1.5rem;
display: none;
z-index: 10;
background: rgba(0, 0, 0, 0.8);
padding: 1rem 2rem;
border-radius: 4px;
}
/* Responsive */
@media (max-width: 1024px) {
.split-layout {
flex-direction: column;
overflow-y: auto;
}
.ascii-workspace {
height: 80vh; /* Give space for scroll */
}
}
</style>