Commit 1a530b08 authored by Clark Lin's avatar Clark Lin
Browse files

Optimized TextToMermaid. Migrated partial vuetified elements to TextToMarkdown.

parent ca404669
<template> <template>
<div class="text-to-markdown"> <v-container fluid class="text-to-markdown">
<h1 class="page-title">Text to Markdown Converter</h1> <v-row>
<v-col cols="12">
<div class="converter-container"> <h1 class="text-h4 text-center font-weight-bold mx-3 my-3">Text to Markdown Converter</h1>
</v-col>
</v-row>
<v-divider :thickness="1" color="white" class="border-opacity-100"></v-divider>
<v-row class="mx-3 my-3">
<!-- Input Section --> <!-- Input Section -->
<div class="input-section"> <v-col cols="12" md="4" class="pa-3">
<h3>Input Text</h3> <v-card elevation="2" class="input-card">
<textarea <v-card-title class="card-title">Input Text</v-card-title>
<v-card-text>
<v-textarea
v-model="inputText" v-model="inputText"
@input="handleInput" @input="handleInput"
placeholder="Enter your text here to convert to Markdown preview..." placeholder="Enter your text here to convert to Markdown preview..."
class="text-input" variant="outlined"
rows="10" rows="10"
></textarea> class="text-input"
></v-textarea>
<div class="button-group"> <div class="button-group">
<button @click="convertNow" class="convert-btn" :disabled="isProcessing"> <v-btn
{{ isProcessing ? 'Converting...' : 'Convert Now' }} @click="convertNow"
</button> :loading="isProcessing"
<button @click="clearAll" class="clear-btn">Clear All</button> color="primary"
class="convert-btn"
>
Convert Now
</v-btn>
<v-btn
@click="clearAll"
variant="outlined"
color="secondary"
>
Clear All
</v-btn>
</div> </div>
<!-- Status Indicator --> <!-- Status Indicator -->
<div class="status" :class="currentState"> <v-alert :type="alertType"
<span v-if="currentState === 'typing'">Typing...</span> :icon="alertIcon"
<span v-else-if="currentState === 'processing'">Converting...</span> variant="tonal"
<span v-else-if="currentState === 'ready'">Ready</span> density="compact"
<span v-else-if="currentState === 'error'">Error occurred</span> class="my-2">
</div> {{ statusText }}
</div> </v-alert>
</v-card-text>
</v-card>
</v-col>
<!-- Output Section --> <!-- Output Section -->
<div class="output-section"> <v-col cols="12" md="8" class="pa-3">
<h3>Markdown Preview</h3> <h3>Markdown Preview</h3>
<!-- Preview Section --> <!-- Preview Section -->
...@@ -75,6 +95,21 @@ ...@@ -75,6 +95,21 @@
</div> </div>
</div> </div>
<!-- Generated Markdown Section -->
<div class="code-section">
<div class="diagram-header">
<h4>Generated Markdown:</h4>
<div class="diagram-controls">
<button @click="copyToClipboard" class="copy-btn" :disabled="!markdownCode">
{{ copyButtonText }}
</button>
</div>
</div>
<div class="code-container">
<pre><code>{{ markdownCode || 'Your generated markdown will appear here...' }}</code></pre>
</div>
</div>
<!-- Fullscreen Modal --> <!-- Fullscreen Modal -->
<div v-if="isFullscreen" class="fullscreen-modal" @click="closeFullscreen"> <div v-if="isFullscreen" class="fullscreen-modal" @click="closeFullscreen">
<div class="modal-content" @click.stop> <div class="modal-content" @click.stop>
...@@ -137,9 +172,9 @@ ...@@ -137,9 +172,9 @@
</div> </div>
<!-- Generated Markdown Section --> <!-- Generated Markdown Section -->
</div> </v-col>
</div> </v-row>
</div> </v-container>
</template> </template>
<script setup> <script setup>
...@@ -163,6 +198,9 @@ const isDragging = ref(false) ...@@ -163,6 +198,9 @@ const isDragging = ref(false)
const dragStart = ref({ x: 0, y: 0 }) const dragStart = ref({ x: 0, y: 0 })
const dragOffset = ref({ x: 0, y: 0 }) const dragOffset = ref({ x: 0, y: 0 })
// Copy button state
const copyButtonText = ref('Copy to Clipboard')
let debounceTimeout = null let debounceTimeout = null
// Debounced conversion logic // Debounced conversion logic
...@@ -246,6 +284,36 @@ const clearAll = () => { ...@@ -246,6 +284,36 @@ const clearAll = () => {
currentState.value = 'ready' currentState.value = 'ready'
} }
const alertType = computed(() => {
switch (currentState.value) {
case 'error': return 'error'
case 'processing': return 'info'
case 'ready': return 'success'
case 'typing': return 'warning'
default: return 'info'
}
})
const alertIcon = computed(() => {
switch (currentState.value) {
case 'error': return 'mdi-alert-circle'
case 'processing': return 'mdi-progress-clock'
case 'ready': return 'mdi-check-circle'
case 'typing': return 'mdi-pencil'
default: return 'mdi-information'
}
})
const statusText = computed(() => {
switch (currentState.value) {
case 'typing': return 'Typing...'
case 'processing': return 'Converting...'
case 'ready': return 'Ready'
case 'error': return 'Error occurred'
default: return 'Ready'
}
})
// Rendered Markdown (computed for v-html) // Rendered Markdown (computed for v-html)
const renderedMarkdown = computed(() => { const renderedMarkdown = computed(() => {
return markdownCode.value ? marked.parse(markdownCode.value) : '' return markdownCode.value ? marked.parse(markdownCode.value) : ''
...@@ -349,6 +417,25 @@ const downloadPreview = async () => { ...@@ -349,6 +417,25 @@ const downloadPreview = async () => {
} }
} }
// Copy to clipboard function
const copyToClipboard = async () => {
if (!markdownCode.value) return
try {
await navigator.clipboard.writeText(markdownCode.value)
copyButtonText.value = 'Copied!'
setTimeout(() => {
copyButtonText.value = 'Copy to Clipboard'
}, 2000)
} catch (err) {
console.error('Failed to copy: ', err)
copyButtonText.value = 'Copy Failed'
setTimeout(() => {
copyButtonText.value = 'Copy to Clipboard'
}, 2000)
}
}
// Mount/unmount // Mount/unmount
onMounted(() => { onMounted(() => {
document.addEventListener('keydown', handleKeydown) document.addEventListener('keydown', handleKeydown)
...@@ -360,13 +447,10 @@ onUnmounted(() => { ...@@ -360,13 +447,10 @@ onUnmounted(() => {
<style scoped> <style scoped>
.text-to-markdown { .text-to-markdown {
/* max-width: 2400px; */
display: grid;
grid-template-rows: 1fr;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
margin: 10px;
padding: 0; padding: 0;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
height: 100%;
} }
.converter-container { .converter-container {
display: grid; display: grid;
...@@ -430,11 +514,38 @@ onUnmounted(() => { ...@@ -430,11 +514,38 @@ onUnmounted(() => {
.status { .status {
margin-top: 10px; margin-top: 10px;
font-size: 14px; font-size: 14px;
padding: 8px 12px;
border-radius: 4px;
font-weight: 500;
display: inline-block;
min-width: 100px;
text-align: center;
}
.status.typing {
background-color: #e2f0ff;
color: #007bff;
border: 1px solid #b8daff;
} }
.status.typing { color: #888; }
.status.processing { color: #007bff; } .status.processing {
.status.ready { color: #28a745; } background-color: #fff3cd;
.status.error { color: #dc3545; } color: #856404;
border: 1px solid #ffeaa7;
}
.status.ready {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.status.error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
/* Diagram Section (now first) */ /* Diagram Section (now first) */
.diagram-section { .diagram-section {
background: #f8f9fa; background: #f8f9fa;
...@@ -1143,18 +1254,6 @@ onUnmounted(() => { ...@@ -1143,18 +1254,6 @@ onUnmounted(() => {
gap: 80px; gap: 80px;
} }
} */ } */
.page-title {
font-size: 2.4rem; /* Larger, more prominent */
font-weight: 700; /* Bold */
letter-spacing: 1px;
text-align: center;
line-height: 1.2;
/* Optional: add a subtle shadow for depth */
text-shadow: 0 2px 8px rgba(76, 110, 245, 0.08);
/* Optional: add a divider line below */
border-bottom: 1px solid black;
padding-bottom: 12px;
}
/* Markdown content styles */ /* Markdown content styles */
.markdown-preview, .markdown-preview,
......
...@@ -10,47 +10,41 @@ ...@@ -10,47 +10,41 @@
<v-divider :thickness="1" color="white" class="border-opacity-100"></v-divider> <v-divider :thickness="1" color="white" class="border-opacity-100"></v-divider>
<!-- <div class="converter-container"> --> <!-- <div class="converter-container"> -->
<v-row class="mx-3 my-3"> <v-row class="mx-3 my-3">
<v-col cols="12" md="4" class="pa-3">
<!-- Input Section --> <!-- Input Section -->
<div class="input-section"> <v-col cols="12" md="4" class="pa-3">
<!-- <div class="input-section"> -->
<v-card elevation="2" class="input-card">
<!-- <h3>Input Text</h3> --> <!-- <h3>Input Text</h3> -->
<v-card-title class="text-h6 pa-0">Input Text</v-card-title> <v-card-title class="card-title">Input Text</v-card-title>
<v-card-text>
<v-textarea <v-textarea
v-model="inputText" v-model="inputText"
@input="handleInput" @input="handleInput"
placeholder="Enter your text here to convert to Mermaid diagram..." placeholder="Enter your text here to convert to Mermaid diagram..."
class="text-input" class="text-input"
variant="outlined"
rows="10" rows="10"
></v-textarea> ></v-textarea>
<div class="button-group"> <div class="button-group">
<!-- <button @click="convertNow" class="convert-btn" :disabled="isProcessing">
{{ isProcessing ? 'Converting...' : 'Convert Now' }}
</button> -->
<v-btn <v-btn
@click="convertNow" @click="convertNow"
:loading="isProcessing" :loading="isProcessing"
:disabled="isProcessing"
color="primary" color="primary"
variant="flat"> class="convert-btn"
>
Convert Now Convert Now
</v-btn> </v-btn>
<!-- <button @click="clearAll" class="clear-btn">Clear All</button> --> <!-- <button @click="clearAll" class="clear-btn">Clear All</button> -->
<v-btn <v-btn
@click="clearAll" @click="clearAll"
variant="outlined"
color="secondary" color="secondary"
variant="outlined"> >
Clear All Clear All
</v-btn> </v-btn>
</div> </div>
<!-- Status Indicator --> <!-- Status Indicator -->
<!-- <div class="status" :class="currentState">
<span v-if="currentState === 'typing'">Typing...</span>
<span v-else-if="currentState === 'processing'">Converting...</span>
<span v-else-if="currentState === 'ready'">Ready</span>
<span v-else-if="currentState === 'error'">Error occurred</span>
</div> -->
<v-alert :type="alertType" <v-alert :type="alertType"
:icon="alertIcon" :icon="alertIcon"
variant="tonal" variant="tonal"
...@@ -58,18 +52,18 @@ ...@@ -58,18 +52,18 @@
class="my-2"> class="my-2">
{{ statusText }} {{ statusText }}
</v-alert> </v-alert>
</div> </v-card-text>
</v-card>
</v-col> </v-col>
<v-col cols="12" md="8" class="pa-3"> <v-col cols="12" md="8" class="pa-3">
<!-- Output Section --> <!-- Output Section -->
<div class="output-section"> <div elevation="2" class="output-section">
<!-- <h3>Mermaid Diagram</h3> --> <!-- <h3>Mermaid Diagram</h3> -->
<v-card-title class="text-h6 pa-0">Mermaid Diagram</v-card-title> <v-card-title class="text-h6 pa-0 ma-2">Mermaid Diagram</v-card-title>
<!-- Diagram Section --> <!-- Diagram Section -->
<v-card class="diagram-section mb-4"> <v-card-text>
<v-card-text class="pa-0">
<div class="diagram-header"> <div class="diagram-header">
<!-- <h4>Visual Diagram:</h4> --> <!-- <h4>Visual Diagram:</h4> -->
<v-card-subtitle class="text-subtitle-1 pa-0">Visual Diagram:</v-card-subtitle> <v-card-subtitle class="text-subtitle-1 pa-0">Visual Diagram:</v-card-subtitle>
...@@ -125,7 +119,6 @@ ...@@ -125,7 +119,6 @@
</div> </div>
</div> </div>
</v-card-text> </v-card-text>
</v-card>
<!-- Fullscreen Modal --> <!-- Fullscreen Modal -->
<div v-if="isFullscreen" class="fullscreen-modal" @click="closeFullscreen"> <div v-if="isFullscreen" class="fullscreen-modal" @click="closeFullscreen">
...@@ -663,15 +656,10 @@ ...@@ -663,15 +656,10 @@
gap: 40px; gap: 40px;
margin: 40px; margin: 40px;
} */ } */
.input-section, .output-section {
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
border: 1px solid #e9ecef;
}
.output-section { .output-section {
/* background: #f8f9fa; */ background: #f8f9fa;
padding: 20px; /* padding: 20px; */
border-radius: 8px; border-radius: 8px;
border: 1px solid #e9ecef; border: 1px solid #e9ecef;
max-width: 100%; max-width: 100%;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment