Merge 1440c23331 into e876d03608
				
					
				
			This commit is contained in:
		
						commit
						58c75c3126
					
				| @ -64,6 +64,7 @@ | ||||
|     "highlight.js": "^11.7.0", | ||||
|     "iarna-toml-esm": "^3.0.5", | ||||
|     "ibantools": "^4.3.3", | ||||
|     "image-to-ascii-art": "^0.0.4", | ||||
|     "json5": "^2.2.3", | ||||
|     "jwt-decode": "^3.1.2", | ||||
|     "libphonenumber-js": "^1.10.28", | ||||
|  | ||||
							
								
								
									
										19
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										19
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							| @ -92,6 +92,9 @@ dependencies: | ||||
|   ibantools: | ||||
|     specifier: ^4.3.3 | ||||
|     version: 4.3.3 | ||||
|   image-to-ascii-art: | ||||
|     specifier: ^0.0.4 | ||||
|     version: 0.0.4 | ||||
|   json5: | ||||
|     specifier: ^2.2.3 | ||||
|     version: 2.2.3 | ||||
| @ -3351,7 +3354,7 @@ packages: | ||||
|     dependencies: | ||||
|       '@unhead/dom': 0.5.1 | ||||
|       '@unhead/schema': 0.5.1 | ||||
|       '@vueuse/shared': 10.7.2(vue@3.3.4) | ||||
|       '@vueuse/shared': 10.9.0(vue@3.3.4) | ||||
|       unhead: 0.5.1 | ||||
|       vue: 3.3.4 | ||||
|     transitivePeerDependencies: | ||||
| @ -3993,10 +3996,10 @@ packages: | ||||
|       - vue | ||||
|     dev: false | ||||
| 
 | ||||
|   /@vueuse/shared@10.7.2(vue@3.3.4): | ||||
|     resolution: {integrity: sha512-qFbXoxS44pi2FkgFjPvF4h7c9oMDutpyBdcJdMYIMg9XyXli2meFMuaKn+UMgsClo//Th6+beeCgqweT/79BVA==} | ||||
|   /@vueuse/shared@10.9.0(vue@3.3.4): | ||||
|     resolution: {integrity: sha512-Uud2IWncmAfJvRaFYzv5OHDli+FbOzxiVEQdLCKQKLyhz94PIyFC3CHcH7EDMwIn8NPtD06+PNbC/PiO0LGLtw==} | ||||
|     dependencies: | ||||
|       vue-demi: 0.14.6(vue@3.3.4) | ||||
|       vue-demi: 0.14.7(vue@3.3.4) | ||||
|     transitivePeerDependencies: | ||||
|       - '@vue/composition-api' | ||||
|       - vue | ||||
| @ -6124,6 +6127,10 @@ packages: | ||||
|     dev: true | ||||
|     optional: true | ||||
| 
 | ||||
|   /image-to-ascii-art@0.0.4: | ||||
|     resolution: {integrity: sha512-MvY8f2zQv8oAMdxK7908Y+JMan7bsUaSQKS/lHRJwzATyQyvoE4MCJXeqFMNxmFUWcxPqpD1ioqOUvR1peZzuA==} | ||||
|     dev: false | ||||
| 
 | ||||
|   /import-fresh@3.3.0: | ||||
|     resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} | ||||
|     engines: {node: '>=6'} | ||||
| @ -9151,8 +9158,8 @@ packages: | ||||
|       vue: 3.3.4 | ||||
|     dev: false | ||||
| 
 | ||||
|   /vue-demi@0.14.6(vue@3.3.4): | ||||
|     resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==} | ||||
|   /vue-demi@0.14.7(vue@3.3.4): | ||||
|     resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==} | ||||
|     engines: {node: '>=12'} | ||||
|     hasBin: true | ||||
|     requiresBuild: true | ||||
|  | ||||
							
								
								
									
										25
									
								
								src/tools/image-to-ascii-art/image-to-ascii-art.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/tools/image-to-ascii-art/image-to-ascii-art.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | ||||
| declare module 'image-to-ascii-art' { | ||||
|     interface ConfigInterface { | ||||
|         // the integer of pixels drawn on the canvas.
 | ||||
|         // it sets bigger,the generated ascii art will be more detailed.
 | ||||
|         // it has two type to set:
 | ||||
|         //    1. (0, 1] decimal.result is that this number multiplied by the number of pixels in the original image
 | ||||
|         //    2. An integer greater than 1.
 | ||||
|         drawWidth?: number; | ||||
|         drawHeight?: number; | ||||
|         // the integer that pick one for every how many pixels.
 | ||||
|         // it must be an integer greater than 0.
 | ||||
|         pickDensityHorizontal?: number; | ||||
|         pickDensityVertical?: number; | ||||
|         // set the char of every grey range.
 | ||||
|         greyRangeChar?: GreyRangeChar[]; | ||||
|         // if a grey value can't match one of the 'greyRangeChar' config,use this char.
 | ||||
|         defaultGreyChar?: string; | ||||
|     } | ||||
| 
 | ||||
|     class ImageToAsciiArt { | ||||
|         constructor({ canvas, config = {} }: { canvas?: HTMLCanvasElement; config?: ConfigInterface } = {}) | ||||
|         public convert(image: string | HTMLImageElement): Promise<string> | ||||
|         public destroy(): void | ||||
|     } | ||||
| } | ||||
							
								
								
									
										102
									
								
								src/tools/image-to-ascii-art/image-to-ascii-art.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								src/tools/image-to-ascii-art/image-to-ascii-art.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,102 @@ | ||||
| <script setup lang="ts"> | ||||
| import { ImageToAsciiArt } from 'image-to-ascii-art'; | ||||
| import TextareaCopyable from '@/components/TextareaCopyable.vue'; | ||||
| import { languages, translateToLanguage } from '@/utils/ascii-lang-utils'; | ||||
| 
 | ||||
| const inputBase64 = ref(''); | ||||
| const language = useStorage('image-to-ascii-art:language', 'raw'); | ||||
| const scale = ref(100); | ||||
| const errored = ref(false); | ||||
| const processing = ref(false); | ||||
| 
 | ||||
| function toBase64(file: File) { | ||||
|   return new Promise<string>((resolve, reject) => { | ||||
|     const reader = new FileReader(); | ||||
|     reader.readAsDataURL(file); | ||||
|     reader.onload = () => resolve(reader.result?.toString() ?? ''); | ||||
|     reader.onerror = error => reject(error); | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| const languagesOptions = languages.map(lang => ({ value: lang.id, label: lang.name })); | ||||
| 
 | ||||
| const output = computedAsync(async () => { | ||||
|   const inputBase64Value = inputBase64.value; | ||||
|   if (!inputBase64Value) { | ||||
|     return ''; | ||||
|   } | ||||
|   const scaleValue = scale.value / 100.0; | ||||
|   const languageValue = language.value; | ||||
| 
 | ||||
|   let outputValue = ''; | ||||
|   processing.value = true; | ||||
|   try { | ||||
|     errored.value = false; | ||||
| 
 | ||||
|     const imageToAsciiArt = new ImageToAsciiArt({ | ||||
|       config: { | ||||
|         drawWidth: scaleValue, | ||||
|         drawHeight: scaleValue * 0.4, | ||||
|       }, | ||||
|     }); | ||||
|     outputValue = translateToLanguage(await imageToAsciiArt.convert(inputBase64Value), languageValue); | ||||
|     imageToAsciiArt.destroy(); | ||||
|   } | ||||
|   catch (e) { | ||||
|     errored.value = true; | ||||
|   } | ||||
|   processing.value = false; | ||||
| 
 | ||||
|   return outputValue; | ||||
| }); | ||||
| 
 | ||||
| async function onFileUploaded(uploadedFile: File) { | ||||
|   inputBase64.value = await toBase64(uploadedFile); | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <c-card style="max-width: 600px;"> | ||||
|     <div style="flex: 0 0 100%"> | ||||
|       <div mx-auto max-w-600px> | ||||
|         <c-file-upload | ||||
|           title="Drag and drop a Image file here, or click to select a file" | ||||
|           paste-image | ||||
|           @file-upload="onFileUploaded" | ||||
|         /> | ||||
|       </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <n-form-item label="Output scale" label-placement="left" mt-2> | ||||
|       <n-slider v-model:value="scale" :step="1" :min="1" :max="100" mr-2 /> | ||||
|       <n-input-number v-model:value="scale" size="small" :min="1" :max="100" /> | ||||
|     </n-form-item> | ||||
| 
 | ||||
|     <c-select v-model:value="language" :options="languagesOptions" searchable mt-3 /> | ||||
| 
 | ||||
|     <n-divider /> | ||||
| 
 | ||||
|     <div v-if="processing" flex items-center justify-center> | ||||
|       <n-spin size="medium" /> | ||||
|       <span class="ml-2">Processing...</span> | ||||
|     </div> | ||||
| 
 | ||||
|     <c-alert v-if="errored" mt-1 text-center type="error"> | ||||
|       Current settings resulted in error. | ||||
|     </c-alert> | ||||
| 
 | ||||
|     <n-form-item v-if="!processing && !errored" label="Ascii Art text:"> | ||||
|       <TextareaCopyable | ||||
|         :value="output" | ||||
|         mb-1 mt-1 | ||||
|         copy-placement="outside" | ||||
|       /> | ||||
|     </n-form-item> | ||||
|   </c-card> | ||||
| </template> | ||||
| 
 | ||||
| <style lang="less"> | ||||
| .n-code pre { | ||||
|   font-size: 0.2em !important; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										12
									
								
								src/tools/image-to-ascii-art/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/tools/image-to-ascii-art/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| import { Artboard } from '@vicons/tabler'; | ||||
| import { defineTool } from '../tool'; | ||||
| 
 | ||||
| export const tool = defineTool({ | ||||
|   name: 'Image to ASCII Art', | ||||
|   path: '/image-to-ascii-art', | ||||
|   description: 'Image to ASCII Art Generator', | ||||
|   keywords: ['image', 'ascii', 'art'], | ||||
|   component: () => import('./image-to-ascii-art.vue'), | ||||
|   icon: Artboard, | ||||
|   createdAt: new Date('2024-03-15'), | ||||
| }); | ||||
| @ -6,6 +6,7 @@ import { tool as asciiTextDrawer } from './ascii-text-drawer'; | ||||
| 
 | ||||
| import { tool as textToUnicode } from './text-to-unicode'; | ||||
| import { tool as safelinkDecoder } from './safelink-decoder'; | ||||
| import { tool as imageToAsciiArt } from './image-to-ascii-art'; | ||||
| import { tool as pdfSignatureChecker } from './pdf-signature-checker'; | ||||
| import { tool as numeronymGenerator } from './numeronym-generator'; | ||||
| import { tool as macAddressGenerator } from './mac-address-generator'; | ||||
| @ -132,7 +133,13 @@ export const toolsByCategory: ToolCategory[] = [ | ||||
|   }, | ||||
|   { | ||||
|     name: 'Images and videos', | ||||
|     components: [qrCodeGenerator, wifiQrCodeGenerator, svgPlaceholderGenerator, cameraRecorder], | ||||
|     components: [ | ||||
|       qrCodeGenerator, | ||||
|       wifiQrCodeGenerator, | ||||
|       svgPlaceholderGenerator, | ||||
|       cameraRecorder, | ||||
|       imageToAsciiArt, | ||||
|     ], | ||||
|   }, | ||||
|   { | ||||
|     name: 'Development', | ||||
|  | ||||
							
								
								
									
										55
									
								
								src/utils/ascii-lang-utils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/utils/ascii-lang-utils.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | ||||
| function escapeXml(unsafe: string) { | ||||
|   return unsafe.replace(/[<>&'"]/g, (c: string) => { | ||||
|     switch (c) { | ||||
|       case '<': return '<'; | ||||
|       case '>': return '>'; | ||||
|       case '&': return '&'; | ||||
|       case '\'': return '''; | ||||
|       case '"': return '"'; | ||||
|     } | ||||
|     return c; | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| const dontEscape = ''; | ||||
| const escapeBackslash = '\\\\'; | ||||
| const escapeSingleQuote = '\''; | ||||
| const escapeDoubleQuote = '"'; | ||||
| const defaultEscape = escapeBackslash + escapeSingleQuote + escapeDoubleQuote; | ||||
| export const languages = [ | ||||
|   { id: 'raw', name: 'Raw Text', prefix: '', suffix: '', begin: '', end: '', escape: dontEscape }, | ||||
|   { id: 'bash', name: 'Bash', prefix: 'echo "', suffix: '"', begin: '', end: '', escape: escapeBackslash + escapeDoubleQuote }, | ||||
|   { id: 'pwsh', name: 'PowerShell', prefix: 'Write-Output \'', suffix: '\'', begin: '', end: '', escape: escapeBackslash + escapeSingleQuote }, | ||||
|   { id: 'c', name: 'C', prefix: 'printf("', suffix: '\\n");', begin: '#include <stdio.h>\n', end: '', escape: defaultEscape }, | ||||
|   { id: 'cpp', name: 'C++', prefix: 'std::cout << "', suffix: '\\n";', begin: '#include <iostream>\n', end: '', escape: defaultEscape }, | ||||
|   { id: 'csharp', name: 'C#', prefix: 'Console.WriteLine(@"', suffix: '");', begin: 'using System;\n', end: '', escape: escapeDoubleQuote }, | ||||
|   { id: 'vbnet', name: 'VB.Net', prefix: 'Console.WriteLine("', suffix: '")', begin: '', end: '', escape: (l: string) => l.replace('"', '""') }, | ||||
|   { id: 'node', name: 'Node.js', prefix: 'console.log("', suffix: '");', begin: '', end: '', escape: defaultEscape }, | ||||
|   { id: 'python', name: 'Python', prefix: 'print("', suffix: '")', begin: '', end: '', escape: escapeBackslash + escapeDoubleQuote }, | ||||
|   { id: 'html', name: 'HTML', prefix: '', suffix: '', begin: '<pre>\n', end: '\n</pre>', escape: (l: string) => escapeXml(l) }, | ||||
|   { id: 'rust', name: 'Rust', prefix: 'println!("', suffix: '");', begin: '', end: '', escape: defaultEscape }, | ||||
|   { id: 'go', name: 'Go', prefix: 'fmt.Println("', suffix: '")', begin: 'import "fmt"\n', end: '', escape: defaultEscape }, | ||||
|   { id: 'ruby', name: 'Ruby', prefix: 'puts "', suffix: '"', begin: '', end: '', escape: defaultEscape }, | ||||
|   { id: 'php', name: 'PHP', prefix: 'echo "', suffix: '\\n";', begin: '<?php\n', end: '\n?>', escape: defaultEscape }, | ||||
|   { id: 'swift', name: 'Swift', prefix: 'print("', suffix: '")', begin: '', end: '', escape: defaultEscape }, | ||||
|   { id: 'kotlin', name: 'Kotlin', prefix: 'println("', suffix: '")', begin: '', end: '', escape: defaultEscape }, | ||||
|   { id: 'sql', name: 'SQL', prefix: 'SELECT \'', suffix: '\\n\'', begin: '', end: '', escape: (l: string) => l.replace('\'', '\'\'') }, | ||||
|   { id: 'java', name: 'Java', prefix: 'System.out.println("', suffix: '");', begin: '', end: '', escape: defaultEscape }, | ||||
| ]; | ||||
| export function translateToLanguage(asciiArt: string, languageId: string) { | ||||
|   const langConfig = languages.find(l => l.id === languageId); | ||||
|   if (!langConfig) { | ||||
|     return asciiArt; | ||||
|   } | ||||
| 
 | ||||
|   const escape = typeof langConfig.escape === 'function' | ||||
|     ? langConfig.escape | ||||
|     : function (line: string) { | ||||
|       return langConfig.escape | ||||
|         ? line.replace(new RegExp(`([${langConfig.escape}])`, 'g'), '\\$1') | ||||
|         : line; | ||||
|     }; | ||||
|   return langConfig.begin + asciiArt.split('\n').map((line) => { | ||||
|     return langConfig.prefix + escape(line) + langConfig.suffix; | ||||
|   }).join('\n') + langConfig.end; | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user