├── src ├── vite-env.d.ts ├── main.tsx ├── utils.ts ├── App.css ├── hooks.ts ├── natives.ts ├── assets │ └── react.svg └── App.tsx ├── src-tauri ├── build.rs ├── icons │ ├── 32x32.png │ ├── 64x64.png │ ├── icon.icns │ ├── icon.ico │ ├── icon.png │ ├── 128x128.png │ ├── 128x128@2x.png │ ├── StoreLogo.png │ ├── Square30x30Logo.png │ ├── Square44x44Logo.png │ ├── Square71x71Logo.png │ ├── Square89x89Logo.png │ ├── Square107x107Logo.png │ ├── Square142x142Logo.png │ ├── Square150x150Logo.png │ ├── Square284x284Logo.png │ └── Square310x310Logo.png ├── .gitignore ├── src │ ├── main.rs │ └── lib.rs ├── capabilities │ └── default.json ├── tauri.conf.json └── Cargo.toml ├── app-icon.png ├── screenshot.png ├── tsconfig.node.json ├── .gitignore ├── index.html ├── tsconfig.json ├── README.md ├── vite.config.ts ├── package.json ├── LICENSE └── public ├── vite.svg └── tauri.svg /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src-tauri/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tauri_build::build() 3 | } 4 | -------------------------------------------------------------------------------- /app-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiql/get-unique-id-app/HEAD/app-icon.png -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiql/get-unique-id-app/HEAD/screenshot.png -------------------------------------------------------------------------------- /src-tauri/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiql/get-unique-id-app/HEAD/src-tauri/icons/32x32.png -------------------------------------------------------------------------------- /src-tauri/icons/64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiql/get-unique-id-app/HEAD/src-tauri/icons/64x64.png -------------------------------------------------------------------------------- /src-tauri/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiql/get-unique-id-app/HEAD/src-tauri/icons/icon.icns -------------------------------------------------------------------------------- /src-tauri/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiql/get-unique-id-app/HEAD/src-tauri/icons/icon.ico -------------------------------------------------------------------------------- /src-tauri/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiql/get-unique-id-app/HEAD/src-tauri/icons/icon.png -------------------------------------------------------------------------------- /src-tauri/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiql/get-unique-id-app/HEAD/src-tauri/icons/128x128.png -------------------------------------------------------------------------------- /src-tauri/icons/128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiql/get-unique-id-app/HEAD/src-tauri/icons/128x128@2x.png -------------------------------------------------------------------------------- /src-tauri/icons/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiql/get-unique-id-app/HEAD/src-tauri/icons/StoreLogo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square30x30Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiql/get-unique-id-app/HEAD/src-tauri/icons/Square30x30Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square44x44Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiql/get-unique-id-app/HEAD/src-tauri/icons/Square44x44Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square71x71Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiql/get-unique-id-app/HEAD/src-tauri/icons/Square71x71Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square89x89Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiql/get-unique-id-app/HEAD/src-tauri/icons/Square89x89Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square107x107Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiql/get-unique-id-app/HEAD/src-tauri/icons/Square107x107Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square142x142Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiql/get-unique-id-app/HEAD/src-tauri/icons/Square142x142Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square150x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiql/get-unique-id-app/HEAD/src-tauri/icons/Square150x150Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square284x284Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiql/get-unique-id-app/HEAD/src-tauri/icons/Square284x284Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square310x310Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiql/get-unique-id-app/HEAD/src-tauri/icons/Square310x310Logo.png -------------------------------------------------------------------------------- /src-tauri/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Generated by Tauri 6 | # will have schema files for capabilities auto-completion 7 | /gen/schemas 8 | -------------------------------------------------------------------------------- /src-tauri/src/main.rs: -------------------------------------------------------------------------------- 1 | // Prevents additional console window on Windows in release, DO NOT REMOVE!! 2 | #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] 3 | 4 | fn main() { 5 | get_unique_id_app_lib::run() 6 | } 7 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App"; 4 | 5 | ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( 6 | 7 | 8 | , 9 | ); 10 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | export function sleep(millis: number) { 2 | return new Promise((resolve) => setTimeout(resolve, millis)); 3 | } 4 | 5 | export function isDigit(char: string): boolean { 6 | return /^\d$/.test(char); 7 | } 8 | 9 | export function isLetter(char: string): boolean { 10 | return /^[A-Za-z]$/.test(char); 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Get Unique ID 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 0; 3 | margin: 0; 4 | overflow: hidden; 5 | 6 | cursor: default; 7 | -webkit-user-select: none; /* Chrome, Safari, Opera */ 8 | -moz-user-select: none; /* Firefox */ 9 | -ms-user-select: none; /* Internet Explorer/Edge */ 10 | user-select: none; /* Standard syntax */ 11 | } 12 | 13 | :root { 14 | font-synthesis: none; 15 | text-rendering: optimizeLegibility; 16 | -webkit-font-smoothing: antialiased; 17 | -moz-osx-font-smoothing: grayscale; 18 | -webkit-text-size-adjust: 100%; 19 | } 20 | -------------------------------------------------------------------------------- /src-tauri/capabilities/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../gen/schemas/desktop-schema.json", 3 | "identifier": "default", 4 | "description": "Capability for the main window", 5 | "windows": ["main"], 6 | "permissions": [ 7 | "core:default", 8 | "opener:default", 9 | "core:window:allow-start-dragging", 10 | "core:window:allow-set-size", 11 | "core:window:allow-theme", 12 | "core:event:allow-listen", 13 | "clipboard-manager:default", 14 | "clipboard-manager:allow-write-text", 15 | "clipboard-manager:allow-read-text", 16 | "dialog:default" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src"], 24 | "references": [{ "path": "./tsconfig.node.json" }] 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | image 2 | 3 | # Get Unique ID 4 | 5 | Generates unique IDs(UUID/CUID/ULID/NanoID/ObjectID/Snowflake, etc) for you to use in debugging, development, or anywhere else you may need a unique ID. 6 | 7 | ![alt text](screenshot.png) 8 | 9 | ## Supported ID Formats 10 | - UUID v1(Gregorian Time-based) 11 | - UUID v3(MD5 Name-based) 12 | - UUID v4(Random) 13 | - UUID v5(SHA-1 Name-based) 14 | - UUID v6(Reordered Gregorian Time-based) 15 | - UUID v7(Unix Time-based) 16 | - Short UUID 17 | - Nil UUID 18 | - Max UUID 19 | - ULID 20 | - UPID 21 | - CUID 22 | - CUID2 23 | - Nano ID 24 | - NUID 25 | - TSID 26 | - SCRU128 27 | - Snowflake 28 | - Sonyflake 29 | - Object ID 30 | 31 | ## License 32 | 33 | MIT -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | 4 | // @ts-expect-error process is a nodejs global 5 | const host = process.env.TAURI_DEV_HOST; 6 | 7 | // https://vitejs.dev/config/ 8 | export default defineConfig(async () => ({ 9 | plugins: [react()], 10 | 11 | // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` 12 | // 13 | // 1. prevent vite from obscuring rust errors 14 | clearScreen: false, 15 | // 2. tauri expects a fixed port, fail if that port is not available 16 | server: { 17 | port: 1420, 18 | strictPort: true, 19 | host: host || false, 20 | hmr: host 21 | ? { 22 | protocol: "ws", 23 | host, 24 | port: 1421, 25 | } 26 | : undefined, 27 | watch: { 28 | // 3. tell vite to ignore watching `src-tauri` 29 | ignored: ["**/src-tauri/**"], 30 | }, 31 | }, 32 | })); 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "get-unique-id-app", 3 | "private": true, 4 | "version": "0.1.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview", 10 | "tauri": "tauri" 11 | }, 12 | "dependencies": { 13 | "@radix-ui/react-icons": "^1.3.2", 14 | "@radix-ui/themes": "^3.2.0", 15 | "@tauri-apps/api": "^2", 16 | "@tauri-apps/plugin-clipboard-manager": "^2", 17 | "@tauri-apps/plugin-dialog": "^2.2.0", 18 | "@tauri-apps/plugin-opener": "^2", 19 | "@tauri-apps/plugin-shell": "^2", 20 | "react": "^18.3.1", 21 | "react-dom": "^18.3.1", 22 | "react-intl": "^7.1.5", 23 | "use-resize-observer": "^9.1.0" 24 | }, 25 | "devDependencies": { 26 | "@tauri-apps/cli": "^2", 27 | "@types/react": "^18.3.1", 28 | "@types/react-dom": "^18.3.1", 29 | "@vitejs/plugin-react": "^4.3.4", 30 | "typescript": "~5.6.2", 31 | "vite": "^6.0.3" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src-tauri/tauri.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.tauri.app/config/2", 3 | "productName": "Get Unique ID", 4 | "version": "0.1.0", 5 | "identifier": "com.hiqlapps.get-unique-id-app", 6 | "build": { 7 | "beforeDevCommand": "npm run dev", 8 | "devUrl": "http://localhost:1420", 9 | "beforeBuildCommand": "npm run build", 10 | "frontendDist": "../dist" 11 | }, 12 | "app": { 13 | "windows": [ 14 | { 15 | "title": "Get Unique ID", 16 | "width": 680, 17 | "height": 400, 18 | "maximized": false, 19 | "resizable": false, 20 | "titleBarStyle": "Overlay" 21 | } 22 | ], 23 | "security": { 24 | "csp": null 25 | } 26 | }, 27 | "bundle": { 28 | "active": true, 29 | "targets": "all", 30 | "icon": [ 31 | "icons/32x32.png", 32 | "icons/128x128.png", 33 | "icons/128x128@2x.png", 34 | "icons/icon.icns", 35 | "icons/icon.ico" 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 hiql 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src-tauri/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "get-unique-id-app" 3 | version = "0.1.0" 4 | description = "Unique ID Generator" 5 | authors = ["hiql"] 6 | edition = "2021" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [lib] 11 | # The `_lib` suffix may seem redundant but it is necessary 12 | # to make the lib name unique and wouldn't conflict with the bin name. 13 | # This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519 14 | name = "get_unique_id_app_lib" 15 | crate-type = ["staticlib", "cdylib", "rlib"] 16 | 17 | [build-dependencies] 18 | tauri-build = { version = "2", features = [] } 19 | 20 | [dependencies] 21 | tauri = { version = "2", features = [] } 22 | tauri-plugin-opener = "2" 23 | tauri-plugin-shell = "2" 24 | tauri-plugin-clipboard-manager = "2" 25 | tauri-plugin-dialog = "2" 26 | serde = { version = "1", features = ["derive"] } 27 | serde_json = "1" 28 | uuid = { version = "1.12.1", features = ["v1", "v3", "v4", "v5", "v6", "v7"] } 29 | short-uuid = "0.1.4" 30 | nanoid = "0.4.0" 31 | ulid = "1.1.4" 32 | cuid = "1.3.3" 33 | uguid = "2.2.0" 34 | nuid = "0.5.0" 35 | upid = "0.2.0" 36 | tsid = "0.3.1" 37 | scru128 = "3.1.0" 38 | sonyflake = "0.3.0" 39 | rs-snowflake = "0.6.0" 40 | bson = "2.13.0" 41 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/tauri.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/hooks.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { UnlistenFn } from "@tauri-apps/api/event"; 3 | import { getCurrentWindow } from "@tauri-apps/api/window"; 4 | import { writeText } from "@tauri-apps/plugin-clipboard-manager"; 5 | 6 | export const useTheme = () => { 7 | const [theme, setTheme] = useState<"light" | "dark" | "inherit">("inherit"); 8 | 9 | useEffect(() => { 10 | let unlisten: UnlistenFn | undefined; 11 | 12 | (async () => { 13 | setTheme((await getCurrentWindow().theme()) || "inherit"); 14 | 15 | unlisten = await getCurrentWindow().onThemeChanged( 16 | ({ payload: theme }) => { 17 | console.log(`theme changed to ${theme}`); 18 | setTheme(theme); 19 | }, 20 | ); 21 | })(); 22 | 23 | return () => { 24 | if (unlisten != null) { 25 | unlisten(); 26 | } 27 | }; 28 | }, []); 29 | 30 | return theme; 31 | }; 32 | 33 | export function useCopy() { 34 | const [isCopied, setIsCopied] = useState(false); 35 | 36 | const copyToClipboard = async (text: string) => { 37 | await writeText(text); 38 | setIsCopied(true); 39 | }; 40 | 41 | const resetCopyStatus = () => { 42 | setIsCopied(false); 43 | }; 44 | 45 | useEffect(() => { 46 | if (isCopied) { 47 | const timer = setTimeout(resetCopyStatus, 3000); // Reset copy status after 3 seconds 48 | return () => clearTimeout(timer); 49 | } 50 | }, [isCopied]); 51 | 52 | return { isCopied, copyToClipboard, resetCopyStatus }; 53 | } 54 | 55 | export function useLocalStorage(key: string, initialValue: T) { 56 | // State to store our value 57 | // Pass initial state function to useState so logic is only executed once 58 | const [storedValue, setStoredValue] = useState(() => { 59 | try { 60 | // Get from local storage by key 61 | const item = window.localStorage.getItem(key); 62 | // Parse stored json or if none return initialValue 63 | return item ? JSON.parse(item) : initialValue; 64 | } catch (error) { 65 | // If error also return initialValue 66 | console.log(error); 67 | return initialValue; 68 | } 69 | }); 70 | // Return a wrapped version of useState's setter function that ... 71 | // ... persists the new value to localStorage. 72 | const setValue = (value: T | ((val: T) => T)) => { 73 | try { 74 | // Allow value to be a function so we have same API as useState 75 | const valueToStore = 76 | value instanceof Function ? value(storedValue) : value; 77 | // Save state 78 | setStoredValue(valueToStore); 79 | // Save to local storage 80 | window.localStorage.setItem(key, JSON.stringify(valueToStore)); 81 | } catch (error) { 82 | // A more advanced implementation would handle the error case 83 | console.log(error); 84 | } 85 | }; 86 | return [storedValue, setValue] as const; 87 | } 88 | -------------------------------------------------------------------------------- /src/natives.ts: -------------------------------------------------------------------------------- 1 | import { invoke } from "@tauri-apps/api/core"; 2 | 3 | const saveToFile = async ( 4 | content: string, 5 | fileName: string, 6 | ): Promise => { 7 | return await invoke("save_to_file", { content, fileName: fileName }); 8 | }; 9 | 10 | const generateUUIDV1 = async (n?: number): Promise => { 11 | return await invoke("gen_uuid_v1", { n: n || 1 }); 12 | }; 13 | 14 | const generateUUIDV3 = async ( 15 | namespace: string, 16 | name: string, 17 | n?: number, 18 | ): Promise => { 19 | return await invoke("gen_uuid_v3", { 20 | namespace, 21 | name: name || "name", 22 | n: n || 1, 23 | }); 24 | }; 25 | 26 | const generateUUIDV4 = async (n?: number): Promise => { 27 | return await invoke("gen_uuid_v4", { n: n || 1 }); 28 | }; 29 | 30 | const generateUUIDV5 = async ( 31 | namespace: string, 32 | name: string, 33 | n?: number, 34 | ): Promise => { 35 | return await invoke("gen_uuid_v5", { 36 | namespace, 37 | name: name || "name", 38 | n: n || 1, 39 | }); 40 | }; 41 | 42 | const generateUUIDV6 = async (n?: number): Promise => { 43 | return await invoke("gen_uuid_v6", { n: n || 1 }); 44 | }; 45 | 46 | const generateUUIDV7 = async (n?: number): Promise => { 47 | return await invoke("gen_uuid_v7", { n: n || 1 }); 48 | }; 49 | 50 | const generateShortUUID = async (n?: number): Promise => { 51 | return await invoke("gen_short_uuid", { n: n || 1 }); 52 | }; 53 | 54 | const generateNilUUID = async (n?: number): Promise => { 55 | return await invoke("gen_nil_uuid", { n: n || 1 }); 56 | }; 57 | 58 | const generateMaxUUID = async (n?: number): Promise => { 59 | return await invoke("gen_max_uuid", { n: n || 1 }); 60 | }; 61 | 62 | const generateNanoID = async (n?: number): Promise => { 63 | return await invoke("gen_nano_id", { n: n || 1 }); 64 | }; 65 | 66 | const generateULID = async (n?: number): Promise => { 67 | return await invoke("gen_ulid", { n: n || 1 }); 68 | }; 69 | 70 | const generateCUID = async (n?: number): Promise => { 71 | return await invoke("gen_cuid", { n: n || 1 }); 72 | }; 73 | 74 | const generateCUID2 = async (n?: number): Promise => { 75 | return await invoke("gen_cuid2", { n: n || 1 }); 76 | }; 77 | 78 | const generateSnowflake = async (n?: number): Promise => { 79 | return await invoke("gen_snowflake", { n: n || 1 }); 80 | }; 81 | 82 | const generateSonyflake = async (n?: number): Promise => { 83 | return await invoke("gen_sonyflake", { n: n || 1 }); 84 | }; 85 | 86 | const generateNUID = async (n?: number): Promise => { 87 | return await invoke("gen_nuid", { n: n || 1 }); 88 | }; 89 | 90 | const generateUPID = async (prefix: string, n?: number): Promise => { 91 | return await invoke("gen_upid", { prefix, n: n || 1 }); 92 | }; 93 | 94 | const generateTSID = async (n?: number): Promise => { 95 | return await invoke("gen_tsid", { n: n || 1 }); 96 | }; 97 | 98 | const generateObjectID = async (n?: number): Promise => { 99 | return await invoke("gen_object_id", { n: n || 1 }); 100 | }; 101 | 102 | const generateSCRU128 = async (n?: number): Promise => { 103 | return await invoke("gen_scru128", { n: n || 1 }); 104 | }; 105 | 106 | export default { 107 | saveToFile, 108 | generateUUIDV1, 109 | generateUUIDV3, 110 | generateUUIDV4, 111 | generateUUIDV5, 112 | generateUUIDV6, 113 | generateUUIDV7, 114 | generateShortUUID, 115 | generateNilUUID, 116 | generateMaxUUID, 117 | generateNanoID, 118 | generateULID, 119 | generateCUID, 120 | generateCUID2, 121 | generateNUID, 122 | generateUPID, 123 | generateTSID, 124 | generateSnowflake, 125 | generateSonyflake, 126 | generateObjectID, 127 | generateSCRU128, 128 | }; 129 | -------------------------------------------------------------------------------- /src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src-tauri/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | 3 | use nanoid::nanoid; 4 | use short_uuid::short; 5 | use snowflake::SnowflakeIdGenerator; 6 | use sonyflake::Sonyflake; 7 | use tauri::menu::{AboutMetadata, MenuBuilder, MenuItemBuilder, SubmenuBuilder}; 8 | use tauri_plugin_dialog::DialogExt; 9 | use tauri_plugin_opener::OpenerExt; 10 | use uuid::Uuid; 11 | 12 | #[tauri::command] 13 | fn gen_uuid_v1(n: u32) -> Vec { 14 | let mut list = vec![]; 15 | for _ in 0..n { 16 | let id = Uuid::now_v1(&[1, 2, 3, 4, 5, 6]).hyphenated().to_string(); 17 | list.push(id); 18 | } 19 | list 20 | } 21 | 22 | #[tauri::command] 23 | fn gen_uuid_v3(namespace: &str, name: &str, n: u32) -> Vec { 24 | if name.is_empty() { 25 | return vec![]; 26 | } 27 | let new_id = match Uuid::try_parse(namespace) { 28 | Ok(v) => Uuid::new_v3(&v, name.as_bytes()).hyphenated().to_string(), 29 | _ => "".to_string(), 30 | }; 31 | let mut list = vec![]; 32 | for _ in 0..n { 33 | let id = if new_id.is_empty() { 34 | let r = Uuid::new_v4(); 35 | Uuid::new_v3(&r, name.as_bytes()).hyphenated().to_string() 36 | } else { 37 | new_id.clone() 38 | }; 39 | list.push(id); 40 | } 41 | list 42 | } 43 | 44 | #[tauri::command] 45 | fn gen_uuid_v4(n: u32) -> Vec { 46 | let mut list = vec![]; 47 | for _ in 0..n { 48 | let id = Uuid::new_v4().hyphenated().to_string(); 49 | list.push(id); 50 | } 51 | list 52 | } 53 | 54 | #[tauri::command] 55 | fn gen_uuid_v5(namespace: &str, name: &str, n: u32) -> Vec { 56 | if name.is_empty() { 57 | return vec![]; 58 | } 59 | let new_id = match Uuid::try_parse(namespace) { 60 | Ok(v) => Uuid::new_v5(&v, name.as_bytes()).hyphenated().to_string(), 61 | _ => "".to_string(), 62 | }; 63 | let mut list = vec![]; 64 | for _ in 0..n { 65 | let id = if new_id.is_empty() { 66 | let r = Uuid::new_v4(); 67 | Uuid::new_v5(&r, name.as_bytes()).hyphenated().to_string() 68 | } else { 69 | new_id.clone() 70 | }; 71 | list.push(id); 72 | } 73 | list 74 | } 75 | 76 | #[tauri::command] 77 | fn gen_uuid_v6(n: u32) -> Vec { 78 | let mut list = vec![]; 79 | for _ in 0..n { 80 | let id = Uuid::now_v6(&[1, 2, 3, 4, 5, 6]).hyphenated().to_string(); 81 | list.push(id); 82 | } 83 | list 84 | } 85 | 86 | #[tauri::command] 87 | fn gen_uuid_v7(n: u32) -> Vec { 88 | let mut list = vec![]; 89 | for _ in 0..n { 90 | let id = Uuid::now_v7().hyphenated().to_string(); 91 | list.push(id); 92 | } 93 | list 94 | } 95 | 96 | #[tauri::command] 97 | fn gen_short_uuid(n: u32) -> Vec { 98 | let mut list = vec![]; 99 | for _ in 0..n { 100 | let shortened_uuid = short!(); 101 | let id = shortened_uuid.to_string(); 102 | list.push(id); 103 | } 104 | list 105 | } 106 | 107 | #[tauri::command] 108 | fn gen_nil_uuid(n: u32) -> Vec { 109 | let mut list = vec![]; 110 | let id = "00000000-0000-0000-0000-000000000000".to_string(); 111 | for _ in 0..n { 112 | list.push(id.clone()); 113 | } 114 | list 115 | } 116 | 117 | #[tauri::command] 118 | fn gen_max_uuid(n: u32) -> Vec { 119 | let mut list = vec![]; 120 | let id = "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF".to_string(); 121 | for _ in 0..n { 122 | list.push(id.clone()); 123 | } 124 | list 125 | } 126 | 127 | #[tauri::command] 128 | fn gen_nano_id(n: u32) -> Vec { 129 | let mut list = vec![]; 130 | for _ in 0..n { 131 | let id = nanoid!(); 132 | list.push(id); 133 | } 134 | list 135 | } 136 | 137 | #[tauri::command] 138 | fn gen_ulid(n: u32) -> Vec { 139 | let mut list = vec![]; 140 | let mut gen = ulid::Generator::new(); 141 | for _ in 0..n { 142 | let id = gen.generate().unwrap(); 143 | list.push(id.to_string()); 144 | } 145 | list 146 | } 147 | 148 | #[tauri::command] 149 | fn gen_cuid(n: u32) -> Vec { 150 | let mut list = vec![]; 151 | for _ in 0..n { 152 | let id = cuid::cuid1().unwrap(); 153 | list.push(id); 154 | } 155 | list 156 | } 157 | 158 | #[tauri::command] 159 | fn gen_cuid2(n: u32) -> Vec { 160 | let mut list = vec![]; 161 | for _ in 0..n { 162 | let id = cuid::cuid2(); 163 | list.push(id); 164 | } 165 | list 166 | } 167 | 168 | #[tauri::command] 169 | fn gen_nuid(n: u32) -> Vec { 170 | let mut list = vec![]; 171 | let mut gen = nuid::NUID::new(); 172 | for _ in 0..n { 173 | let id = gen.next().to_string(); 174 | list.push(id); 175 | } 176 | list 177 | } 178 | 179 | #[tauri::command] 180 | fn gen_snowflake(n: u32) -> Vec { 181 | let mut list = vec![]; 182 | let mut g = SnowflakeIdGenerator::new(1, 1); 183 | for _ in 0..n { 184 | let id = g.generate().to_string(); 185 | list.push(id); 186 | } 187 | list 188 | } 189 | 190 | #[tauri::command] 191 | fn gen_sonyflake(n: u32) -> Vec { 192 | let mut list = vec![]; 193 | let gen = Sonyflake::new().unwrap(); 194 | for _ in 0..n { 195 | let id = format!("{}", gen.next_id().unwrap()); 196 | list.push(id); 197 | } 198 | list 199 | } 200 | 201 | #[tauri::command] 202 | fn gen_upid(prefix: &str, n: u32) -> Vec { 203 | let mut list = vec![]; 204 | for _ in 0..n { 205 | let id = upid::Upid::new(prefix).to_string(); 206 | list.push(id); 207 | } 208 | list 209 | } 210 | 211 | #[tauri::command] 212 | fn gen_tsid(n: u32) -> Vec { 213 | let mut list = vec![]; 214 | for _ in 0..n { 215 | let id = tsid::create_tsid().to_string(); 216 | list.push(id); 217 | } 218 | list 219 | } 220 | 221 | #[tauri::command] 222 | fn gen_object_id(n: u32) -> Vec { 223 | let mut list = vec![]; 224 | for _ in 0..n { 225 | let id = bson::oid::ObjectId::new().to_string(); 226 | list.push(id) 227 | } 228 | list 229 | } 230 | 231 | #[tauri::command] 232 | fn gen_scru128(n: u32) -> Vec { 233 | let mut list = vec![]; 234 | for _ in 0..n { 235 | let id = scru128::new_string(); 236 | list.push(id); 237 | } 238 | list 239 | } 240 | 241 | #[tauri::command] 242 | async fn save_to_file(app: tauri::AppHandle, content: &str, file_name: &str) -> Result<(), String> { 243 | let file_path = app 244 | .dialog() 245 | .file() 246 | .set_file_name(file_name) 247 | .blocking_save_file(); 248 | if let Some(path) = file_path { 249 | match fs::write(path.to_string(), content) { 250 | Ok(_) => println!("File saved"), 251 | Err(e) => eprintln!("Failed to save file: {}", e), 252 | } 253 | } 254 | Ok(()) 255 | } 256 | 257 | #[cfg_attr(mobile, tauri::mobile_entry_point)] 258 | pub fn run() { 259 | tauri::Builder::default() 260 | .setup(|app| { 261 | let github = MenuItemBuilder::new("Github").id("github").build(app)?; 262 | let rfc9562 = MenuItemBuilder::new("RFC 9562").id("rfc9562").build(app)?; 263 | let app_submenu = SubmenuBuilder::new(app, "App") 264 | .about(Some(AboutMetadata { 265 | ..Default::default() 266 | })) 267 | .separator() 268 | .item(&github) 269 | .item(&rfc9562) 270 | .separator() 271 | .services() 272 | .separator() 273 | .hide() 274 | .hide_others() 275 | .quit() 276 | .build()?; 277 | let menu = MenuBuilder::new(app).items(&[&app_submenu]).build()?; 278 | app.set_menu(menu)?; 279 | app.on_menu_event(move |app, event| { 280 | if event.id() == github.id() { 281 | app.opener() 282 | .open_url("https://github.com/hiql/get-unique-id-app", None::<&str>) 283 | .unwrap(); 284 | } else if event.id() == rfc9562.id() { 285 | app.opener() 286 | .open_url("https://www.rfc-editor.org/rfc/rfc9562.html", None::<&str>) 287 | .unwrap(); 288 | } 289 | }); 290 | Ok(()) 291 | }) 292 | .plugin(tauri_plugin_clipboard_manager::init()) 293 | .plugin(tauri_plugin_shell::init()) 294 | .plugin(tauri_plugin_opener::init()) 295 | .plugin(tauri_plugin_dialog::init()) 296 | .invoke_handler(tauri::generate_handler![ 297 | save_to_file, 298 | gen_uuid_v1, 299 | gen_uuid_v3, 300 | gen_uuid_v4, 301 | gen_uuid_v5, 302 | gen_uuid_v6, 303 | gen_uuid_v7, 304 | gen_short_uuid, 305 | gen_nil_uuid, 306 | gen_max_uuid, 307 | gen_ulid, 308 | gen_upid, 309 | gen_nano_id, 310 | gen_cuid, 311 | gen_cuid2, 312 | gen_nuid, 313 | gen_tsid, 314 | gen_snowflake, 315 | gen_sonyflake, 316 | gen_object_id, 317 | gen_scru128 318 | ]) 319 | .run(tauri::generate_context!()) 320 | .expect("error while running tauri application"); 321 | } 322 | 323 | #[cfg(test)] 324 | mod test { 325 | use super::*; 326 | 327 | #[test] 328 | fn test_gen_nil_empty_uuid() { 329 | let expected = "00000000-0000-0000-0000-000000000000"; 330 | let ids = gen_nil_uuid(5); 331 | println!("{:?}", ids); 332 | assert_eq!(5, ids.len()); 333 | assert_eq!(expected, ids[0]); 334 | } 335 | } 336 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { 3 | Text, 4 | Flex, 5 | Theme, 6 | Box, 7 | Select, 8 | Button, 9 | TextField, 10 | IconButton, 11 | DropdownMenu, 12 | Separator, 13 | Tooltip, 14 | Badge, 15 | Switch, 16 | RadioGroup, 17 | ScrollArea, 18 | } from "@radix-ui/themes"; 19 | import { getCurrentWindow, LogicalSize } from "@tauri-apps/api/window"; 20 | import useResizeObserver from "use-resize-observer"; 21 | import { FormattedMessage, IntlProvider, useIntl } from "react-intl"; 22 | import { 23 | CopyIcon, 24 | DownloadIcon, 25 | MinusIcon, 26 | PlusIcon, 27 | QuestionMarkCircledIcon, 28 | ShuffleIcon, 29 | UpdateIcon, 30 | } from "@radix-ui/react-icons"; 31 | import "@radix-ui/themes/styles.css"; 32 | import "./App.css"; 33 | import { useCopy, useLocalStorage, useTheme } from "./hooks"; 34 | import { isDigit, isLetter, sleep } from "./utils"; 35 | import natives from "./natives"; 36 | 37 | interface IDVersion { 38 | key: string; 39 | name: string; 40 | } 41 | 42 | interface PredefinedUUID { 43 | id: string; 44 | name: string; 45 | } 46 | 47 | const ID_VERSIONS: IDVersion[] = [ 48 | { key: "uuidv1", name: "UUID v1(Gregorian Time-based)" }, 49 | { key: "uuidv3", name: "UUID v3(MD5 Name-based)" }, 50 | { key: "uuidv4", name: "UUID v4(Random)" }, 51 | { key: "uuidv5", name: "UUID v5(SHA-1 Name-based)" }, 52 | { key: "uuidv6", name: "UUID v6(Reordered Gregorian Time-based)" }, 53 | { key: "uuidv7", name: "UUID v7(Unix Time-based)" }, 54 | { key: "shortuuid", name: "Short UUID" }, 55 | { key: "niluuid", name: "Nil UUID" }, 56 | { key: "maxuuid", name: "Max UUID" }, 57 | { key: "ulid", name: "ULID" }, 58 | { key: "upid", name: "UPID" }, 59 | { key: "cuid", name: "CUID" }, 60 | { key: "cuid2", name: "CUID2" }, 61 | { key: "nanoid", name: "Nano ID" }, 62 | { key: "nuid", name: "NUID" }, 63 | { key: "tsid", name: "TSID" }, 64 | { key: "scru128", name: "SCRU128" }, 65 | { key: "snowflake", name: "Snowflake" }, 66 | { key: "sonyflake", name: "Sonyflake" }, 67 | { key: "objectid", name: "Object ID" }, 68 | ]; 69 | 70 | const PREDEFINED_UUIDS: PredefinedUUID[] = [ 71 | { id: "6ba7b810-9dad-11d1-80b4-00c04fd430c8", name: "DNS" }, 72 | { id: "6ba7b811-9dad-11d1-80b4-00c04fd430c8", name: "URL" }, 73 | { id: "6ba7b812-9dad-11d1-80b4-00c04fd430c8", name: "OID" }, 74 | { id: "6ba7b814-9dad-11d1-80b4-00c04fd430c8", name: "X.500" }, 75 | ]; 76 | 77 | const MIN_BULK_QUANTITY = 1; 78 | const MAX_BULK_QUANTITY = 500; 79 | const DFT_BULK_QUANTITY = 10; 80 | 81 | const messagesInChinese = { 82 | uniqueIDVersion: "选择类型:", 83 | namespace: "命名空间:", 84 | namespacePlaceholder: "输入命名空间UUID值(默认随机)", 85 | namespaceTooltip: 86 | "命名空间必须是格式为00000000-0000-0000-0000-000000000000的有效UUID值。请选择一个预设的UUID值或自动填充随机UUID值。", 87 | name: "名称:", 88 | namePlaceholder: "输入名称(默认是‘name’)", 89 | nameTooltip: 90 | "必填项。名称可以是任意内容。相同命名空间和相同名称将始终生成相同的UUID值。", 91 | prefix: "前缀:", 92 | prefixPlaceholder: "输入前缀(4个字符长度,默认是'zzzz')", 93 | predefinedUUIDs: "预设UUID值", 94 | bulkGeneration: "批量生成", 95 | bulkQuantity: "批量生成数量({min}~{max}):", 96 | bulkOutput: "输出格式:", 97 | bulkOutputRaw: "原始", 98 | bulkOutputJson: "JSON", 99 | generatedResult: "生成结果:", 100 | regenerate: "重新生成", 101 | copyToClipboard: "拷贝至剪贴板", 102 | saveToFile: "保存至文件", 103 | copied: "已拷贝!", 104 | }; 105 | 106 | function App({ changeLocale }: { changeLocale: (locale: string) => void }) { 107 | const [idVersion, setIDVersion] = useState("uuidv4"); 108 | const [namespace, setNamespace] = useState(""); 109 | const [name, setName] = useState(""); 110 | const [namespaceType, setNamespaceType] = useState(""); 111 | const [prefix, setPrefix] = useState(""); 112 | const [isBulkMode, setIsBulkMode] = useState(false); 113 | const [bulkQuantity, setBulkQuantity] = useState(DFT_BULK_QUANTITY); 114 | const [bulkOutput, setBulkOutput] = useState("raw"); 115 | const [isGenerating, setIsGenerating] = useState(false); 116 | const [result, setResult] = useState(""); 117 | const [bulkResult, setBulkResult] = useState([]); 118 | const { isCopied, copyToClipboard } = useCopy(); 119 | const { isCopied: isBulkCopied, copyToClipboard: copyBulkToClipboard } = 120 | useCopy(); 121 | const intl = useIntl(); 122 | 123 | async function generate(n: number) { 124 | let value: string[] = []; 125 | if (idVersion === "uuidv1") { 126 | value = await natives.generateUUIDV1(n); 127 | } else if (idVersion === "uuidv3") { 128 | value = await natives.generateUUIDV3(namespace, name, n); 129 | } else if (idVersion === "uuidv4") { 130 | value = await natives.generateUUIDV4(n); 131 | } else if (idVersion === "uuidv5") { 132 | value = await natives.generateUUIDV5(namespace, name, n); 133 | } else if (idVersion === "uuidv6") { 134 | value = await natives.generateUUIDV6(n); 135 | } else if (idVersion === "uuidv7") { 136 | value = await natives.generateUUIDV7(n); 137 | } else if (idVersion === "shortuuid") { 138 | value = await natives.generateShortUUID(n); 139 | } else if (idVersion === "niluuid") { 140 | value = await natives.generateNilUUID(n); 141 | } else if (idVersion === "maxuuid") { 142 | value = await natives.generateMaxUUID(n); 143 | } else if (idVersion === "ulid") { 144 | value = await natives.generateULID(n); 145 | } else if (idVersion === "nanoid") { 146 | value = await natives.generateNanoID(n); 147 | } else if (idVersion === "cuid") { 148 | value = await natives.generateCUID(n); 149 | } else if (idVersion === "cuid2") { 150 | value = await natives.generateCUID2(n); 151 | } else if (idVersion === "snowflake") { 152 | value = await natives.generateSnowflake(n); 153 | } else if (idVersion === "sonyflake") { 154 | value = await natives.generateSonyflake(n); 155 | } else if (idVersion === "nuid") { 156 | value = await natives.generateNUID(n); 157 | } else if (idVersion === "upid") { 158 | value = await natives.generateUPID(prefix || "zzzz", n); 159 | } else if (idVersion === "tsid") { 160 | value = await natives.generateTSID(n); 161 | } else if (idVersion === "objectid") { 162 | value = await natives.generateObjectID(n); 163 | } else if (idVersion === "scru128") { 164 | value = await natives.generateSCRU128(n); 165 | } 166 | return value; 167 | } 168 | 169 | async function generateOne() { 170 | setIsGenerating(true); 171 | const newID = (await generate(1))[0]; 172 | setResult(newID); 173 | setIsGenerating(false); 174 | } 175 | 176 | async function generateBulk() { 177 | setIsGenerating(true); 178 | await sleep(100); 179 | const ids = await generate(bulkQuantity); 180 | setBulkResult(ids); 181 | setIsGenerating(false); 182 | } 183 | 184 | useEffect(() => { 185 | generateOne(); 186 | setBulkResult([]); 187 | }, [idVersion, namespace, name, prefix]); 188 | 189 | async function setWindowHeight(height: number) { 190 | await getCurrentWindow().setSize(new LogicalSize(480, height)); 191 | } 192 | 193 | const { ref: windowInnerBoxRef } = useResizeObserver({ 194 | onResize: ({ height }) => { 195 | if (height) { 196 | setWindowHeight(height); 197 | } 198 | }, 199 | }); 200 | 201 | useEffect(() => { 202 | document.addEventListener( 203 | "contextmenu", 204 | (e) => { 205 | e.preventDefault(); 206 | return false; 207 | }, 208 | { capture: true } 209 | ); 210 | }, []); 211 | 212 | return ( 213 | 214 | 215 | 216 | 217 | 218 | 224 | 228 | 229 | 230 | 231 | 232 | changeLocale && changeLocale("en")} 234 | > 235 | English 236 | 237 | changeLocale && changeLocale("cn")} 239 | > 240 | 简体中文 241 | 242 | 243 | 244 | 245 | 246 | 247 | 251 | 252 | setIDVersion(value)} 255 | > 256 | 257 | 258 | {ID_VERSIONS.map((version) => ( 259 | 260 | {version.name} 261 | 262 | ))} 263 | 264 | 265 | {idVersion === "uuidv3" || idVersion === "uuidv5" ? ( 266 | <> 267 | 268 | 269 | 270 | { 278 | setNamespace(e.target.value); 279 | setNamespaceType(""); 280 | }} 281 | > 282 | 283 | {namespaceType ? ( 284 | 285 | {namespaceType} 286 | 287 | ) : null} 288 | { 292 | const id = (await natives.generateUUIDV4())[0]; 293 | setNamespace(id); 294 | setNamespaceType(""); 295 | }} 296 | > 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 312 | 313 | {PREDEFINED_UUIDS.map((uuid) => ( 314 | { 317 | setNamespace(uuid.id); 318 | setNamespaceType(uuid.name); 319 | }} 320 | > 321 | {uuid.name} 322 | 323 | ))} 324 | 325 | 326 | 327 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | setName(e.target.value)} 349 | > 350 | 351 | 358 | 359 | 360 | 361 | 362 | 363 | ) : null} 364 | {idVersion === "upid" ? ( 365 | <> 366 | 367 | 368 | 369 | setPrefix(e.target.value)} 379 | /> 380 | 381 | ) : null} 382 | 383 | 394 | 395 | 396 | 400 | 401 | setIsBulkMode(checked)} 405 | /> 406 | 407 | {isBulkMode ? ( 408 | <> 409 | 410 | 411 | 412 | 420 | 421 | { 426 | if (!e.target.value) { 427 | setBulkQuantity(MIN_BULK_QUANTITY); 428 | return; 429 | } 430 | const value = parseInt(e.target.value, 10); 431 | if (value <= MIN_BULK_QUANTITY) { 432 | setBulkQuantity(MIN_BULK_QUANTITY); 433 | } else if (value >= MAX_BULK_QUANTITY) { 434 | setBulkQuantity(MAX_BULK_QUANTITY); 435 | } else { 436 | setBulkQuantity(parseInt(e.target.value, 10)); 437 | } 438 | }} 439 | > 440 | 441 | { 445 | if (bulkQuantity >= MAX_BULK_QUANTITY) { 446 | setBulkQuantity(MAX_BULK_QUANTITY); 447 | } else { 448 | setBulkQuantity(bulkQuantity + 1); 449 | } 450 | }} 451 | > 452 | 453 | 454 | { 458 | if (bulkQuantity <= MIN_BULK_QUANTITY) { 459 | setBulkQuantity(MIN_BULK_QUANTITY); 460 | } else { 461 | setBulkQuantity(bulkQuantity - 1); 462 | } 463 | }} 464 | > 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 476 | 477 | setBulkOutput(value)} 480 | radioGroup="bulkOutput" 481 | > 482 | 483 | 484 | 485 | 486 | 490 | 491 | 492 | 493 | 494 | 495 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | ) : null} 507 | 508 | 509 | 510 | 514 | 515 | 516 | 517 | {isBulkMode ? ( 518 | 528 | 529 | {bulkOutput === "raw" ? ( 530 | bulkResult.map((item, rownum) => ( 531 | 532 | 544 | {rownum + 1} 545 | 546 | {item} 547 | 548 | )) 549 | ) : bulkOutput === "json" ? ( 550 | 551 |
552 |                     {bulkResult.length > 0
553 |                       ? JSON.stringify(bulkResult, undefined, 2)
554 |                       : ""}
555 |                   
556 |
557 | ) : null} 558 |
559 |
560 | ) : ( 561 | 570 | 576 | {[...result].map((char, i) => 577 | char === " " ? ( 578 |   579 | ) : ( 580 | 592 | {char} 593 | 594 | ) 595 | )} 596 | 597 | 598 | )} 599 | 606 | 619 | 653 | {isBulkMode ? ( 654 | 690 | ) : null} 691 | 692 |
693 |
694 | ); 695 | } 696 | 697 | function AppWithProvider() { 698 | const theme = useTheme(); 699 | const [storedLocaleValue, setLocaleValue] = useLocalStorage("language", "en"); 700 | return ( 701 | 706 | 707 | 708 | 709 | 710 | ); 711 | } 712 | 713 | export default AppWithProvider; 714 | --------------------------------------------------------------------------------