mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-05-04 05:19:12 -04:00
feat: add file upload option and word wrap
This commit is contained in:
parent
78c4436b3f
commit
0c2dcbdf67
2 changed files with 93 additions and 12 deletions
|
@ -7,7 +7,15 @@ import sqlHljs from 'highlight.js/lib/languages/sql';
|
||||||
import xmlHljs from 'highlight.js/lib/languages/xml';
|
import xmlHljs from 'highlight.js/lib/languages/xml';
|
||||||
import yamlHljs from 'highlight.js/lib/languages/yaml';
|
import yamlHljs from 'highlight.js/lib/languages/yaml';
|
||||||
import iniHljs from 'highlight.js/lib/languages/ini';
|
import iniHljs from 'highlight.js/lib/languages/ini';
|
||||||
|
import bashHljs from 'highlight.js/lib/languages/bash';
|
||||||
|
import markdownHljs from 'highlight.js/lib/languages/markdown';
|
||||||
|
import jsHljs from 'highlight.js/lib/languages/javascript';
|
||||||
|
import cssHljs from 'highlight.js/lib/languages/css';
|
||||||
|
import goHljs from 'highlight.js/lib/languages/go';
|
||||||
|
import csharpHljs from 'highlight.js/lib/languages/csharp';
|
||||||
|
import { Base64 } from 'js-base64';
|
||||||
import { useCopy } from '@/composable/copy';
|
import { useCopy } from '@/composable/copy';
|
||||||
|
import { useDownloadFileFromBase64 } from '@/composable/downloadBase64';
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
@ -16,12 +24,17 @@ const props = withDefaults(
|
||||||
language?: string
|
language?: string
|
||||||
copyPlacement?: 'top-right' | 'bottom-right' | 'outside' | 'none'
|
copyPlacement?: 'top-right' | 'bottom-right' | 'outside' | 'none'
|
||||||
copyMessage?: string
|
copyMessage?: string
|
||||||
|
wordWrap?: boolean
|
||||||
|
downloadFileName?: string
|
||||||
|
downloadButtonText?: string
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
followHeightOf: null,
|
followHeightOf: null,
|
||||||
language: 'txt',
|
language: 'txt',
|
||||||
copyPlacement: 'top-right',
|
copyPlacement: 'top-right',
|
||||||
copyMessage: 'Copy to clipboard',
|
copyMessage: 'Copy to clipboard',
|
||||||
|
downloadFileName: '',
|
||||||
|
downloadButtonText: 'Download',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
hljs.registerLanguage('sql', sqlHljs);
|
hljs.registerLanguage('sql', sqlHljs);
|
||||||
|
@ -30,12 +43,25 @@ hljs.registerLanguage('html', xmlHljs);
|
||||||
hljs.registerLanguage('xml', xmlHljs);
|
hljs.registerLanguage('xml', xmlHljs);
|
||||||
hljs.registerLanguage('yaml', yamlHljs);
|
hljs.registerLanguage('yaml', yamlHljs);
|
||||||
hljs.registerLanguage('toml', iniHljs);
|
hljs.registerLanguage('toml', iniHljs);
|
||||||
|
hljs.registerLanguage('bash', bashHljs);
|
||||||
|
hljs.registerLanguage('markdown', markdownHljs);
|
||||||
|
hljs.registerLanguage('css', cssHljs);
|
||||||
|
hljs.registerLanguage('javascript', jsHljs);
|
||||||
|
hljs.registerLanguage('go', goHljs);
|
||||||
|
hljs.registerLanguage('csharp', csharpHljs);
|
||||||
|
|
||||||
const { value, language, followHeightOf, copyPlacement, copyMessage } = toRefs(props);
|
const { value, language, followHeightOf, copyPlacement, copyMessage, downloadFileName, downloadButtonText } = toRefs(props);
|
||||||
const { height } = followHeightOf.value ? useElementSize(followHeightOf) : { height: ref(null) };
|
const { height } = followHeightOf.value ? useElementSize(followHeightOf) : { height: ref(null) };
|
||||||
|
|
||||||
const { copy, isJustCopied } = useCopy({ source: value, createToast: false });
|
const { copy, isJustCopied } = useCopy({ source: value, createToast: false });
|
||||||
const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : copyMessage.value);
|
const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : copyMessage.value);
|
||||||
|
|
||||||
|
const valueBase64 = computed(() => Base64.encode(value.value));
|
||||||
|
const { download } = useDownloadFileFromBase64(
|
||||||
|
{
|
||||||
|
source: valueBase64,
|
||||||
|
filename: downloadFileName,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -47,11 +73,16 @@ const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : copyMessage.
|
||||||
:style="height ? `min-height: ${height - 40 /* card padding */ + 10 /* negative margin compensation */}px` : ''"
|
:style="height ? `min-height: ${height - 40 /* card padding */ + 10 /* negative margin compensation */}px` : ''"
|
||||||
>
|
>
|
||||||
<n-config-provider :hljs="hljs">
|
<n-config-provider :hljs="hljs">
|
||||||
<n-code :code="value" :language="language" :trim="false" data-test-id="area-content" />
|
<n-code :code="value" :language="language" :word-wrap="wordWrap" :trim="false" data-test-id="area-content" />
|
||||||
</n-config-provider>
|
</n-config-provider>
|
||||||
</n-scrollbar>
|
</n-scrollbar>
|
||||||
<div absolute right-10px top-10px>
|
<div
|
||||||
<c-tooltip v-if="value" :tooltip="tooltipText" position="left">
|
v-if="value && copyPlacement !== 'none'"
|
||||||
|
absolute right-10px
|
||||||
|
:top-10px="copyPlacement === 'top-right' ? '' : 'no'"
|
||||||
|
:bottom-10px="copyPlacement === 'bottom-right' ? '' : 'no'"
|
||||||
|
>
|
||||||
|
<c-tooltip v-if="value && copyPlacement !== 'outside'" :tooltip="tooltipText" position="left">
|
||||||
<c-button circle important:h-10 important:w-10 @click="copy()">
|
<c-button circle important:h-10 important:w-10 @click="copy()">
|
||||||
<n-icon size="22" :component="Copy" />
|
<n-icon size="22" :component="Copy" />
|
||||||
</c-button>
|
</c-button>
|
||||||
|
@ -63,6 +94,11 @@ const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : copyMessage.
|
||||||
{{ tooltipText }}
|
{{ tooltipText }}
|
||||||
</c-button>
|
</c-button>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="downloadFileName !== '' && value !== ''" mt-5 flex justify-center>
|
||||||
|
<c-button secondary @click="download">
|
||||||
|
{{ downloadButtonText }}
|
||||||
|
</c-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,31 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import PostalMime from 'postal-mime';
|
import PostalMime from 'postal-mime';
|
||||||
|
|
||||||
|
const inputType = ref<'file' | 'content'>('file');
|
||||||
const emailContent = ref('');
|
const emailContent = ref('');
|
||||||
|
const fileInput = ref() as Ref<File | null>;
|
||||||
|
const error = ref('');
|
||||||
|
|
||||||
const parsedEmail = computedAsync(async () => {
|
const parsedEmail = computedAsync(async () => {
|
||||||
const emailContentValue = emailContent.value;
|
const emailContentValue = emailContent.value;
|
||||||
|
const file = fileInput.value;
|
||||||
|
error.value = '';
|
||||||
try {
|
try {
|
||||||
return await PostalMime.parse(emailContentValue);
|
if (inputType.value === 'file' && file) {
|
||||||
|
return await PostalMime.parse(await file.arrayBuffer());
|
||||||
|
} else if (inputType.value === 'content' && emailContentValue) {
|
||||||
|
return await PostalMime.parse(emailContentValue);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (e: any) {
|
catch (e: any) {
|
||||||
return e.toString();
|
error.value = e.toString();
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function downloadFile(data: Uint8Array, fileName: string, fileType: string) {
|
function downloadFile(data: ArrayBuffer, fileName: string, fileType: string) {
|
||||||
const blob = new Blob([data], { type: fileType || 'application/octet-stream' });
|
const blob = new Blob([data], { type: fileType || 'application/octet-stream' });
|
||||||
const downloadUrl = URL.createObjectURL(blob);
|
const downloadUrl = URL.createObjectURL(blob);
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
|
@ -23,12 +35,38 @@ function downloadFile(data: Uint8Array, fileName: string, fileType: string) {
|
||||||
a.click();
|
a.click();
|
||||||
URL.revokeObjectURL(downloadUrl);
|
URL.revokeObjectURL(downloadUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onUpload(file: File) {
|
||||||
|
if (file) {
|
||||||
|
fileInput.value = file;
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div style="max-width: 600px;">
|
<div style="max-width: 600px;">
|
||||||
<c-card title="Input" mb-2>
|
<c-card title="Input" mb-2>
|
||||||
|
<n-radio-group v-model:value="inputType" name="radiogroup" mb-2 flex justify-center>
|
||||||
|
<n-space>
|
||||||
|
<n-radio
|
||||||
|
value="file"
|
||||||
|
label="File"
|
||||||
|
/>
|
||||||
|
<n-radio
|
||||||
|
value="content"
|
||||||
|
label="Content"
|
||||||
|
/>
|
||||||
|
</n-space>
|
||||||
|
</n-radio-group>
|
||||||
|
|
||||||
|
<c-file-upload
|
||||||
|
v-if="inputType === 'file'"
|
||||||
|
title="Drag and drop EML file here, or click to select a file"
|
||||||
|
@file-upload="onUpload"
|
||||||
|
/>
|
||||||
|
|
||||||
<c-input-text
|
<c-input-text
|
||||||
|
v-if="inputType === 'content'"
|
||||||
v-model:value="emailContent"
|
v-model:value="emailContent"
|
||||||
label="Email Content"
|
label="Email Content"
|
||||||
multiline
|
multiline
|
||||||
|
@ -38,32 +76,39 @@ function downloadFile(data: Uint8Array, fileName: string, fileType: string) {
|
||||||
/>
|
/>
|
||||||
</c-card>
|
</c-card>
|
||||||
|
|
||||||
<c-card v-if="parsedEmail && emailContent" title="Output">
|
<c-alert v-if="error">
|
||||||
|
{{ error }}
|
||||||
|
</c-alert>
|
||||||
|
|
||||||
|
<c-card v-if="!error && parsedEmail" title="Output">
|
||||||
|
<input-copyable v-if="fileInput?.name" label="File Name" :value="fileInput?.name" />
|
||||||
<input-copyable v-if="parsedEmail.date" label="Date" :value="parsedEmail.date" />
|
<input-copyable v-if="parsedEmail.date" label="Date" :value="parsedEmail.date" />
|
||||||
<input-copyable v-if="parsedEmail.from?.name" label="From (name)" :value="parsedEmail.from?.name" />
|
<input-copyable v-if="parsedEmail.from?.name" label="From (name)" :value="parsedEmail.from?.name" />
|
||||||
<input-copyable v-if="parsedEmail.from" label="From (address)" :value="parsedEmail.from?.address || parsedEmail.from" />
|
<input-copyable v-if="parsedEmail.from" label="From (address)" :value="parsedEmail.from?.address || parsedEmail.from" />
|
||||||
<input-copyable v-if="parsedEmail.to" label="To" :value="JSON.stringify(parsedEmail.to)" />
|
<input-copyable v-if="parsedEmail.to" label="To" :value="JSON.stringify(parsedEmail.to)" />
|
||||||
<input-copyable v-if="parsedEmail.cc" label="Cc" :value="JSON.stringify(parsedEmail.cc)" />
|
<input-copyable v-if="parsedEmail.cc" label="Cc" :value="JSON.stringify(parsedEmail.cc)" />
|
||||||
<input-copyable v-if="parsedEmail.bcc?.name" label="Bcc" :value="JSON.stringify(parsedEmail.bcc)" />
|
<input-copyable v-if="parsedEmail.bcc" label="Bcc" :value="JSON.stringify(parsedEmail.bcc)" />
|
||||||
<input-copyable v-if="parsedEmail.replyTo" label="Reply-To" :value="JSON.stringify(parsedEmail.replyTo)" />
|
<input-copyable v-if="parsedEmail.replyTo" label="Reply-To" :value="JSON.stringify(parsedEmail.replyTo)" />
|
||||||
<input-copyable v-if="parsedEmail.subject" label="Subject" :value="parsedEmail.subject" />
|
<input-copyable v-if="parsedEmail.subject" label="Subject" :value="parsedEmail.subject" />
|
||||||
<c-card v-if="parsedEmail.text" title="Plain Content" mb-2>
|
<c-card v-if="parsedEmail.text" title="Plain Content" mb-2>
|
||||||
<details>
|
<details>
|
||||||
<summary>See content</summary>
|
<summary>See content</summary>
|
||||||
<textarea-copyable :value="parsedEmail.text" />
|
<textarea-copyable :value="parsedEmail.text" word-wrap />
|
||||||
</details>
|
</details>
|
||||||
</c-card>
|
</c-card>
|
||||||
<c-card v-if="parsedEmail.html" title="Html Content" mb-2>
|
<c-card v-if="parsedEmail.html" title="Html Content" mb-2>
|
||||||
<details>
|
<details>
|
||||||
<summary>See content</summary>
|
<summary>See content</summary>
|
||||||
<textarea-copyable :value="parsedEmail.html" />
|
<textarea-copyable :value="parsedEmail.html" word-wrap />
|
||||||
</details>
|
</details>
|
||||||
</c-card>
|
</c-card>
|
||||||
<c-card v-if="parsedEmail?.attachments?.length" title="Attachments" mb-2>
|
<c-card v-if="parsedEmail?.attachments?.length" title="Attachments" mb-2>
|
||||||
<n-table>
|
<n-table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">Attachment</th><th scope="col" />
|
<th scope="col">
|
||||||
|
Attachment
|
||||||
|
</th><th scope="col" />
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue