100 lines
2.5 KiB
Vue
100 lines
2.5 KiB
Vue
|
|
<script setup>
|
||
|
|
import { reactive, ref } 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,
|
||
|
|
toolConfig: { tool: 'chafa', flags: {} },
|
||
|
|
result: '',
|
||
|
|
errors: [],
|
||
|
|
isConverting: false,
|
||
|
|
})
|
||
|
|
|
||
|
|
const bridge = ref(null)
|
||
|
|
|
||
|
|
function addError(message, source = 'error') {
|
||
|
|
state.errors.push({
|
||
|
|
timestamp: new Date().toLocaleTimeString(),
|
||
|
|
source,
|
||
|
|
message,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
async function runConvert() {
|
||
|
|
if (!state.image) {
|
||
|
|
addError('No image selected.', 'warn')
|
||
|
|
return
|
||
|
|
}
|
||
|
|
state.isConverting = true
|
||
|
|
state.result = ''
|
||
|
|
try {
|
||
|
|
await bridge.value.convert()
|
||
|
|
} finally {
|
||
|
|
state.isConverting = false
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
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" style="border-color: var(--ascii-border)">
|
||
|
|
<h1
|
||
|
|
class="text-lg font-semibold tracking-widest font-mono"
|
||
|
|
style="color: var(--ascii-green)"
|
||
|
|
>
|
||
|
|
ASCIInator
|
||
|
|
</h1>
|
||
|
|
</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="state.image = $event" />
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section>
|
||
|
|
<h2 class="text-xs font-medium uppercase tracking-wider mb-3"
|
||
|
|
style="color: rgba(224,224,224,.35)">Tool & Options</h2>
|
||
|
|
<ToolSelector @tool-config="state.toolConfig = $event" />
|
||
|
|
</section>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="flex justify-center">
|
||
|
|
<button
|
||
|
|
class="ascii-btn-primary"
|
||
|
|
:disabled="state.isConverting || !state.image"
|
||
|
|
@click="runConvert"
|
||
|
|
>
|
||
|
|
{{ state.isConverting ? 'Converting...' : 'Convert' }}
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<ShellBridge
|
||
|
|
ref="bridge"
|
||
|
|
:image="state.image"
|
||
|
|
:tool-config="state.toolConfig"
|
||
|
|
@conversion-result="onResult"
|
||
|
|
@conversion-error="onError"
|
||
|
|
/>
|
||
|
|
|
||
|
|
<OutputDisplay :result="state.result" />
|
||
|
|
</main>
|
||
|
|
|
||
|
|
<ErrorLog :errors="state.errors" @clear="state.errors = []" />
|
||
|
|
</div>
|
||
|
|
</template>
|