Skip to content

Commit d37be27

Browse files
committed
feat(new tool): Image to ASCII Art
Image to ASCII Art
1 parent 23f82d9 commit d37be27

File tree

4 files changed

+123
-1
lines changed

4 files changed

+123
-1
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
"highlight.js": "^11.7.0",
6565
"iarna-toml-esm": "^3.0.5",
6666
"ibantools": "^4.3.3",
67+
"image-to-ascii-art": "^0.0.4",
6768
"json5": "^2.2.3",
6869
"jwt-decode": "^3.1.2",
6970
"libphonenumber-js": "^1.10.28",
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<script setup lang="ts">
2+
import { ImageToAsciiArt } from 'image-to-ascii-art';
3+
import TextareaCopyable from '@/components/TextareaCopyable.vue';
4+
import { languages, translateToLanguage } from '@/utils/ascii-lang-utils';
5+
6+
const inputBase64 = ref('');
7+
const language = useStorage('image-to-ascii-art:language', 'raw');
8+
const scale = ref(100);
9+
const errored = ref(false);
10+
const processing = ref(false);
11+
12+
function toBase64(file: File) {
13+
return new Promise<string>((resolve, reject) => {
14+
const reader = new FileReader();
15+
reader.readAsDataURL(file);
16+
reader.onload = () => resolve(reader.result?.toString() ?? '');
17+
reader.onerror = error => reject(error);
18+
});
19+
}
20+
21+
const languagesOptions = languages.map(lang => ({ value: lang.id, label: lang.name }));
22+
23+
const output = computedAsync(async () => {
24+
const inputBase64Value = inputBase64.value;
25+
if (!inputBase64Value) {
26+
return '';
27+
}
28+
const scaleValue = scale.value / 100.0;
29+
const languageValue = language.value;
30+
31+
let outputValue = '';
32+
processing.value = true;
33+
try {
34+
errored.value = false;
35+
36+
const imageToAsciiArt = new ImageToAsciiArt({
37+
config: {
38+
drawWidth: scaleValue,
39+
drawHeight: scaleValue * 0.4,
40+
},
41+
});
42+
outputValue = translateToLanguage(await imageToAsciiArt.convert(inputBase64Value), languageValue);
43+
imageToAsciiArt.destroy();
44+
}
45+
catch (e) {
46+
errored.value = true;
47+
}
48+
processing.value = false;
49+
50+
return outputValue;
51+
});
52+
53+
async function onFileUploaded(uploadedFile: File) {
54+
inputBase64.value = await toBase64(uploadedFile);
55+
}
56+
</script>
57+
58+
<template>
59+
<c-card style="max-width: 600px;">
60+
<div style="flex: 0 0 100%">
61+
<div mx-auto max-w-600px>
62+
<c-file-upload
63+
title="Drag and drop a Image file here, or click to select a file"
64+
paste-image
65+
@file-upload="onFileUploaded"
66+
/>
67+
</div>
68+
</div>
69+
70+
<n-form-item label="Output scale" label-placement="left" mt-2>
71+
<n-slider v-model:value="scale" :step="1" :min="1" :max="100" mr-2 />
72+
<n-input-number v-model:value="scale" size="small" :min="1" :max="100" />
73+
</n-form-item>
74+
75+
<c-select v-model:value="language" :options="languagesOptions" searchable mt-3 />
76+
77+
<n-divider />
78+
79+
<div v-if="processing" flex items-center justify-center>
80+
<n-spin size="medium" />
81+
<span class="ml-2">Processing...</span>
82+
</div>
83+
84+
<c-alert v-if="errored" mt-1 text-center type="error">
85+
Current settings resulted in error.
86+
</c-alert>
87+
88+
<n-form-item v-if="!processing && !errored" label="Ascii Art text:">
89+
<TextareaCopyable
90+
:value="output"
91+
mb-1 mt-1
92+
copy-placement="outside"
93+
/>
94+
</n-form-item>
95+
</c-card>
96+
</template>
97+
98+
<style lang="less">
99+
.n-code pre {
100+
font-size: 0.2em !important;
101+
}
102+
</style>

src/tools/image-to-ascii-art/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Artboard } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'Image to ASCII Art',
6+
path: '/image-to-ascii-art',
7+
description: 'Image to ASCII Art Generator',
8+
keywords: ['image', 'ascii', 'art'],
9+
component: () => import('./image-to-ascii-art.vue'),
10+
icon: Artboard,
11+
createdAt: new Date('2024-03-15'),
12+
});

src/tools/index.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { tool as asciiTextDrawer } from './ascii-text-drawer';
66

77
import { tool as textToUnicode } from './text-to-unicode';
88
import { tool as safelinkDecoder } from './safelink-decoder';
9+
import { tool as imageToAsciiArt } from './image-to-ascii-art';
910
import { tool as pdfSignatureChecker } from './pdf-signature-checker';
1011
import { tool as numeronymGenerator } from './numeronym-generator';
1112
import { tool as macAddressGenerator } from './mac-address-generator';
@@ -132,7 +133,13 @@ export const toolsByCategory: ToolCategory[] = [
132133
},
133134
{
134135
name: 'Images and videos',
135-
components: [qrCodeGenerator, wifiQrCodeGenerator, svgPlaceholderGenerator, cameraRecorder],
136+
components: [
137+
qrCodeGenerator,
138+
wifiQrCodeGenerator,
139+
svgPlaceholderGenerator,
140+
cameraRecorder,
141+
imageToAsciiArt,
142+
],
136143
},
137144
{
138145
name: 'Development',

0 commit comments

Comments
 (0)