ASCIInator/src/App.vue

106 lines
2.9 KiB
Vue
Raw Normal View History

<script setup>
import { reactive, ref, watch } from 'vue'
import ImageInput from './components/ImageInput.vue'
import ToolSelector from './components/ToolSelector.vue'
import ShellBridge from './components/ShellBridge.vue'
import OutputDisplay from './components/OutputDisplay.vue'
import ErrorLog from './components/ErrorLog.vue'
const state = reactive({
image: null,
imageDims: null,
toolConfig: { tool: 'chafa', flags: {} },
result: '',
errors: [],
isConverting: false,
})
const bridge = ref(null)
let debounceTimer = null
function addError(message, source = 'error') {
state.errors.push({
timestamp: new Date().toLocaleTimeString(),
source,
message,
})
}
function scheduleConvert(delay = 400) {
if (!state.image) return
clearTimeout(debounceTimer)
debounceTimer = setTimeout(() => bridge.value?.convert(), delay)
}
function onImageReady(img) {
state.image = img
const el = new Image()
el.onload = () => { state.imageDims = { w: el.naturalWidth, h: el.naturalHeight } }
el.src = img.dataUrl
}
// New image — convert immediately
watch(() => state.image, (img) => {
if (img) scheduleConvert(0)
})
// Flag / tool changes — debounce for slider drag
watch(() => state.toolConfig, () => scheduleConvert(400), { deep: true })
function onResult(text) {
state.result = text
}
function onError(message) {
addError(message)
}
</script>
<template>
<div class="min-h-screen pb-20" style="background-color: var(--ascii-bg)">
<header class="px-6 py-4 border-b flex items-center gap-4" style="border-color: var(--ascii-border)">
<h1
class="text-lg font-semibold tracking-widest font-mono"
style="color: var(--ascii-green)"
>
ASCIInator
</h1>
<span
v-if="state.isConverting"
class="text-xs font-mono animate-pulse"
style="color: rgba(57,255,20,.5)"
>converting</span>
</header>
<main class="max-w-5xl mx-auto px-6 py-8 flex flex-col gap-8">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<section>
<h2 class="text-xs font-medium uppercase tracking-wider mb-3"
style="color: rgba(224,224,224,.35)">Image</h2>
<ImageInput @image-ready="onImageReady" />
</section>
<section>
<h2 class="text-xs font-medium uppercase tracking-wider mb-3"
style="color: rgba(224,224,224,.35)">Tool &amp; Options</h2>
<ToolSelector :image-dims="state.imageDims" @tool-config="state.toolConfig = $event" />
</section>
</div>
<ShellBridge
ref="bridge"
:image="state.image"
:tool-config="state.toolConfig"
@conversion-result="onResult"
@conversion-error="onError"
@conversion-start="state.isConverting = true"
@conversion-end="state.isConverting = false"
/>
<OutputDisplay :result="state.result" />
</main>
<ErrorLog :errors="state.errors" @clear="state.errors = []" />
</div>
</template>