From f1c3bd2971f2d6aabdf2c9f541550880d0a1c059 Mon Sep 17 00:00:00 2001 From: rusconn Date: Sun, 14 May 2023 12:44:34 +0000 Subject: [PATCH] renewal recreate project by using https://github.com/shadcn/next-template App: - support dark mode - add toggle theme button - add clear search button - add search button - add current page indicator - add tool group pages - add settings tool - add 1 tab format option to Json format tool - add paste button to some tools - add file button to some tools - add copy button to some tools - add clear button to some tools - change favicon - change search hit rate - change each page title - change icons from Material Icons to Lucide - change sidebar scroll area - change editor from Ace to Monaco - change parsable separators of number base converter - change default value of format option of number base converter - change default values of some tool forms - change some styles - remove disabled tools - remove real-time search - fix uri encoding tool Dev: - MUI + Emotion -> Radix UI + Tailwind CSS - Next.js 12 Pages -> Next.js 13 App Router - React 17 -> React 18 - many other packages upgraded - use useState instead of recoil - use Next.js typedRoutes instead of pathpida - clean npm scripts - format import statements by Prettier - no component separations between container and presenter - effective component memoizations - add vscode settings - many refactors --- .babelrc | 43 - .devcontainer/devcontainer.json | 2 +- .editorconfig | 10 + .eslintignore | 6 +- .eslintrc.json | 56 +- .gitignore | 26 +- .prettierignore | 13 +- .prettierrc.json | 4 - .vscode/extensions.json | 11 + .vscode/settings.json | 10 + README.md | 7 +- app/apple-icon.png | Bin 0 -> 2452 bytes app/converters/json-yaml/layout.tsx | 11 + app/converters/json-yaml/page.tsx | 174 + app/converters/layout.tsx | 11 + app/converters/number-base/layout.tsx | 11 + app/converters/number-base/page.tsx | 111 + app/converters/page.tsx | 11 + app/encoders-decoders/base64/layout.tsx | 11 + app/encoders-decoders/base64/page.tsx | 98 + app/encoders-decoders/html/layout.tsx | 11 + app/encoders-decoders/html/page.tsx | 91 + app/encoders-decoders/jwt/layout.tsx | 11 + app/encoders-decoders/jwt/page.tsx | 65 + app/encoders-decoders/layout.tsx | 11 + app/encoders-decoders/page.tsx | 11 + app/encoders-decoders/url/layout.tsx | 11 + app/encoders-decoders/url/page.tsx | 100 + app/favicon.ico | Bin 0 -> 4286 bytes app/formatters/json/layout.tsx | 11 + app/formatters/json/page.tsx | 111 + app/formatters/layout.tsx | 11 + app/formatters/page.tsx | 11 + app/generators/hash/layout.tsx | 11 + app/generators/hash/page.tsx | 129 + app/generators/layout.tsx | 11 + app/generators/page.tsx | 11 + app/generators/uuid/layout.tsx | 11 + app/generators/uuid/page.tsx | 171 + app/icon.svg | 9 + app/layout.tsx | 61 + app/not-found.tsx | 9 + app/page.tsx | 11 + app/search/layout.tsx | 10 + app/search/page.tsx | 26 + app/settings/layout.tsx | 11 + app/settings/page.tsx | 58 + components/buttons/base.tsx | 30 + components/buttons/clear.tsx | 13 + components/buttons/copy.tsx | 24 + components/buttons/file.tsx | 66 + components/buttons/paste.tsx | 27 + components/configuration.tsx | 23 + components/configurations.tsx | 20 + components/control-menu.tsx | 20 + components/icons.tsx | 71 + components/indicator.tsx | 7 + components/labeled-switch.tsx | 19 + components/page-root-section.tsx | 14 + components/page-section.tsx | 25 + components/sidebar.tsx | 29 + components/sidebar/all-tools.tsx | 20 + components/sidebar/search-bar.tsx | 64 + components/sidebar/settings.tsx | 20 + components/sidebar/tool-group.tsx | 69 + components/sidebar/tool-groups.tsx | 38 + components/sidebar/tool-link.tsx | 38 + components/site-header.tsx | 42 + components/tailwind-indicator.tsx | 14 + components/theme-provider.tsx | 8 + components/theme-toggle.tsx | 36 + components/tool-card.tsx | 21 + components/tool-cards.tsx | 17 + components/ui/button.tsx | 39 + components/ui/editor.tsx | 39 + components/ui/input.tsx | 20 + components/ui/label.tsx | 21 + components/ui/select.tsx | 108 + components/ui/separator.tsx | 22 + components/ui/switch.tsx | 28 + components/ui/textarea.tsx | 20 + components/ui/tooltip.tsx | 25 + config/site.ts | 8 + config/tools.ts | 149 + hooks/useScrollFollow.ts | 23 + lib/base.ts | 22 + lib/fonts.ts | 11 + {src/libs => lib}/jwt.ts | 0 lib/style.ts | 6 + {src/libs => lib}/uuid.ts | 9 +- next.config.js => next.config.mjs | 5 +- package.json | 92 +- postcss.config.js | 6 + prettier.config.js | 29 + public/favicon.ico | Bin 25931 -> 0 bytes src/components/common/CodeEditor.tsx | 37 - src/components/common/Configuration.tsx | 30 - src/components/common/Configurations.tsx | 21 - src/components/common/Main.tsx | 26 - src/components/common/MainItem.tsx | 18 - src/components/common/TextField.tsx | 18 - src/components/common/ToolCard.tsx | 95 - src/components/common/ToolCardGrid.tsx | 23 - src/components/common/index.ts | 17 - src/components/layout/Drawer.tsx | 62 - src/components/layout/DrawerCollapseItem.tsx | 55 - src/components/layout/DrawerItem.tsx | 48 - src/components/layout/DrawerItemIcon.tsx | 18 - src/components/layout/Header.tsx | 77 - src/components/layout/Layout.tsx | 42 - src/components/layout/SearchBar.tsx | 71 - src/components/layout/index.ts | 3 - src/components/layout/states/index.ts | 1 - src/components/layout/states/searchText.ts | 6 - .../converters/json-yaml/Configuration.tsx | 50 - .../pages/converters/json-yaml/Content.tsx | 117 - .../pages/converters/json-yaml/index.ts | 3 - .../converters/number-base/Configuration.tsx | 36 - .../pages/converters/number-base/Content.tsx | 145 - .../pages/converters/number-base/index.ts | 3 - .../encoders-decoders/base64/Content.tsx | 53 - .../pages/encoders-decoders/base64/index.ts | 3 - .../pages/encoders-decoders/html/Content.tsx | 46 - .../pages/encoders-decoders/html/index.ts | 3 - .../pages/encoders-decoders/jwt/Content.tsx | 61 - .../pages/encoders-decoders/jwt/index.ts | 3 - .../pages/encoders-decoders/url/Content.tsx | 55 - .../pages/encoders-decoders/url/index.ts | 3 - .../pages/formatters/json/Configuration.tsx | 50 - .../pages/formatters/json/Content.tsx | 88 - src/components/pages/formatters/json/index.ts | 3 - .../pages/generators/hash/Configuration.tsx | 36 - .../pages/generators/hash/Content.tsx | 95 - src/components/pages/generators/hash/index.ts | 3 - .../pages/generators/uuid/Configuration.tsx | 87 - .../pages/generators/uuid/Content.tsx | 119 - src/components/pages/generators/uuid/index.ts | 3 - src/components/pages/home/Content.tsx | 18 - src/components/pages/home/index.ts | 3 - src/components/pages/search/Content.tsx | 45 - src/components/pages/search/index.ts | 3 - src/data/emotion.ts | 3 - src/data/index.ts | 3 - src/data/mui.ts | 27 - src/data/site.ts | 1 - src/data/tools.tsx | 177 - src/libs/$path.ts | 76 - src/libs/string.ts | 34 - src/pages/_app.tsx | 36 - src/pages/_document.tsx | 67 - src/pages/converters/json-yaml.tsx | 7 - src/pages/converters/number-base.tsx | 7 - src/pages/encoders-decoders/base64.tsx | 7 - src/pages/encoders-decoders/html.tsx | 7 - src/pages/encoders-decoders/jwt.tsx | 7 - src/pages/encoders-decoders/url.tsx | 7 - src/pages/formatters/json.tsx | 7 - src/pages/generators/hash.tsx | 7 - src/pages/generators/uuid.tsx | 7 - src/pages/index.tsx | 7 - src/pages/search.tsx | 7 - src/types/emotion.ts | 6 - styles/globals.css | 142 + tailwind.config.js | 107 + tsconfig.json | 15 +- types/route.ts | 4 + yarn.lock | 4130 +++++++++++------ 167 files changed, 5931 insertions(+), 3853 deletions(-) delete mode 100644 .babelrc create mode 100644 .editorconfig mode change 120000 => 100644 .eslintignore mode change 120000 => 100644 .prettierignore delete mode 100644 .prettierrc.json create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 app/apple-icon.png create mode 100644 app/converters/json-yaml/layout.tsx create mode 100644 app/converters/json-yaml/page.tsx create mode 100644 app/converters/layout.tsx create mode 100644 app/converters/number-base/layout.tsx create mode 100644 app/converters/number-base/page.tsx create mode 100644 app/converters/page.tsx create mode 100644 app/encoders-decoders/base64/layout.tsx create mode 100644 app/encoders-decoders/base64/page.tsx create mode 100644 app/encoders-decoders/html/layout.tsx create mode 100644 app/encoders-decoders/html/page.tsx create mode 100644 app/encoders-decoders/jwt/layout.tsx create mode 100644 app/encoders-decoders/jwt/page.tsx create mode 100644 app/encoders-decoders/layout.tsx create mode 100644 app/encoders-decoders/page.tsx create mode 100644 app/encoders-decoders/url/layout.tsx create mode 100644 app/encoders-decoders/url/page.tsx create mode 100644 app/favicon.ico create mode 100644 app/formatters/json/layout.tsx create mode 100644 app/formatters/json/page.tsx create mode 100644 app/formatters/layout.tsx create mode 100644 app/formatters/page.tsx create mode 100644 app/generators/hash/layout.tsx create mode 100644 app/generators/hash/page.tsx create mode 100644 app/generators/layout.tsx create mode 100644 app/generators/page.tsx create mode 100644 app/generators/uuid/layout.tsx create mode 100644 app/generators/uuid/page.tsx create mode 100644 app/icon.svg create mode 100644 app/layout.tsx create mode 100644 app/not-found.tsx create mode 100644 app/page.tsx create mode 100644 app/search/layout.tsx create mode 100644 app/search/page.tsx create mode 100644 app/settings/layout.tsx create mode 100644 app/settings/page.tsx create mode 100644 components/buttons/base.tsx create mode 100644 components/buttons/clear.tsx create mode 100644 components/buttons/copy.tsx create mode 100644 components/buttons/file.tsx create mode 100644 components/buttons/paste.tsx create mode 100644 components/configuration.tsx create mode 100644 components/configurations.tsx create mode 100644 components/control-menu.tsx create mode 100644 components/icons.tsx create mode 100644 components/indicator.tsx create mode 100644 components/labeled-switch.tsx create mode 100644 components/page-root-section.tsx create mode 100644 components/page-section.tsx create mode 100644 components/sidebar.tsx create mode 100644 components/sidebar/all-tools.tsx create mode 100644 components/sidebar/search-bar.tsx create mode 100644 components/sidebar/settings.tsx create mode 100644 components/sidebar/tool-group.tsx create mode 100644 components/sidebar/tool-groups.tsx create mode 100644 components/sidebar/tool-link.tsx create mode 100644 components/site-header.tsx create mode 100644 components/tailwind-indicator.tsx create mode 100644 components/theme-provider.tsx create mode 100644 components/theme-toggle.tsx create mode 100644 components/tool-card.tsx create mode 100644 components/tool-cards.tsx create mode 100644 components/ui/button.tsx create mode 100644 components/ui/editor.tsx create mode 100644 components/ui/input.tsx create mode 100644 components/ui/label.tsx create mode 100644 components/ui/select.tsx create mode 100644 components/ui/separator.tsx create mode 100644 components/ui/switch.tsx create mode 100644 components/ui/textarea.tsx create mode 100644 components/ui/tooltip.tsx create mode 100644 config/site.ts create mode 100644 config/tools.ts create mode 100644 hooks/useScrollFollow.ts create mode 100644 lib/base.ts create mode 100644 lib/fonts.ts rename {src/libs => lib}/jwt.ts (100%) create mode 100644 lib/style.ts rename {src/libs => lib}/uuid.ts (55%) rename next.config.js => next.config.mjs (55%) create mode 100644 postcss.config.js create mode 100644 prettier.config.js delete mode 100644 public/favicon.ico delete mode 100644 src/components/common/CodeEditor.tsx delete mode 100644 src/components/common/Configuration.tsx delete mode 100644 src/components/common/Configurations.tsx delete mode 100644 src/components/common/Main.tsx delete mode 100644 src/components/common/MainItem.tsx delete mode 100644 src/components/common/TextField.tsx delete mode 100644 src/components/common/ToolCard.tsx delete mode 100644 src/components/common/ToolCardGrid.tsx delete mode 100644 src/components/common/index.ts delete mode 100644 src/components/layout/Drawer.tsx delete mode 100644 src/components/layout/DrawerCollapseItem.tsx delete mode 100644 src/components/layout/DrawerItem.tsx delete mode 100644 src/components/layout/DrawerItemIcon.tsx delete mode 100644 src/components/layout/Header.tsx delete mode 100644 src/components/layout/Layout.tsx delete mode 100644 src/components/layout/SearchBar.tsx delete mode 100644 src/components/layout/index.ts delete mode 100644 src/components/layout/states/index.ts delete mode 100644 src/components/layout/states/searchText.ts delete mode 100644 src/components/pages/converters/json-yaml/Configuration.tsx delete mode 100644 src/components/pages/converters/json-yaml/Content.tsx delete mode 100644 src/components/pages/converters/json-yaml/index.ts delete mode 100644 src/components/pages/converters/number-base/Configuration.tsx delete mode 100644 src/components/pages/converters/number-base/Content.tsx delete mode 100644 src/components/pages/converters/number-base/index.ts delete mode 100644 src/components/pages/encoders-decoders/base64/Content.tsx delete mode 100644 src/components/pages/encoders-decoders/base64/index.ts delete mode 100644 src/components/pages/encoders-decoders/html/Content.tsx delete mode 100644 src/components/pages/encoders-decoders/html/index.ts delete mode 100644 src/components/pages/encoders-decoders/jwt/Content.tsx delete mode 100644 src/components/pages/encoders-decoders/jwt/index.ts delete mode 100644 src/components/pages/encoders-decoders/url/Content.tsx delete mode 100644 src/components/pages/encoders-decoders/url/index.ts delete mode 100644 src/components/pages/formatters/json/Configuration.tsx delete mode 100644 src/components/pages/formatters/json/Content.tsx delete mode 100644 src/components/pages/formatters/json/index.ts delete mode 100644 src/components/pages/generators/hash/Configuration.tsx delete mode 100644 src/components/pages/generators/hash/Content.tsx delete mode 100644 src/components/pages/generators/hash/index.ts delete mode 100644 src/components/pages/generators/uuid/Configuration.tsx delete mode 100644 src/components/pages/generators/uuid/Content.tsx delete mode 100644 src/components/pages/generators/uuid/index.ts delete mode 100644 src/components/pages/home/Content.tsx delete mode 100644 src/components/pages/home/index.ts delete mode 100644 src/components/pages/search/Content.tsx delete mode 100644 src/components/pages/search/index.ts delete mode 100644 src/data/emotion.ts delete mode 100644 src/data/index.ts delete mode 100644 src/data/mui.ts delete mode 100644 src/data/site.ts delete mode 100644 src/data/tools.tsx delete mode 100644 src/libs/$path.ts delete mode 100644 src/libs/string.ts delete mode 100644 src/pages/_app.tsx delete mode 100644 src/pages/_document.tsx delete mode 100644 src/pages/converters/json-yaml.tsx delete mode 100644 src/pages/converters/number-base.tsx delete mode 100644 src/pages/encoders-decoders/base64.tsx delete mode 100644 src/pages/encoders-decoders/html.tsx delete mode 100644 src/pages/encoders-decoders/jwt.tsx delete mode 100644 src/pages/encoders-decoders/url.tsx delete mode 100644 src/pages/formatters/json.tsx delete mode 100644 src/pages/generators/hash.tsx delete mode 100644 src/pages/generators/uuid.tsx delete mode 100644 src/pages/index.tsx delete mode 100644 src/pages/search.tsx delete mode 100644 src/types/emotion.ts create mode 100644 styles/globals.css create mode 100644 tailwind.config.js create mode 100644 types/route.ts diff --git a/.babelrc b/.babelrc deleted file mode 100644 index ad3e93c..0000000 --- a/.babelrc +++ /dev/null @@ -1,43 +0,0 @@ -// https://emotion.sh/docs/css-prop##babel-preset -// https://mui.com/guides/minimizing-bundle-size/#option-2 -// https://github.com/vercel/next.js/discussions/17822 -{ - "presets": [ - [ - "next/babel", - { - "preset-react": { - "runtime": "automatic", - "importSource": "@emotion/react" - }, - "preset-env": { - "debug": false, - "targets": { - "browsers": ">1%, not ie 11, not op_mini all" - } - } - } - ] - ], - "plugins": [ - [ - "babel-plugin-import", - { - "libraryName": "@mui/material", - "libraryDirectory": "", - "camel2DashComponentName": false - }, - "core" - ], - [ - "babel-plugin-import", - { - "libraryName": "@mui/icons-material", - "libraryDirectory": "", - "camel2DashComponentName": false - }, - "icons" - ], - ["@emotion/babel-plugin"] - ] -} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 9076768..b218fc5 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -22,7 +22,7 @@ "ms-azuretools.vscode-docker", "esbenp.prettier-vscode", "artdiniz.quitcontrol-vscode", - "styled-components.vscode-styled-components" + "bradlc.vscode-tailwindcss" ], "runServices": ["app"], "shutdownAction": "stopCompose" diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..ae10a5c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +# editorconfig.org +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.eslintignore b/.eslintignore deleted file mode 120000 index 3e4e48b..0000000 --- a/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -.gitignore \ No newline at end of file diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..dc0f9d8 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,5 @@ +dist/* +.cache +public +node_modules +*.esm.js diff --git a/.eslintrc.json b/.eslintrc.json index 669d3db..071a369 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,49 +1,49 @@ { + "$schema": "https://json.schemastore.org/eslintrc", "root": true, - "parserOptions": { - "project": "./tsconfig.json" - }, - "parser": "@typescript-eslint/parser", "extends": [ "airbnb", "airbnb-typescript", "airbnb/hooks", "plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended-requiring-type-checking", + "plugin:tailwindcss/recommended", "next/core-web-vitals", "prettier" ], + "plugins": ["tailwindcss"], "rules": { - "no-console": "off", - "no-else-return": "off", - "no-underscore-dangle": ["error", { "allow": ["_slug"] }], - "import/prefer-default-export": "off", + "@next/next/no-html-link-for-pages": "off", "@typescript-eslint/explicit-module-boundary-types": "off", - "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], - "react/require-default-props": "off", - "react/function-component-definition": "off", - "jsx-a11y/anchor-is-valid": [ + "@typescript-eslint/no-unused-vars": [ "error", - { - "components": ["Link"], - "specialLink": ["hrefLeft", "hrefRight"], - "aspects": ["invalidHref", "preferButton"] - } + { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" } ], - "import/order": [ - "error", - { - "groups": ["builtin", "external", "internal", ["parent", "sibling"], "object", "index"], - "newlines-between": "always", - "alphabetize": { "order": "asc", "caseInsensitive": true } - } - ] + "consistent-return": "off", + "no-else-return": "off", + "no-nested-ternary": "off", + "import/prefer-default-export": "off", + "react/function-component-definition": "off", + "react/jsx-key": "off", + "react/jsx-props-no-spreading": "off", + "react/require-default-props": "off", + "tailwindcss/no-custom-classname": "off" + }, + "settings": { + "tailwindcss": { + "callees": ["cn"], + "config": "./tailwind.config.js" + }, + "next": { + "rootDir": ["./"] + } }, "overrides": [ { - "files": "src/pages/_app.tsx", - "rules": { - "react/jsx-props-no-spreading": "off" + "files": ["*.ts", "*.tsx"], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "project": "./tsconfig.json" } } ] diff --git a/.gitignore b/.gitignore index d848f3d..5180021 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,17 @@ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies -/node_modules -/.pnp +node_modules +.pnp .pnp.js # testing -/coverage +coverage # next.js -/.next/ -/out/ - -# production -/build +.next/ +out/ +build # misc .DS_Store @@ -31,14 +29,14 @@ yarn-error.log* .env.test.local .env.production.local -# vercel -.vercel +# turbo +.turbo + +.contentlayer +.env # typescript -*.tsbuildinfo +tsconfig.tsbuildinfo # eslint .eslintcache - -# env -.env diff --git a/.prettierignore b/.prettierignore deleted file mode 120000 index 3e4e48b..0000000 --- a/.prettierignore +++ /dev/null @@ -1 +0,0 @@ -.gitignore \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..951d0ad --- /dev/null +++ b/.prettierignore @@ -0,0 +1,12 @@ +cache +.cache +package.json +package-lock.json +public +CHANGELOG.md +.yarn +dist +node_modules +.next +build +.contentlayer diff --git a/.prettierrc.json b/.prettierrc.json deleted file mode 100644 index 5a7a8da..0000000 --- a/.prettierrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "printWidth": 100, - "arrowParens": "avoid" -} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..359e733 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,11 @@ +{ + "recommendations": [ + "bierner.github-markdown-preview", + "davidanson.vscode-markdownlint", + "irongeek.vscode-env", + "dbaeumer.vscode-eslint", + "ms-azuretools.vscode-docker", + "esbenp.prettier-vscode", + "bradlc.vscode-tailwindcss" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5232518 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "files.associations": { + "*.css": "tailwindcss" + }, + "tailwindCSS.experimental.classRegex": [["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]], + "typescript.tsdk": "node_modules/typescript/lib", + "typescript.enablePromptUseWorkspaceTsdk": true +} diff --git a/README.md b/README.md index 6476010..359b104 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,11 @@ A web clone of [DevToys](https://github.com/veler/DevToys) +## Known issues + +- [Editor may not resize to fit container size](https://github.com/suren-atoyan/monaco-react/issues/346) +- CSS outlines messed up + ## Todo - [x] Add site layout @@ -15,5 +20,5 @@ A web clone of [DevToys](https://github.com/veler/DevToys) - [ ] Graphic - [ ] Settings - [x] Settings menu item - - [ ] Support dark mode + - [x] Support dark mode - [ ] Support i18n diff --git a/app/apple-icon.png b/app/apple-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4e7b5463790db4b2ece192bdae82c39e4b69fd67 GIT binary patch literal 2452 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 { + setJson(text); + + try { + const parsed = JSON.parse(text) as unknown; + setYaml(YAML.stringify(parsed, { indent: indentation.length, simpleKeys: true })); + } catch { + setYaml(""); + } + }, + [indentation.length] + ); + + const setYamlReactively = useCallback( + (text: string) => { + setYaml(text); + + try { + const parsed = YAML.parse(text, { merge: true }) as unknown; + setJson(JSON.stringify(parsed, null, indentation)); + } catch { + setJson(""); + } + }, + [indentation] + ); + + const clearBoth = useCallback(() => { + setJson(""); + setYaml(""); + }, []); + + const onIndentationChange: SelectProps["onValueChange"] = value => { + setIndentation(value); + + try { + const parsed = JSON.parse(json) as unknown; + setJson(JSON.stringify(parsed, null, value)); + setYaml(YAML.stringify(parsed, { indent: value.length, simpleKeys: true })); + } catch { + clearBoth(); + } + }; + + const onJsonChange: EditorProps["onChange"] = value => setJsonReactively(value ?? ""); + const onYamlChange: EditorProps["onChange"] = value => setYamlReactively(value ?? ""); + + const indentationIcon = useMemo(() => , []); + + const indentationConfig = ( + + + + + + 2 spaces + 4 spaces + + + } + /> + ); + + const jsonPasteButton = useMemo( + () => , + [setJsonReactively] + ); + + const yamlPasteButton = useMemo( + () => , + [setYamlReactively] + ); + + const jsonFileButton = useMemo( + () => ( + + ), + [setJsonReactively] + ); + + const yamlFileButton = useMemo( + () => ( + + ), + [setYamlReactively] + ); + + const jsonCopyButton = ; + const yamlCopyButton = ; + + const clearButton = useMemo( + () => , + [clearBoth] + ); + + const jsonControl = ( + + ); + + const yamlControl = ( + + ); + + return ( + + + + +
+ + + + + + +
+
+ ); +} diff --git a/app/converters/layout.tsx b/app/converters/layout.tsx new file mode 100644 index 0000000..c13501d --- /dev/null +++ b/app/converters/layout.tsx @@ -0,0 +1,11 @@ +import { Metadata } from "next"; + +import { toolGroups } from "@/config/tools"; + +export const metadata: Metadata = { + title: toolGroups.converters.title, +}; + +export default function Layout({ children }: { children: React.ReactNode }) { + return children; +} diff --git a/app/converters/number-base/layout.tsx b/app/converters/number-base/layout.tsx new file mode 100644 index 0000000..67c26f8 --- /dev/null +++ b/app/converters/number-base/layout.tsx @@ -0,0 +1,11 @@ +import { Metadata } from "next"; + +import { toolGroups } from "@/config/tools"; + +export const metadata: Metadata = { + title: toolGroups.converters.tools.numberBase.longTitle, +}; + +export default function Layout({ children }: { children: React.ReactNode }) { + return children; +} diff --git a/app/converters/number-base/page.tsx b/app/converters/number-base/page.tsx new file mode 100644 index 0000000..c99d82f --- /dev/null +++ b/app/converters/number-base/page.tsx @@ -0,0 +1,111 @@ +"use client"; + +import { useCallback, useMemo, useState } from "react"; + +import { toolGroups } from "@/config/tools"; +import * as baselib from "@/lib/base"; +import { Input, InputProps } from "@/components/ui/input"; +import { PasteButton } from "@/components/buttons/paste"; +import { Configuration } from "@/components/configuration"; +import { Configurations } from "@/components/configurations"; +import { ControlMenu } from "@/components/control-menu"; +import { icons } from "@/components/icons"; +import { LabeledSwitch } from "@/components/labeled-switch"; +import { PageRootSection } from "@/components/page-root-section"; +import { PageSection } from "@/components/page-section"; + +const baseConfig = { + 10: { prefix: "", validate: baselib.isDecimal }, + 16: { prefix: "0x", validate: baselib.isHexadecimal }, + 8: { prefix: "0o", validate: baselib.isOctal }, + 2: { prefix: "0b", validate: baselib.isBinary }, +} as const; + +export default function Page() { + const [format, setFormat] = useState(true); + const [int, setInt] = useState(BigInt(42)); + + const newDec = int?.toString(10) ?? ""; + const newHex = int?.toString(16).toUpperCase() ?? ""; + const newOct = int?.toString(8) ?? ""; + const newBin = int?.toString(2) ?? ""; + + const dec = format ? baselib.formatDecimal(newDec) : newDec; + const hex = format ? baselib.formatHexadecimal(newHex) : newHex; + const oct = format ? baselib.formatOctal(newOct) : newOct; + const bin = format ? baselib.formatBinary(newBin) : newBin; + + const trySetInt = (base: 10 | 16 | 8 | 2) => (value: string) => { + if (value === "") { + return setInt(undefined); + } + + const { prefix, validate } = baseConfig[base]; + const unformatted = baselib.unformatNumber(value); + + if (validate(unformatted)) { + setInt(BigInt(`${prefix}${unformatted}`)); + } + }; + + const trySetDec = useCallback((value: string) => trySetInt(10)(value), []); + const trySetHex = useCallback((value: string) => trySetInt(16)(value), []); + const trySetOct = useCallback((value: string) => trySetInt(8)(value), []); + const trySetBin = useCallback((value: string) => trySetInt(2)(value), []); + + const onDecChange: InputProps["onChange"] = ({ currentTarget: { value } }) => trySetDec(value); + const onHexChange: InputProps["onChange"] = ({ currentTarget: { value } }) => trySetHex(value); + const onOctChange: InputProps["onChange"] = ({ currentTarget: { value } }) => trySetOct(value); + const onBinChange: InputProps["onChange"] = ({ currentTarget: { value } }) => trySetBin(value); + + const formatNumberIcon = useMemo(() => , []); + + const formatNumberConfig = useMemo( + () => ( + + } + /> + ), + [format, formatNumberIcon] + ); + + const decPasteButton = useMemo(() => , [trySetDec]); + const hexPasteButton = useMemo(() => , [trySetHex]); + const octPasteButton = useMemo(() => , [trySetOct]); + const binPasteButton = useMemo(() => , [trySetBin]); + + const decControl = ; + const hexControl = ; + const octControl = ; + const binControl = ; + + return ( + + + + + + + + + + + + + + + + + + ); +} diff --git a/app/converters/page.tsx b/app/converters/page.tsx new file mode 100644 index 0000000..1e70aba --- /dev/null +++ b/app/converters/page.tsx @@ -0,0 +1,11 @@ +import { toolGroups } from "@/config/tools"; +import { PageRootSection } from "@/components/page-root-section"; +import { ToolCards } from "@/components/tool-cards"; + +export default function Page() { + return ( + + + + ); +} diff --git a/app/encoders-decoders/base64/layout.tsx b/app/encoders-decoders/base64/layout.tsx new file mode 100644 index 0000000..4b34446 --- /dev/null +++ b/app/encoders-decoders/base64/layout.tsx @@ -0,0 +1,11 @@ +import { Metadata } from "next"; + +import { toolGroups } from "@/config/tools"; + +export const metadata: Metadata = { + title: toolGroups.encodersDecoders.tools.base64.longTitle, +}; + +export default function Layout({ children }: { children: React.ReactNode }) { + return children; +} diff --git a/app/encoders-decoders/base64/page.tsx b/app/encoders-decoders/base64/page.tsx new file mode 100644 index 0000000..2613516 --- /dev/null +++ b/app/encoders-decoders/base64/page.tsx @@ -0,0 +1,98 @@ +"use client"; + +import { useCallback, useMemo, useState } from "react"; +import { decode, encode, isValid } from "js-base64"; + +import { toolGroups } from "@/config/tools"; +import { Textarea, TextareaProps } from "@/components/ui/textarea"; +import { ClearButton } from "@/components/buttons/clear"; +import { CopyButton } from "@/components/buttons/copy"; +import { FileButton } from "@/components/buttons/file"; +import { PasteButton } from "@/components/buttons/paste"; +import { ControlMenu } from "@/components/control-menu"; +import { PageRootSection } from "@/components/page-root-section"; +import { PageSection } from "@/components/page-section"; + +export default function Page() { + const [decoded, setDecoded] = useState("😀😂🤣"); + const [encoded, setEncoded] = useState("8J+YgPCfmILwn6Sj"); + + const setDecodedReactively = useCallback((text: string) => { + setDecoded(text); + setEncoded(encode(text)); + }, []); + + const setEncodedReactively = useCallback((text: string) => { + setEncoded(text); + + const newDecoded = decode(text); + + if (isValid(text) && !newDecoded.includes("�")) { + setDecoded(newDecoded); + } else { + setDecoded(""); + } + }, []); + + const clearBoth = useCallback(() => { + setDecoded(""); + setEncoded(""); + }, []); + + const onDecodedChange: TextareaProps["onChange"] = ({ currentTarget: { value } }) => + setDecodedReactively(value); + + const onEncodedChange: TextareaProps["onChange"] = ({ currentTarget: { value } }) => + setEncodedReactively(value); + + const decodedPasteButton = useMemo( + () => , + [setDecodedReactively] + ); + + const encodedPasteButton = useMemo( + () => , + [setEncodedReactively] + ); + + const decodedFileButton = useMemo( + () => ( + + ), + [setDecodedReactively] + ); + + const encodedFileButton = useMemo( + () => ( + + ), + [setEncodedReactively] + ); + + const decodedCopyButton = useMemo(() => , [decoded]); + const encodedCopyButton = useMemo(() => , [encoded]); + + const clearButton = useMemo( + () => , + [clearBoth] + ); + + const decodedControl = ( + + ); + + const encodedControl = ( + + ); + + return ( + + +