From 46420cbc7ea96829525b1fea21137c09266b8b28 Mon Sep 17 00:00:00 2001 From: Jae Woong Chung <33976823+woong-jae@users.noreply.github.com> Date: Sun, 18 Feb 2024 17:01:31 +0900 Subject: [PATCH] v1.1.0 (#9) --- README.md | 2 +- bin/version/index.js | 23 ++ bin/version/update.js | 36 +++ package.json | 5 +- pnpm-lock.yaml | 211 ++++++++++++++++++ src/assets/manifest.json | 4 +- src/base/components/Toaster/toast.tsx | 72 +++--- src/base/components/Toaster/toaster.tsx | 10 +- src/base/components/Toaster/use-toast.ts | 150 ++++++------- .../solution/content/getFileExtension.ts | 5 +- .../platforms/boj/parseBojLanguage.ts | 10 +- .../solution/platforms/leetcode/index.ts | 3 +- .../index.ts | 25 +-- src/features/solution/programming-language.ts | 18 ++ src/features/solution/types.ts | 11 +- 15 files changed, 432 insertions(+), 153 deletions(-) create mode 100644 bin/version/index.js create mode 100644 bin/version/update.js create mode 100644 src/features/solution/programming-language.ts diff --git a/README.md b/README.md index 484a3bd..7565195 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Code-Vault에서 제공하는 크롬 확장도구입니다. ### 언어 -c, cpp, java, javascript, typescript, python, kotlin +c, cpp, java, javascript, typescript, python, kotlin, go, rust ## 사용법 diff --git a/bin/version/index.js b/bin/version/index.js new file mode 100644 index 0000000..23d24b7 --- /dev/null +++ b/bin/version/index.js @@ -0,0 +1,23 @@ +import { select } from '@inquirer/prompts'; +import { updateManifestVersion } from './update.js'; + +const manifestPath = 'src/assets/manifest.json'; +const target = await select({ + message: 'Select a target', + choices: [ + { + name: 'major', + value: 'major', + }, + { + name: 'minor', + value: 'minor', + }, + { + name: 'patch', + value: 'patch', + }, + ], +}); + +updateManifestVersion(manifestPath, target); diff --git a/bin/version/update.js b/bin/version/update.js new file mode 100644 index 0000000..9cd7255 --- /dev/null +++ b/bin/version/update.js @@ -0,0 +1,36 @@ +import fs from 'node:fs'; + +export async function updateManifestVersion(manifestPath, target) { + const { default: manifest } = await import(`../../${manifestPath}`, { + assert: { + type: 'json', + }, + }); + + const updatedVersion = getUpdatedVersion(manifest.version, target); + + manifest.version = updatedVersion; + manifest.version_name = updatedVersion; + + fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2)); +} + +function getUpdatedVersion(version, target) { + const [major, minor, patch] = version + .split('.') + .map((version) => parseInt(version)); + + if (target === 'major') { + return [major + 1, 0, 0].join('.'); + } + + if (target === 'minor') { + return [major, minor + 1, 0].join('.'); + } + + if (target === 'patch') { + return [major, minor, patch + 1].join('.'); + } + + return [major, minor, patch].join('.'); +} diff --git a/package.json b/package.json index 788d525..7a3aa15 100644 --- a/package.json +++ b/package.json @@ -16,9 +16,11 @@ "start": "webpack serve", "build": "webpack", "build:production": "NODE_ENV=production webpack && zip -r dist.zip dist", - "test": "pnpm jest --watch" + "test": "pnpm jest --watch", + "update:version": "node bin/version" }, "devDependencies": { + "@inquirer/prompts": "^4.0.0", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11", "@swc/cli": "^0.1.62", "@swc/core": "^1.3.86", @@ -75,6 +77,7 @@ "react-dom": "^18.2.0", "tailwind-merge": "^1.14.0", "tailwindcss-animate": "^1.0.7", + "zod": "^3.22.4", "zustand": "^4.4.1" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 05d2171..dab126d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -59,11 +59,17 @@ dependencies: tailwindcss-animate: specifier: ^1.0.7 version: 1.0.7(tailwindcss@3.3.3) + zod: + specifier: ^3.22.4 + version: 3.22.4 zustand: specifier: ^4.4.1 version: 4.4.1(@types/react@18.2.21)(react@18.2.0) devDependencies: + '@inquirer/prompts': + specifier: ^4.0.0 + version: 4.0.0 '@pmmmwh/react-refresh-webpack-plugin': specifier: ^0.5.11 version: 0.5.11(react-refresh@0.14.0)(webpack-dev-server@4.15.1)(webpack@5.88.2) @@ -620,6 +626,121 @@ packages: resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} dev: true + /@inquirer/checkbox@2.0.0: + resolution: {integrity: sha512-z+MRAXQaZCe5jzqwW488jFrRsT8Rbwr3e0wNkbPBhLr5oVeWcSowFidLcCHca+gcLJMHEVMYqAE7J90UtoWyIw==} + engines: {node: '>=18'} + dependencies: + '@inquirer/core': 7.0.0 + '@inquirer/type': 1.2.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + figures: 3.2.0 + dev: true + + /@inquirer/confirm@3.0.0: + resolution: {integrity: sha512-LHeuYP1D8NmQra1eR4UqvZMXwxEdDXyElJmmZfU44xdNLL6+GcQBS0uE16vyfZVjH8c22p9e+DStROfE/hyHrg==} + engines: {node: '>=18'} + dependencies: + '@inquirer/core': 7.0.0 + '@inquirer/type': 1.2.0 + dev: true + + /@inquirer/core@7.0.0: + resolution: {integrity: sha512-g13W5yEt9r1sEVVriffJqQ8GWy94OnfxLCreNSOTw0HPVcszmc/If1KIf7YBmlwtX4klmvwpZHnQpl3N7VX2xA==} + engines: {node: '>=18'} + dependencies: + '@inquirer/type': 1.2.0 + '@types/mute-stream': 0.0.4 + '@types/node': 20.11.19 + '@types/wrap-ansi': 3.0.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-spinners: 2.9.2 + cli-width: 4.1.0 + figures: 3.2.0 + mute-stream: 1.0.0 + run-async: 3.0.0 + signal-exit: 4.1.0 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + dev: true + + /@inquirer/editor@2.0.0: + resolution: {integrity: sha512-0n3agxb1X23A/lx+MI5sV6s/qeywGr4xmKAzZS7ZhToee7L/6DXotWa/VvvwNEoBT0mSuk9SDIAoQ0zLkJmpHg==} + engines: {node: '>=18'} + dependencies: + '@inquirer/core': 7.0.0 + '@inquirer/type': 1.2.0 + external-editor: 3.1.0 + dev: true + + /@inquirer/expand@2.0.0: + resolution: {integrity: sha512-2VETEz+RyRrIeBwULKc5o+PJzKqbsibyT6IY0oP0XvM/17flO6eW7P+rdGCAvFP6g2hKieIH23ZVrcgsosb1/g==} + engines: {node: '>=18'} + dependencies: + '@inquirer/core': 7.0.0 + '@inquirer/type': 1.2.0 + chalk: 4.1.2 + figures: 3.2.0 + dev: true + + /@inquirer/input@2.0.0: + resolution: {integrity: sha512-qOjxSHLzqp/u6TvK7UtidPERoCa6BSSKyKG17aEaSOBl9uAQ4XIIqs9TtcEqwDloakarWS6xxTfR0sE1qvLwIQ==} + engines: {node: '>=18'} + dependencies: + '@inquirer/core': 7.0.0 + '@inquirer/type': 1.2.0 + dev: true + + /@inquirer/password@2.0.0: + resolution: {integrity: sha512-PlUek3wTMiGZothmmGIL4OBLo+rDSCxqIUHsyroyM/+AnR3xr5NHMM0/5z6CuptpJs1ZbQewqslaNi7k6goWMw==} + engines: {node: '>=18'} + dependencies: + '@inquirer/core': 7.0.0 + '@inquirer/type': 1.2.0 + ansi-escapes: 4.3.2 + dev: true + + /@inquirer/prompts@4.0.0: + resolution: {integrity: sha512-IS4FMj4k+3gxlvKCL1ibSEc3mMnpZ4eFeKlv0ZOo0AMWFxLSvCUT3xOw72N+UTeARK7cbwXxpt7KHcZ5q/RV0Q==} + engines: {node: '>=18'} + dependencies: + '@inquirer/checkbox': 2.0.0 + '@inquirer/confirm': 3.0.0 + '@inquirer/core': 7.0.0 + '@inquirer/editor': 2.0.0 + '@inquirer/expand': 2.0.0 + '@inquirer/input': 2.0.0 + '@inquirer/password': 2.0.0 + '@inquirer/rawlist': 2.0.0 + '@inquirer/select': 2.0.0 + dev: true + + /@inquirer/rawlist@2.0.0: + resolution: {integrity: sha512-o4jHJBAvknVE6K15zX8AuLMemb1eN1EL0l+BIbJ2JgtpoU2zSuLf6jT98omvtWk/gbaowjw7RLsW7X5F+G19KA==} + engines: {node: '>=18'} + dependencies: + '@inquirer/core': 7.0.0 + '@inquirer/type': 1.2.0 + chalk: 4.1.2 + dev: true + + /@inquirer/select@2.0.0: + resolution: {integrity: sha512-ZxWP1gHbReAH6HdoNQRV/9W/UjgKSeiiQX2DxJ6w3GDiQeC3fRAL+lukuMM+QGteGqaTjWwIEWhPLvgbGIrRgg==} + engines: {node: '>=18'} + dependencies: + '@inquirer/core': 7.0.0 + '@inquirer/type': 1.2.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + figures: 3.2.0 + dev: true + + /@inquirer/type@1.2.0: + resolution: {integrity: sha512-/vvkUkYhrjbm+RolU7V1aUFDydZVKNKqKHR5TsE+j5DXgXFwrsOPcoGUJ02K0O7q7O53CU2DOTMYCHeGZ25WHA==} + engines: {node: '>=18'} + dev: true + /@istanbuljs/load-nyc-config@1.1.0: resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} engines: {node: '>=8'} @@ -2285,6 +2406,18 @@ packages: resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==} dev: true + /@types/mute-stream@0.0.4: + resolution: {integrity: sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==} + dependencies: + '@types/node': 20.11.19 + dev: true + + /@types/node@20.11.19: + resolution: {integrity: sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==} + dependencies: + undici-types: 5.26.5 + dev: true + /@types/node@20.4.5: resolution: {integrity: sha512-rt40Nk13II9JwQBdeYqmbn2Q6IVTA5uPhvSO+JVqdXw/6/4glI6oR9ezty/A9Hg5u7JH4OmYmuQ+XvjKm0Datg==} @@ -2359,6 +2492,10 @@ packages: resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} dev: true + /@types/wrap-ansi@3.0.0: + resolution: {integrity: sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==} + dev: true + /@types/ws@8.5.5: resolution: {integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==} dependencies: @@ -3285,6 +3422,10 @@ packages: engines: {node: '>=10'} dev: true + /chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + dev: true + /chokidar@3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} @@ -3334,6 +3475,16 @@ packages: webpack: 5.88.2(@swc/core@1.3.86)(webpack-cli@5.1.4) dev: true + /cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + dev: true + + /cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + dev: true + /cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -4337,6 +4488,15 @@ packages: sort-keys-length: 1.0.1 dev: true + /external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + dev: true + /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: true @@ -4386,6 +4546,13 @@ packages: bser: 2.1.1 dev: true + /figures@3.2.0: + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} + dependencies: + escape-string-regexp: 1.0.5 + dev: true + /file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -6045,6 +6212,11 @@ packages: thunky: 1.1.0 dev: true + /mute-stream@1.0.0: + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true + /mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} dependencies: @@ -6261,6 +6433,11 @@ packages: arch: 2.2.0 dev: true + /os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + dev: true + /p-cancelable@2.1.1: resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} engines: {node: '>=8'} @@ -6954,6 +7131,11 @@ packages: glob: 7.2.3 dev: true + /run-async@3.0.0: + resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} + engines: {node: '>=0.12.0'} + dev: true + /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: @@ -7176,6 +7358,11 @@ packages: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: true + /signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + dev: true + /sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} dev: true @@ -7580,6 +7767,13 @@ packages: resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==} dev: true + /tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + dependencies: + os-tmpdir: 1.0.2 + dev: true + /tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} dev: true @@ -7775,6 +7969,10 @@ packages: which-boxed-primitive: 1.0.2 dev: true + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: true + /universal-github-app-jwt@1.1.1: resolution: {integrity: sha512-G33RTLrIBMFmlDV4u4CBF7dh71eWwykck4XgaxaIVeZKOYZRAAxvcGMRFTUclVY6xoUPQvO4Ne5wKGxYm/Yy9w==} dependencies: @@ -8129,6 +8327,15 @@ packages: resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==} dev: true + /wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -8205,6 +8412,10 @@ packages: engines: {node: '>=10'} dev: true + /zod@3.22.4: + resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} + dev: false + /zustand@4.4.1(@types/react@18.2.21)(react@18.2.0): resolution: {integrity: sha512-QCPfstAS4EBiTQzlaGP1gmorkh/UL1Leaj2tdj+zZCZ/9bm0WS7sI2wnfD5lpOszFqWJ1DcPnGoY8RDL61uokw==} engines: {node: '>=12.7.0'} diff --git a/src/assets/manifest.json b/src/assets/manifest.json index c46b255..d44efb6 100644 --- a/src/assets/manifest.json +++ b/src/assets/manifest.json @@ -2,8 +2,8 @@ "manifest_version": 3, "name": "Code-Vault", "description": "Code-Vault에서 제공하는 크롬 확장도구입니다.", - "version": "1.0.2", - "version_name": "1.0.2", + "version": "1.1.0", + "version_name": "1.1.0", "author": "jwchung0828@gmail.com", "homepage_url": "https://github.com/woong-jae/code-vault", "icons": { diff --git a/src/base/components/Toaster/toast.tsx b/src/base/components/Toaster/toast.tsx index 455d04e..4310808 100644 --- a/src/base/components/Toaster/toast.tsx +++ b/src/base/components/Toaster/toast.tsx @@ -1,11 +1,11 @@ -import * as React from "react" -import { Cross2Icon } from "@radix-ui/react-icons" -import * as ToastPrimitives from "@radix-ui/react-toast" -import { cva, type VariantProps } from "class-variance-authority" +import * as React from 'react'; +import { Cross2Icon } from '@radix-ui/react-icons'; +import * as ToastPrimitives from '@radix-ui/react-toast'; +import { cva, type VariantProps } from 'class-variance-authority'; -import { cn } from "~/base/components/utils" +import { cn } from '~/base/components/utils'; -const ToastProvider = ToastPrimitives.Provider +const ToastProvider = ToastPrimitives.Provider; const ToastViewport = React.forwardRef< React.ElementRef, @@ -14,29 +14,29 @@ const ToastViewport = React.forwardRef< -)) -ToastViewport.displayName = ToastPrimitives.Viewport.displayName +)); +ToastViewport.displayName = ToastPrimitives.Viewport.displayName; const toastVariants = cva( - "group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full", + 'group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full', { variants: { variant: { - default: "border bg-background text-foreground", + default: 'border bg-background text-foreground', destructive: - "destructive group border-destructive bg-destructive text-destructive-foreground", + 'destructive group border-destructive bg-destructive text-destructive-foreground', }, }, defaultVariants: { - variant: "default", + variant: 'default', }, - } -) + }, +); const Toast = React.forwardRef< React.ElementRef, @@ -49,9 +49,9 @@ const Toast = React.forwardRef< className={cn(toastVariants({ variant }), className)} {...props} /> - ) -}) -Toast.displayName = ToastPrimitives.Root.displayName + ); +}); +Toast.displayName = ToastPrimitives.Root.displayName; const ToastAction = React.forwardRef< React.ElementRef, @@ -60,13 +60,13 @@ const ToastAction = React.forwardRef< -)) -ToastAction.displayName = ToastPrimitives.Action.displayName +)); +ToastAction.displayName = ToastPrimitives.Action.displayName; const ToastClose = React.forwardRef< React.ElementRef, @@ -75,16 +75,16 @@ const ToastClose = React.forwardRef< -)) -ToastClose.displayName = ToastPrimitives.Close.displayName +)); +ToastClose.displayName = ToastPrimitives.Close.displayName; const ToastTitle = React.forwardRef< React.ElementRef, @@ -92,11 +92,11 @@ const ToastTitle = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -ToastTitle.displayName = ToastPrimitives.Title.displayName +)); +ToastTitle.displayName = ToastPrimitives.Title.displayName; const ToastDescription = React.forwardRef< React.ElementRef, @@ -104,15 +104,15 @@ const ToastDescription = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -ToastDescription.displayName = ToastPrimitives.Description.displayName +)); +ToastDescription.displayName = ToastPrimitives.Description.displayName; -type ToastProps = React.ComponentPropsWithoutRef +type ToastProps = React.ComponentPropsWithoutRef; -type ToastActionElement = React.ReactElement +type ToastActionElement = React.ReactElement; export { type ToastProps, @@ -124,4 +124,4 @@ export { ToastDescription, ToastClose, ToastAction, -} +}; diff --git a/src/base/components/Toaster/toaster.tsx b/src/base/components/Toaster/toaster.tsx index 3702bd8..b442a8f 100644 --- a/src/base/components/Toaster/toaster.tsx +++ b/src/base/components/Toaster/toaster.tsx @@ -5,11 +5,11 @@ import { ToastProvider, ToastTitle, ToastViewport, -} from "@base/components/Toaster/toast" -import { useToast } from "@base/components/Toaster/use-toast" +} from '@base/components/Toaster/toast'; +import { useToast } from '@base/components/Toaster/use-toast'; export function Toaster() { - const { toasts } = useToast() + const { toasts } = useToast(); return ( @@ -25,9 +25,9 @@ export function Toaster() { {action} - ) + ); })} - ) + ); } diff --git a/src/base/components/Toaster/use-toast.ts b/src/base/components/Toaster/use-toast.ts index 7a4c988..e498189 100644 --- a/src/base/components/Toaster/use-toast.ts +++ b/src/base/components/Toaster/use-toast.ts @@ -1,104 +1,104 @@ // Inspired by react-hot-toast library -import * as React from "react" +import * as React from 'react'; import type { ToastActionElement, ToastProps, -} from "@base/components/Toaster/toast" +} from '@base/components/Toaster/toast'; -const TOAST_LIMIT = 1 -const TOAST_REMOVE_DELAY = 1000000 +const TOAST_LIMIT = 1; +const TOAST_REMOVE_DELAY = 1000000; type ToasterToast = ToastProps & { - id: string - title?: React.ReactNode - description?: React.ReactNode - action?: ToastActionElement -} + id: string; + title?: React.ReactNode; + description?: React.ReactNode; + action?: ToastActionElement; +}; const actionTypes = { - ADD_TOAST: "ADD_TOAST", - UPDATE_TOAST: "UPDATE_TOAST", - DISMISS_TOAST: "DISMISS_TOAST", - REMOVE_TOAST: "REMOVE_TOAST", -} as const + ADD_TOAST: 'ADD_TOAST', + UPDATE_TOAST: 'UPDATE_TOAST', + DISMISS_TOAST: 'DISMISS_TOAST', + REMOVE_TOAST: 'REMOVE_TOAST', +} as const; -let count = 0 +let count = 0; function genId() { - count = (count + 1) % Number.MAX_SAFE_INTEGER - return count.toString() + count = (count + 1) % Number.MAX_SAFE_INTEGER; + return count.toString(); } -type ActionType = typeof actionTypes +type ActionType = typeof actionTypes; type Action = | { - type: ActionType["ADD_TOAST"] - toast: ToasterToast + type: ActionType['ADD_TOAST']; + toast: ToasterToast; } | { - type: ActionType["UPDATE_TOAST"] - toast: Partial + type: ActionType['UPDATE_TOAST']; + toast: Partial; } | { - type: ActionType["DISMISS_TOAST"] - toastId?: ToasterToast["id"] + type: ActionType['DISMISS_TOAST']; + toastId?: ToasterToast['id']; } | { - type: ActionType["REMOVE_TOAST"] - toastId?: ToasterToast["id"] - } + type: ActionType['REMOVE_TOAST']; + toastId?: ToasterToast['id']; + }; interface State { - toasts: ToasterToast[] + toasts: ToasterToast[]; } -const toastTimeouts = new Map>() +const toastTimeouts = new Map>(); const addToRemoveQueue = (toastId: string) => { if (toastTimeouts.has(toastId)) { - return + return; } const timeout = setTimeout(() => { - toastTimeouts.delete(toastId) + toastTimeouts.delete(toastId); dispatch({ - type: "REMOVE_TOAST", + type: 'REMOVE_TOAST', toastId: toastId, - }) - }, TOAST_REMOVE_DELAY) + }); + }, TOAST_REMOVE_DELAY); - toastTimeouts.set(toastId, timeout) -} + toastTimeouts.set(toastId, timeout); +}; export const reducer = (state: State, action: Action): State => { switch (action.type) { - case "ADD_TOAST": + case 'ADD_TOAST': return { ...state, toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT), - } + }; - case "UPDATE_TOAST": + case 'UPDATE_TOAST': return { ...state, toasts: state.toasts.map((t) => - t.id === action.toast.id ? { ...t, ...action.toast } : t + t.id === action.toast.id ? { ...t, ...action.toast } : t, ), - } + }; - case "DISMISS_TOAST": { - const { toastId } = action + case 'DISMISS_TOAST': { + const { toastId } = action; // ! Side effects ! - This could be extracted into a dismissToast() action, // but I'll keep it here for simplicity if (toastId) { - addToRemoveQueue(toastId) + addToRemoveQueue(toastId); } else { state.toasts.forEach((toast) => { - addToRemoveQueue(toast.id) - }) + addToRemoveQueue(toast.id); + }); } return { @@ -109,84 +109,84 @@ export const reducer = (state: State, action: Action): State => { ...t, open: false, } - : t + : t, ), - } + }; } - case "REMOVE_TOAST": + case 'REMOVE_TOAST': if (action.toastId === undefined) { return { ...state, toasts: [], - } + }; } return { ...state, toasts: state.toasts.filter((t) => t.id !== action.toastId), - } + }; } -} +}; -const listeners: Array<(state: State) => void> = [] +const listeners: Array<(state: State) => void> = []; -let memoryState: State = { toasts: [] } +let memoryState: State = { toasts: [] }; function dispatch(action: Action) { - memoryState = reducer(memoryState, action) + memoryState = reducer(memoryState, action); listeners.forEach((listener) => { - listener(memoryState) - }) + listener(memoryState); + }); } -type Toast = Omit +type Toast = Omit; function toast({ ...props }: Toast) { - const id = genId() + const id = genId(); const update = (props: ToasterToast) => dispatch({ - type: "UPDATE_TOAST", + type: 'UPDATE_TOAST', toast: { ...props, id }, - }) - const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id }) + }); + const dismiss = () => dispatch({ type: 'DISMISS_TOAST', toastId: id }); dispatch({ - type: "ADD_TOAST", + type: 'ADD_TOAST', toast: { ...props, id, open: true, onOpenChange: (open) => { - if (!open) dismiss() + if (!open) dismiss(); }, }, - }) + }); return { id: id, dismiss, update, - } + }; } function useToast() { - const [state, setState] = React.useState(memoryState) + const [state, setState] = React.useState(memoryState); React.useEffect(() => { - listeners.push(setState) + listeners.push(setState); return () => { - const index = listeners.indexOf(setState) + const index = listeners.indexOf(setState); if (index > -1) { - listeners.splice(index, 1) + listeners.splice(index, 1); } - } - }, [state]) + }; + }, [state]); return { ...state, toast, - dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }), - } + dismiss: (toastId?: string) => dispatch({ type: 'DISMISS_TOAST', toastId }), + }; } -export { useToast, toast } +export { useToast, toast }; diff --git a/src/features/solution/content/getFileExtension.ts b/src/features/solution/content/getFileExtension.ts index 4f4b9fb..6ede8c0 100644 --- a/src/features/solution/content/getFileExtension.ts +++ b/src/features/solution/content/getFileExtension.ts @@ -1,4 +1,4 @@ -import type { ProgrammingLanguage } from '../types'; +import type { ProgrammingLanguage } from '../programming-language'; export type GetFileExtension = ( programmingLanguage: ProgrammingLanguage, @@ -9,6 +9,7 @@ export const getFileExtension: GetFileExtension = (programmingLanguage) => { case 'c': case 'cpp': case 'java': + case 'go': return programmingLanguage; case 'python': return 'py'; @@ -18,6 +19,8 @@ export const getFileExtension: GetFileExtension = (programmingLanguage) => { return 'js'; case 'typescript': return 'ts'; + case 'rust': + return 'rs'; default: return 'txt'; } diff --git a/src/features/solution/platforms/boj/parseBojLanguage.ts b/src/features/solution/platforms/boj/parseBojLanguage.ts index baf57e9..cbd8a0a 100644 --- a/src/features/solution/platforms/boj/parseBojLanguage.ts +++ b/src/features/solution/platforms/boj/parseBojLanguage.ts @@ -1,4 +1,4 @@ -import type { ProgrammingLanguage } from '../../types'; +import type { ProgrammingLanguage } from '../../programming-language'; const parseBojLanguage = (langNum: string): ProgrammingLanguage => { switch (langNum) { @@ -17,14 +17,14 @@ const parseBojLanguage = (langNum: string): ProgrammingLanguage => { return 'java'; case '69': return 'kotlin'; - // case '12': - // return 'go'; + case '12': + return 'go'; // case '68': // return 'ruby'; // case '74': // return 'swift'; - // case '94': - // return 'rust'; + case '94': + return 'rust'; default: return 'unknown'; } diff --git a/src/features/solution/platforms/leetcode/index.ts b/src/features/solution/platforms/leetcode/index.ts index b78ff99..56798ea 100644 --- a/src/features/solution/platforms/leetcode/index.ts +++ b/src/features/solution/platforms/leetcode/index.ts @@ -5,6 +5,7 @@ import { Process, type SolutionStatus, } from '~/features/solution/solution-tracker/types'; +import { ProgrammingLanguageEnum } from '../../programming-language'; import LeetcodePacketToSolutionStatusMapper from './LeetcodePacketToSolutionStatusMapper'; import LeetcodeSolutionInterceptor from './LeetcodeSolutionInterceptor'; @@ -37,7 +38,7 @@ export const initLeetcodeSolutionCatcherFromBackground: CreateSolutionCatcher = payload: { link: details.url.split('/').slice(0, -2).join('/'), platform: 'leetcode', - language: lang, + language: ProgrammingLanguageEnum.parse(lang), problemId: question_id, code: typed_code, }, diff --git a/src/features/solution/platforms/programmers/ProgrammersPacketToSolutionStatusMapper/index.ts b/src/features/solution/platforms/programmers/ProgrammersPacketToSolutionStatusMapper/index.ts index 69188a9..b483e80 100644 --- a/src/features/solution/platforms/programmers/ProgrammersPacketToSolutionStatusMapper/index.ts +++ b/src/features/solution/platforms/programmers/ProgrammersPacketToSolutionStatusMapper/index.ts @@ -1,5 +1,9 @@ +import { + ProgrammingLanguageEnum, + type ProgrammingLanguage, +} from '~/features/solution/programming-language'; import { Process } from '~/features/solution/solution-tracker/types'; -import type { ProgrammingLanguage, Solution } from '../../../types'; +import type { Solution } from '../../../types'; import { type ProgrammersCode, type ProgrammersIdentifier, @@ -90,22 +94,9 @@ export default class ProgrammersPacketToSolutionStatusMapper } private parseLanguage(language: string): ProgrammingLanguage { - switch (language) { - case 'c': - return 'c'; - case 'cpp': - return 'cpp'; - case 'java': - return 'java'; - case 'kotlin': - return 'kotlin'; - case 'javascript': - return 'javascript'; - case 'python': - case 'python3': - return 'python'; - default: - return 'unknown'; + if (language.includes('python')) { + return 'python'; } + return ProgrammingLanguageEnum.parse(language); } } diff --git a/src/features/solution/programming-language.ts b/src/features/solution/programming-language.ts new file mode 100644 index 0000000..5075f60 --- /dev/null +++ b/src/features/solution/programming-language.ts @@ -0,0 +1,18 @@ +import { z } from 'zod'; + +export const ProgrammingLanguageEnum = z + .enum([ + 'javascript', + 'typescript', + 'java', + 'kotlin', + 'c', + 'cpp', + 'python', + 'go', + 'rust', + 'unknown', + ]) + .default('unknown'); + +export type ProgrammingLanguage = z.infer; diff --git a/src/features/solution/types.ts b/src/features/solution/types.ts index 98cc063..9445557 100644 --- a/src/features/solution/types.ts +++ b/src/features/solution/types.ts @@ -1,18 +1,11 @@ +import type { ProgrammingLanguage } from './programming-language'; + /* Solution */ type MilliSecond = number; type MegaByte = number; type Url = string; export type Platform = 'programmers' | 'boj' | 'leetcode'; -export type ProgrammingLanguage = - | 'javascript' - | 'typescript' - | 'java' - | 'kotlin' - | 'c' - | 'cpp' - | 'python' - | 'unknown'; export type Solution = { problemId: string; platform: Platform;