diff --git a/frontend/components/task/TaskDocumentList.vue b/frontend/components/task/TaskDocumentList.vue
index 270f8c2..a9be141 100644
--- a/frontend/components/task/TaskDocumentList.vue
+++ b/frontend/components/task/TaskDocumentList.vue
@@ -68,6 +68,7 @@ function isImage(mimeType: string): boolean {
}
function getIconForMime(mimeType: string): string {
+ if (mimeType === 'text/markdown') return 'mdi:language-markdown'
if (mimeType === 'application/pdf') return 'heroicons:document-text'
if (mimeType.includes('spreadsheet') || mimeType.includes('excel')) return 'heroicons:table-cells'
if (mimeType.includes('word') || mimeType.includes('document')) return 'heroicons:document'
diff --git a/frontend/components/task/TaskDocumentPreview.vue b/frontend/components/task/TaskDocumentPreview.vue
index 8e7f2d3..deb7781 100644
--- a/frontend/components/task/TaskDocumentPreview.vue
+++ b/frontend/components/task/TaskDocumentPreview.vue
@@ -58,6 +58,46 @@
class="h-[85vh] w-[80vw] rounded-lg bg-white"
/>
+
+
+
+
{{ document.originalName }}
+
+
+
+
+
+
+
{{ textContent }}
+
+
+
@@ -73,7 +113,7 @@
- {{ document.originalName }}
+ {{ document.originalName }}
@@ -98,19 +138,56 @@ defineEmits<{
}>()
const overlayRef = ref(null)
+const textContent = ref('')
+const loadingText = ref(false)
+const copied = ref(false)
-const { getDownloadUrl } = useTaskDocumentService()
+const { getDownloadUrl, getContent } = useTaskDocumentService()
+const { t } = useI18n()
+
+const TEXT_MIME_TYPES = ['text/markdown', 'text/plain', 'text/csv', 'application/json', 'application/xml', 'text/xml']
+
+function isTextDocument(doc: TaskDocument | null): boolean {
+ if (!doc) return false
+ if (TEXT_MIME_TYPES.includes(doc.mimeType)) return true
+ return /\.(md|markdown|txt|csv|json|xml)$/i.test(doc.originalName)
+}
const downloadUrl = computed(() => props.document ? getDownloadUrl(props.document.id) : '')
const isImage = computed(() => props.document?.mimeType.startsWith('image/') ?? false)
const isPdf = computed(() => props.document?.mimeType === 'application/pdf')
+const isText = computed(() => isTextDocument(props.document))
-// Focus overlay for keyboard events
-watch(() => props.document, (doc) => {
- if (doc) {
- nextTick(() => overlayRef.value?.focus())
+async function copyContent() {
+ try {
+ await navigator.clipboard.writeText(textContent.value)
+ copied.value = true
+ useToast().success(t('taskDocuments.copied'))
+ setTimeout(() => { copied.value = false }, 2000)
+ } catch {
+ // Clipboard unavailable
}
-})
+}
+
+// Focus overlay for keyboard events, and load text content for text/markdown documents
+watch(() => props.document, async (doc) => {
+ textContent.value = ''
+ copied.value = false
+ if (!doc) return
+
+ nextTick(() => overlayRef.value?.focus())
+
+ if (isTextDocument(doc)) {
+ loadingText.value = true
+ try {
+ textContent.value = await getContent(doc.id)
+ } catch {
+ textContent.value = ''
+ } finally {
+ loadingText.value = false
+ }
+ }
+}, { immediate: true })