diff --git a/package.json b/package.json index d1e6e458..e24a3d80 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "highlight.js": "^11.7.0", "iarna-toml-esm": "^3.0.5", "ibantools": "^4.3.3", - "js-base64": "^3.7.6", + "js-base64": "^3.7.7", "json5": "^2.2.3", "jwt-decode": "^3.1.2", "libphonenumber-js": "^1.10.28", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8619d8c0..aef1df41 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -93,7 +93,7 @@ dependencies: specifier: ^4.3.3 version: 4.3.3 js-base64: - specifier: ^3.7.6 + specifier: ^3.7.7 version: 3.7.7 json5: specifier: ^2.2.3 @@ -166,7 +166,7 @@ dependencies: version: 3.3.4 vue-i18n: specifier: ^9.9.1 - version: 9.9.1(vue@3.3.4) + version: 9.13.1(vue@3.3.4) vue-router: specifier: ^4.1.6 version: 4.1.6(vue@3.3.4) @@ -189,7 +189,7 @@ devDependencies: version: 1.1.50 '@intlify/unplugin-vue-i18n': specifier: ^2.0.0 - version: 2.0.0(rollup@2.79.1)(vue-i18n@9.9.1) + version: 2.0.0(rollup@2.79.1)(vue-i18n@9.13.1) '@playwright/test': specifier: ^1.32.3 version: 1.32.3 @@ -2209,8 +2209,8 @@ packages: - supports-color dev: true - /@intlify/bundle-utils@7.5.0(vue-i18n@9.9.1): - resolution: {integrity: sha512-6DymqusddBQ8kVtVBsVFFF7arNfIhuLacOmmsqayT2vl427j9m0VX12mMC+cgoVIodSpRfzYPaPTdPuJq7mK0Q==} + /@intlify/bundle-utils@7.5.1(vue-i18n@9.13.1): + resolution: {integrity: sha512-UovJl10oBIlmYEcWw+VIHdKY5Uv5sdPG0b/b6bOYxGLln3UwB75+2dlc0F3Fsa0RhoznQ5Rp589/BZpABpE4Xw==} engines: {node: '>= 14.16'} peerDependencies: petite-vue-i18n: '*' @@ -2221,8 +2221,8 @@ packages: vue-i18n: optional: true dependencies: - '@intlify/message-compiler': 9.9.1 - '@intlify/shared': 9.9.1 + '@intlify/message-compiler': 9.13.1 + '@intlify/shared': 9.13.1 acorn: 8.11.2 escodegen: 2.1.0 estree-walker: 2.0.2 @@ -2230,29 +2230,29 @@ packages: magic-string: 0.30.5 mlly: 1.4.2 source-map-js: 1.0.2 - vue-i18n: 9.9.1(vue@3.3.4) + vue-i18n: 9.13.1(vue@3.3.4) yaml-eslint-parser: 1.2.2 dev: true - /@intlify/core-base@9.9.1: - resolution: {integrity: sha512-qsV15dg7jNX2faBRyKMgZS8UcFJViWEUPLdzZ9UR0kQZpFVeIpc0AG7ZOfeP7pX2T9SQ5jSiorq/tii9nkkafA==} + /@intlify/core-base@9.13.1: + resolution: {integrity: sha512-+bcQRkJO9pcX8d0gel9ZNfrzU22sZFSA0WVhfXrf5jdJOS24a+Bp8pozuS9sBI9Hk/tGz83pgKfmqcn/Ci7/8w==} engines: {node: '>= 16'} dependencies: - '@intlify/message-compiler': 9.9.1 - '@intlify/shared': 9.9.1 + '@intlify/message-compiler': 9.13.1 + '@intlify/shared': 9.13.1 - /@intlify/message-compiler@9.9.1: - resolution: {integrity: sha512-zTvP6X6HeumHOXuAE1CMMsV6tTX+opKMOxO1OHTCg5N5Sm/F7d8o2jdT6W6L5oHUsJ/vvkGefHIs7Q3hfowmsA==} + /@intlify/message-compiler@9.13.1: + resolution: {integrity: sha512-SKsVa4ajYGBVm7sHMXd5qX70O2XXjm55zdZB3VeMFCvQyvLew/dLvq3MqnaIsTMF1VkkOb9Ttr6tHcMlyPDL9w==} engines: {node: '>= 16'} dependencies: - '@intlify/shared': 9.9.1 + '@intlify/shared': 9.13.1 source-map-js: 1.0.2 - /@intlify/shared@9.9.1: - resolution: {integrity: sha512-b3Pta1nwkz5rGq434v0psHwEwHGy1pYCttfcM22IE//K9owbpkEvFptx9VcuRAxjQdrO2If249cmDDjBu5wMDA==} + /@intlify/shared@9.13.1: + resolution: {integrity: sha512-u3b6BKGhE6j/JeRU6C/RL2FgyJfy6LakbtfeVF8fJXURpZZTzfh3e05J0bu0XPw447Q6/WUp3C4ajv4TMS4YsQ==} engines: {node: '>= 16'} - /@intlify/unplugin-vue-i18n@2.0.0(rollup@2.79.1)(vue-i18n@9.9.1): + /@intlify/unplugin-vue-i18n@2.0.0(rollup@2.79.1)(vue-i18n@9.13.1): resolution: {integrity: sha512-1oKvm92L9l2od2H9wKx2ZvR4tzn7gUtd7bPLI7AWUmm7U9H1iEypndt5d985ypxGsEs0gToDaKTrytbBIJwwSg==} engines: {node: '>= 14.16'} peerDependencies: @@ -2267,8 +2267,8 @@ packages: vue-i18n-bridge: optional: true dependencies: - '@intlify/bundle-utils': 7.5.0(vue-i18n@9.9.1) - '@intlify/shared': 9.9.1 + '@intlify/bundle-utils': 7.5.1(vue-i18n@9.13.1) + '@intlify/shared': 9.13.1 '@rollup/pluginutils': 5.0.5(rollup@2.79.1) '@vue/compiler-sfc': 3.3.4 debug: 4.3.4 @@ -2279,7 +2279,7 @@ packages: picocolors: 1.0.0 source-map-js: 1.0.2 unplugin: 1.4.0 - vue-i18n: 9.9.1(vue@3.3.4) + vue-i18n: 9.13.1(vue@3.3.4) transitivePeerDependencies: - rollup - supports-color @@ -3354,7 +3354,7 @@ packages: dependencies: '@unhead/dom': 0.5.1 '@unhead/schema': 0.5.1 - '@vueuse/shared': 10.8.0(vue@3.3.4) + '@vueuse/shared': 10.11.0(vue@3.3.4) unhead: 0.5.1 vue: 3.3.4 transitivePeerDependencies: @@ -3987,19 +3987,19 @@ packages: - vue dev: false - /@vueuse/shared@10.3.0(vue@3.3.4): - resolution: {integrity: sha512-kGqCTEuFPMK4+fNWy6dUOiYmxGcUbtznMwBZLC1PubidF4VZY05B+Oht7Jh7/6x4VOWGpvu3R37WHi81cKpiqg==} + /@vueuse/shared@10.11.0(vue@3.3.4): + resolution: {integrity: sha512-fyNoIXEq3PfX1L3NkNhtVQUSRtqYwJtJg+Bp9rIzculIZWHTkKSysujrOk2J+NrRulLTQH9+3gGSfYLWSEWU1A==} dependencies: - vue-demi: 0.14.5(vue@3.3.4) + vue-demi: 0.14.10(vue@3.3.4) transitivePeerDependencies: - '@vue/composition-api' - vue dev: false - /@vueuse/shared@10.8.0(vue@3.3.4): - resolution: {integrity: sha512-dUdy6zwHhULGxmr9YUg8e+EnB39gcM4Fe2oKBSrh3cOsV30JcMPtsyuspgFCUo5xxFNaeMf/W2yyKfST7Bg8oQ==} + /@vueuse/shared@10.3.0(vue@3.3.4): + resolution: {integrity: sha512-kGqCTEuFPMK4+fNWy6dUOiYmxGcUbtznMwBZLC1PubidF4VZY05B+Oht7Jh7/6x4VOWGpvu3R37WHi81cKpiqg==} dependencies: - vue-demi: 0.14.7(vue@3.3.4) + vue-demi: 0.14.5(vue@3.3.4) transitivePeerDependencies: - '@vue/composition-api' - vue @@ -9143,8 +9143,8 @@ packages: vue: 3.3.4 dev: false - /vue-demi@0.14.5(vue@3.3.4): - resolution: {integrity: sha512-o9NUVpl/YlsGJ7t+xuqJKx8EBGf1quRhCiT6D/J0pfwmk9zUwYkC7yrF4SZCe6fETvSM3UNL2edcbYrSyc4QHA==} + /vue-demi@0.14.10(vue@3.3.4): + resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} engines: {node: '>=12'} hasBin: true requiresBuild: true @@ -9158,8 +9158,8 @@ packages: vue: 3.3.4 dev: false - /vue-demi@0.14.7(vue@3.3.4): - resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==} + /vue-demi@0.14.5(vue@3.3.4): + resolution: {integrity: sha512-o9NUVpl/YlsGJ7t+xuqJKx8EBGf1quRhCiT6D/J0pfwmk9zUwYkC7yrF4SZCe6fETvSM3UNL2edcbYrSyc4QHA==} engines: {node: '>=12'} hasBin: true requiresBuild: true @@ -9191,14 +9191,14 @@ packages: - supports-color dev: true - /vue-i18n@9.9.1(vue@3.3.4): - resolution: {integrity: sha512-xyQ4VspLdNSPTKBFBPWa1tvtj+9HuockZwgFeD2OhxxXuC2CWeNvV4seu2o9+vbQOyQbhAM5Ez56oxUrrnTWdw==} + /vue-i18n@9.13.1(vue@3.3.4): + resolution: {integrity: sha512-mh0GIxx0wPtPlcB1q4k277y0iKgo25xmDPWioVVYanjPufDBpvu5ySTjP5wOrSvlYQ2m1xI+CFhGdauv/61uQg==} engines: {node: '>= 16'} peerDependencies: vue: ^3.0.0 dependencies: - '@intlify/core-base': 9.9.1 - '@intlify/shared': 9.9.1 + '@intlify/core-base': 9.13.1 + '@intlify/shared': 9.13.1 '@vue/devtools-api': 6.5.0 vue: 3.3.4 @@ -9449,6 +9449,7 @@ packages: /workbox-google-analytics@7.0.0: resolution: {integrity: sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==} + deprecated: It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained dependencies: workbox-background-sync: 7.0.0 workbox-core: 7.0.0 diff --git a/src/components/FormatTransformer.vue b/src/components/FormatTransformer.vue index bcfcdb58..12c3d691 100644 --- a/src/components/FormatTransformer.vue +++ b/src/components/FormatTransformer.vue @@ -1,7 +1,9 @@ @@ -53,5 +68,11 @@ const output = computed(() => transformer.value(input.value)); {{ outputLabel }} + + + + {{ downloadButtonText }} + + diff --git a/src/components/TextareaCopyable.vue b/src/components/TextareaCopyable.vue index 8b0aae61..a0961f1b 100644 --- a/src/components/TextareaCopyable.vue +++ b/src/components/TextareaCopyable.vue @@ -7,7 +7,15 @@ import sqlHljs from 'highlight.js/lib/languages/sql'; import xmlHljs from 'highlight.js/lib/languages/xml'; import yamlHljs from 'highlight.js/lib/languages/yaml'; 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 { useDownloadFileFromBase64 } from '@/composable/downloadBase64'; const props = withDefaults( defineProps<{ @@ -16,12 +24,17 @@ const props = withDefaults( language?: string copyPlacement?: 'top-right' | 'bottom-right' | 'outside' | 'none' copyMessage?: string + wordWrap?: boolean + downloadFileName?: string + downloadButtonText?: string }>(), { followHeightOf: null, language: 'txt', copyPlacement: 'top-right', copyMessage: 'Copy to clipboard', + downloadFileName: '', + downloadButtonText: 'Download', }, ); hljs.registerLanguage('sql', sqlHljs); @@ -30,12 +43,25 @@ hljs.registerLanguage('html', xmlHljs); hljs.registerLanguage('xml', xmlHljs); hljs.registerLanguage('yaml', yamlHljs); 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 { copy, isJustCopied } = useCopy({ source: value, createToast: false }); const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : copyMessage.value); + +const valueBase64 = computed(() => Base64.encode(value.value)); +const { download } = useDownloadFileFromBase64( + { + source: valueBase64, + filename: downloadFileName, + }); @@ -47,11 +73,16 @@ const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : copyMessage. :style="height ? `min-height: ${height - 40 /* card padding */ + 10 /* negative margin compensation */}px` : ''" > - + - - + + @@ -63,6 +94,11 @@ const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : copyMessage. {{ tooltipText }} + + + {{ downloadButtonText }} + + diff --git a/src/composable/downloadBase64.ts b/src/composable/downloadBase64.ts index 3bc20226..773541e2 100644 --- a/src/composable/downloadBase64.ts +++ b/src/composable/downloadBase64.ts @@ -1,6 +1,7 @@ import { extension as getExtensionFromMimeType, extension as getMimeTypeFromExtension } from 'mime-types'; -import type { Ref } from 'vue'; +import type { MaybeRef, Ref } from 'vue'; import _ from 'lodash'; +import { get } from '@vueuse/core'; export { getMimeTypeFromBase64, @@ -75,21 +76,11 @@ function downloadFromBase64({ sourceValue, filename, extension, fileMimeType }: } function useDownloadFileFromBase64( - { source, filename, extension, fileMimeType }: - { source: Ref; filename?: string; extension?: string; fileMimeType?: string }) { - return { - download() { - downloadFromBase64({ sourceValue: source.value, filename, extension, fileMimeType }); - }, - }; -} - -function useDownloadFileFromBase64Refs( { source, filename, extension }: - { source: Ref; filename?: Ref; extension?: Ref }) { + { source: MaybeRef; filename?: MaybeRef; extension?: MaybeRef }) { return { download() { - downloadFromBase64({ sourceValue: source.value, filename: filename?.value, extension: extension?.value }); + downloadFromBase64({ sourceValue: get(source), filename: get(filename), extension: get(extension) }); }, }; } @@ -116,3 +107,13 @@ function previewImageFromBase64(base64String: string): HTMLImageElement { return img; } + +function useDownloadFileFromBase64Refs( + { source, filename, extension }: + { source: Ref; filename?: Ref; extension?: Ref }) { + return { + download() { + downloadFromBase64({ sourceValue: source.value, filename: filename?.value, extension: extension?.value }); + }, + }; +} diff --git a/src/tools/json-minify/json-minify.vue b/src/tools/json-minify/json-minify.vue index 51d62703..e29dd0e0 100644 --- a/src/tools/json-minify/json-minify.vue +++ b/src/tools/json-minify/json-minify.vue @@ -23,5 +23,6 @@ const rules: UseValidationRule[] = [ output-language="json" :input-validation-rules="rules" :transformer="transformer" + download-file-name="output.json" /> diff --git a/src/tools/json-to-csv/json-to-csv.vue b/src/tools/json-to-csv/json-to-csv.vue index e2f5ddb6..0c0c8dc6 100644 --- a/src/tools/json-to-csv/json-to-csv.vue +++ b/src/tools/json-to-csv/json-to-csv.vue @@ -28,5 +28,6 @@ const rules: UseValidationRule[] = [ output-label="CSV version of your JSON" :input-validation-rules="rules" :transformer="transformer" + download-file-name="output.csv" /> diff --git a/src/tools/json-to-toml/json-to-toml.vue b/src/tools/json-to-toml/json-to-toml.vue index b1d37a38..bd3d3633 100644 --- a/src/tools/json-to-toml/json-to-toml.vue +++ b/src/tools/json-to-toml/json-to-toml.vue @@ -24,5 +24,6 @@ const rules: UseValidationRule[] = [ output-language="toml" :input-validation-rules="rules" :transformer="transformer" + download-file-name="output.toml" /> diff --git a/src/tools/json-to-yaml-converter/json-to-yaml.vue b/src/tools/json-to-yaml-converter/json-to-yaml.vue index cbaeb22d..e2aab10d 100644 --- a/src/tools/json-to-yaml-converter/json-to-yaml.vue +++ b/src/tools/json-to-yaml-converter/json-to-yaml.vue @@ -23,5 +23,6 @@ const rules: UseValidationRule[] = [ output-language="yaml" :input-validation-rules="rules" :transformer="transformer" + download-file-name="output.yaml" /> diff --git a/src/tools/list-converter/list-converter.vue b/src/tools/list-converter/list-converter.vue index 19dd30e5..3b9043d6 100644 --- a/src/tools/list-converter/list-converter.vue +++ b/src/tools/list-converter/list-converter.vue @@ -119,5 +119,6 @@ function transformer(value: string) { input-placeholder="Paste your input data here..." output-label="Your transformed data" :transformer="transformer" + download-file-name="output.txt" /> diff --git a/src/tools/toml-to-json/toml-to-json.vue b/src/tools/toml-to-json/toml-to-json.vue index 8c6dbfe2..c5fe9d43 100644 --- a/src/tools/toml-to-json/toml-to-json.vue +++ b/src/tools/toml-to-json/toml-to-json.vue @@ -22,5 +22,6 @@ const rules: UseValidationRule[] = [ output-language="json" :input-validation-rules="rules" :transformer="transformer" + download-file-name="output.json" /> diff --git a/src/tools/toml-to-yaml/toml-to-yaml.vue b/src/tools/toml-to-yaml/toml-to-yaml.vue index ec4e0158..989e9eef 100644 --- a/src/tools/toml-to-yaml/toml-to-yaml.vue +++ b/src/tools/toml-to-yaml/toml-to-yaml.vue @@ -23,5 +23,6 @@ const rules: UseValidationRule[] = [ output-language="yaml" :input-validation-rules="rules" :transformer="transformer" + download-file-name="output.yaml" /> diff --git a/src/tools/xml-formatter/xml-formatter.vue b/src/tools/xml-formatter/xml-formatter.vue index d59cf8c7..968faa2f 100644 --- a/src/tools/xml-formatter/xml-formatter.vue +++ b/src/tools/xml-formatter/xml-formatter.vue @@ -42,5 +42,6 @@ const rules: UseValidationRule[] = [ :input-validation-rules="rules" :transformer="transformer" :input-default="defaultValue" + download-file-name="output.xml" /> diff --git a/src/tools/yaml-to-json-converter/yaml-to-json.vue b/src/tools/yaml-to-json-converter/yaml-to-json.vue index 39c9297f..b2043465 100644 --- a/src/tools/yaml-to-json-converter/yaml-to-json.vue +++ b/src/tools/yaml-to-json-converter/yaml-to-json.vue @@ -27,5 +27,6 @@ const rules: UseValidationRule[] = [ output-language="json" :input-validation-rules="rules" :transformer="transformer" + download-file-name="output.json" /> diff --git a/src/tools/yaml-to-toml/yaml-to-toml.vue b/src/tools/yaml-to-toml/yaml-to-toml.vue index cad72e6b..37f22def 100644 --- a/src/tools/yaml-to-toml/yaml-to-toml.vue +++ b/src/tools/yaml-to-toml/yaml-to-toml.vue @@ -24,5 +24,6 @@ const rules: UseValidationRule[] = [ output-language="toml" :input-validation-rules="rules" :transformer="transformer" + download-file-name="output.toml" />