File size: 3,491 Bytes
8b9f7d9
 
 
 
81046e2
 
8b9f7d9
 
 
 
 
 
 
81046e2
8b9f7d9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81046e2
8b9f7d9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81046e2
8b9f7d9
81046e2
8b9f7d9
 
 
 
 
 
 
 
 
81046e2
8b9f7d9
 
 
 
 
81046e2
8b9f7d9
 
 
 
81046e2
8b9f7d9
 
 
 
 
 
 
 
 
 
81046e2
8b9f7d9
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
<script setup lang="ts">
import { ref } from 'vue'
import { marked } from 'marked'
import type { Citation } from '../types'
import type { Lang } from '../i18n'
import { t } from '../i18n'
import { analyzeImage } from '../api'
import Wc3Button from '../wc3/base/Button.vue'
import CitationPanel from './CitationPanel.vue'

const props = defineProps<{
  model: string
  showCitations: boolean
  lang: Lang
}>()

const selectedFile = ref<File | null>(null)
const imagePreview = ref('')
const question = ref('')
const analysis = ref('')
const citations = ref<Citation[]>([])
const isLoading = ref(false)
const error = ref('')
const fileInput = ref<HTMLInputElement | null>(null)

function renderMd(text: string): string {
  return marked.parse(text) as string
}

function handleFileSelect(e: Event) {
  const input = e.target as HTMLInputElement
  if (input.files?.[0]) {
    selectedFile.value = input.files[0]
    imagePreview.value = URL.createObjectURL(input.files[0])
    analysis.value = ''
    citations.value = []
    error.value = ''
  }
}

function handleDrop(e: DragEvent) {
  e.preventDefault()
  if (e.dataTransfer?.files[0]) {
    selectedFile.value = e.dataTransfer.files[0]
    imagePreview.value = URL.createObjectURL(e.dataTransfer.files[0])
    analysis.value = ''
    citations.value = []
    error.value = ''
  }
}

async function handleAnalyze() {
  if (!selectedFile.value || isLoading.value) return
  isLoading.value = true
  error.value = ''
  analysis.value = ''
  citations.value = []

  try {
    const res = await analyzeImage(selectedFile.value, question.value, props.model)
    analysis.value = res.analysis
    citations.value = res.citations
  } catch (e: any) {
    error.value = e.message || t('image.error', props.lang)
  } finally {
    isLoading.value = false
  }
}
</script>

<template>
  <div class="image-tab">
    <div
      class="upload-zone"
      @click="fileInput?.click()"
      @drop="handleDrop"
      @dragover.prevent
    >
      <input ref="fileInput" type="file" accept="image/*" @change="handleFileSelect" />
      <div v-if="!imagePreview">
        <div class="icon">📄</div>
        <p>{{ t('image.dropHint', lang) }}</p>
        <p style="font-size: 0.8rem; color: var(--wc3-text-muted);">
          {{ t('image.uploadHint', lang) }}
        </p>
      </div>
      <img v-else :src="imagePreview" class="image-preview" alt="Preview" />
    </div>

    <div v-if="selectedFile" style="margin-bottom: 1rem;">
      <input
        class="wc3-input"
        v-model="question"
        :placeholder="t('image.questionPlaceholder', lang)"
      />
    </div>

    <div v-if="selectedFile" style="margin-bottom: 1.5rem; height: 38px; min-width: 160px; display: inline-block;">
      <Wc3Button size="s" @click="handleAnalyze" :disabled="isLoading">
        {{ isLoading ? t('image.analyzing', lang) : t('image.analyze', lang) }}
      </Wc3Button>
    </div>

    <div v-if="isLoading" class="loading">
      <span>{{ t('image.analyzingStatus', lang) }}</span>
      <span class="dot-pulse"><span></span><span></span><span></span></span>
    </div>

    <div v-if="error" class="error-msg" style="margin-bottom: 1rem;">⚠️ {{ error }}</div>

    <div v-if="analysis" class="analysis-result">
      <div v-html="renderMd(analysis)"></div>
      <CitationPanel
        v-if="showCitations && citations.length"
        :citations="citations"
        :lang="lang"
        style="margin-top: 0.75rem;"
      />
    </div>
  </div>
</template>