From 2d6c1685ced786845a8b1a1109f71d9ff2d080ce Mon Sep 17 00:00:00 2001 From: shawn Date: Thu, 18 Jun 2026 23:27:55 +0800 Subject: [PATCH 01/10] =?UTF-8?q?feat:=20UI=20=E9=87=8D=E5=81=9A=E8=BF=81?= =?UTF-8?q?=E7=A7=BB=E5=88=B0=20Tailwind=20v4=20+=20shadcn(Base=20UI=20?= =?UTF-8?q?=E5=90=8E=E7=AB=AF)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 接入 Tailwind v4(@tailwindcss/vite,CSS-first,过渡期只引 theme+utilities 层、不带 preflight);新增 clsx/tailwind-merge/cn + class-variance-authority + @/ 别名 - 设计令牌重建为 zinc + 蓝强调(亮暗平等),新增字重/行高/字距令牌;index.css 用 @theme inline 把 shadcn 色名映射到语义令牌 - 数据/代码字体换 JetBrains Mono(替换 Source Code Pro),同步 CodeMirror 主题 - 共享原语 shadcn 化:Button(cva)/Input/Select/Checkbox/Tabs/Field/Modal,门面 API 不变 - 连接弹窗做大做舒展(lg=760)+ 所有弹窗加宽(默认 md=660)+ footer .spacer 兼容 - 侧栏目录树/分组头圆角药丸 hover - 修复亮色白底白字:theme-polish 通用元素规则收进 @layer base,避免压过 Tailwind 工具类 --- electron.vite.config.ts | 8 +- package.json | 9 +- pnpm-lock.yaml | 387 +++++++++++++++++- src/renderer/src/components/common/Button.tsx | 54 ++- src/renderer/src/components/common/Modal.tsx | 33 +- .../src/components/settings/SettingsModal.tsx | 1 - .../src/components/sidebar/ConnectionForm.tsx | 54 ++- src/renderer/src/components/ui/Checkbox.tsx | 23 +- src/renderer/src/components/ui/Field.tsx | 31 +- src/renderer/src/components/ui/Input.tsx | 18 +- src/renderer/src/components/ui/Select.tsx | 32 +- src/renderer/src/components/ui/Tabs.tsx | 18 +- src/renderer/src/lib/pineEditorTheme.ts | 48 +-- src/renderer/src/lib/utils.ts | 11 + src/renderer/src/main.tsx | 17 +- src/renderer/src/styles/base-ui.css | 6 +- src/renderer/src/styles/explorer.css | 4 + src/renderer/src/styles/index.css | 55 ++- src/renderer/src/styles/modals-forms.css | 6 +- src/renderer/src/styles/theme-polish.css | 79 ++-- src/renderer/src/styles/tokens.css | 174 ++++---- tsconfig.web.json | 3 +- 22 files changed, 795 insertions(+), 276 deletions(-) create mode 100644 src/renderer/src/lib/utils.ts diff --git a/electron.vite.config.ts b/electron.vite.config.ts index 24fc94f..8d630dc 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -1,6 +1,7 @@ import { resolve } from 'path' import { defineConfig } from 'electron-vite' import react from '@vitejs/plugin-react' +import tailwindcss from '@tailwindcss/vite' export default defineConfig({ main: { @@ -50,10 +51,13 @@ export default defineConfig({ resolve: { alias: { '@renderer': resolve('src/renderer/src'), - '@shared': resolve('src/shared') + '@shared': resolve('src/shared'), + // shadcn convention: '@' points at the renderer source root, mirrored + // in tsconfig.web.json#paths so generated components resolve '@/...'. + '@': resolve('src/renderer/src') } }, - plugins: [react()], + plugins: [react(), tailwindcss()], build: { rollupOptions: { input: { index: resolve(__dirname, 'src/renderer/index.html') } diff --git a/package.json b/package.json index fb01dd5..93095dd 100644 --- a/package.json +++ b/package.json @@ -35,21 +35,25 @@ "@codemirror/search": "^6.7.0", "@codemirror/state": "^6.6.0", "@codemirror/view": "^6.43.0", - "@fontsource/source-code-pro": "^5.2.7", + "@fontsource/jetbrains-mono": "^5.2.8", "@mongosh/async-rewriter2": "^2.4.15", "@tanstack/react-virtual": "^3.10.9", "@uiw/codemirror-themes": "^4.25.10", "@uiw/react-codemirror": "^4.23.6", "bson": "^6.9.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "i18next": "^26.3.1", "lucide-react": "^1.17.0", "mongodb": "^6.10.0", "prettier": "^3.8.3", "react-i18next": "^17.0.8", - "ssh2": "^1.16.0" + "ssh2": "^1.16.0", + "tailwind-merge": "^3.6.0" }, "devDependencies": { "@babel/preset-typescript": "^7.29.7", + "@tailwindcss/vite": "^4.3.1", "@types/node": "^22.9.0", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", @@ -63,6 +67,7 @@ "mongodb-memory-server": "^11.2.0", "react": "^18.3.1", "react-dom": "^18.3.1", + "tailwindcss": "^4.3.1", "typescript": "^5.6.3", "vite": "^7.3.5", "vitest": "^4.1.8", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1401241..d4442dc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,9 +35,9 @@ importers: '@codemirror/view': specifier: ^6.43.0 version: 6.43.0 - '@fontsource/source-code-pro': - specifier: ^5.2.7 - version: 5.2.7 + '@fontsource/jetbrains-mono': + specifier: ^5.2.8 + version: 5.2.8 '@mongosh/async-rewriter2': specifier: ^2.4.15 version: 2.4.15 @@ -53,6 +53,12 @@ importers: bson: specifier: ^6.9.0 version: 6.10.4 + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 i18next: specifier: ^26.3.1 version: 26.3.1(typescript@5.9.3) @@ -71,10 +77,16 @@ importers: ssh2: specifier: ^1.16.0 version: 1.17.0 + tailwind-merge: + specifier: ^3.6.0 + version: 3.6.0 devDependencies: '@babel/preset-typescript': specifier: ^7.29.7 version: 7.29.7(@babel/core@7.29.7) + '@tailwindcss/vite': + specifier: ^4.3.1 + version: 4.3.1(vite@7.3.5(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0)) '@types/node': specifier: ^22.9.0 version: 22.19.19 @@ -89,7 +101,7 @@ importers: version: 1.15.5 '@vitejs/plugin-react': specifier: ^5.2.0 - version: 5.2.0(vite@7.3.5(@types/node@22.19.19)(jiti@2.7.0)) + version: 5.2.0(vite@7.3.5(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0)) '@vitest/coverage-v8': specifier: ^4.1.8 version: 4.1.8(vitest@4.1.8) @@ -101,7 +113,7 @@ importers: version: 26.8.1(electron-builder-squirrel-windows@26.8.1) electron-vite: specifier: ^5.0.0 - version: 5.0.0(vite@7.3.5(@types/node@22.19.19)(jiti@2.7.0)) + version: 5.0.0(vite@7.3.5(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0)) exceljs: specifier: ^4.4.0 version: 4.4.0 @@ -114,15 +126,18 @@ importers: react-dom: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) + tailwindcss: + specifier: ^4.3.1 + version: 4.3.1 typescript: specifier: ^5.6.3 version: 5.9.3 vite: specifier: ^7.3.5 - version: 7.3.5(@types/node@22.19.19)(jiti@2.7.0) + version: 7.3.5(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0) vitest: specifier: ^4.1.8 - version: 4.1.8(@types/node@22.19.19)(@vitest/coverage-v8@4.1.8)(vite@7.3.5(@types/node@22.19.19)(jiti@2.7.0)) + version: 4.1.8(@types/node@22.19.19)(@vitest/coverage-v8@4.1.8)(vite@7.3.5(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0)) zustand: specifier: ^5.0.1 version: 5.0.14(@types/react@18.3.30)(react@18.3.1)(use-sync-external-store@1.6.0(react@18.3.1)) @@ -736,8 +751,8 @@ packages: '@floating-ui/utils@0.2.11': resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} - '@fontsource/source-code-pro@5.2.7': - resolution: {integrity: sha512-7papq9TH94KT+S5VSY8cU7tFmwuGkIe3qxXRMscuAXH6AjMU+KJI75f28FzgBVDrlMfA0jjlTV4/x5+H5o/5EQ==} + '@fontsource/jetbrains-mono@5.2.8': + resolution: {integrity: sha512-6w8/SG4kqvIMu7xd7wt6x3idn1Qux3p9N62s6G3rfldOUYHpWcc2FKrqf+Vo44jRvqWj2oAtTHrZXEP23oSKwQ==} '@isaacs/fs-minipass@4.0.1': resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} @@ -942,6 +957,100 @@ packages: resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} engines: {node: '>=10'} + '@tailwindcss/node@4.3.1': + resolution: {integrity: sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A==} + + '@tailwindcss/oxide-android-arm64@4.3.1': + resolution: {integrity: sha512-SVlyf61g374l5cHyg8x9kf5xmLcOaxvOTsbsqDnSsDJaKOEFZ7GCvi84VAVGpxojYOs1+3K6M0UjXfqPU8vmOQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.3.1': + resolution: {integrity: sha512-hVnWLwv+e/l7c4WKyVtHVrIPvYdqWHjRB3MDIqARynzFtnQg85kmQEFCbV9Ja0VVx4xXTIiDWY60Y7iz/iNoDA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.3.1': + resolution: {integrity: sha512-Cf7abu0WVgbhU7ANgPUnSAvm7nCvMweusHb8FnaHlLfv/Caq4GYaEZg7ZImzzmjx4lIAfuS8q+eLIS7A7IzxIg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.3.1': + resolution: {integrity: sha512-ZZqzX2Y+GXtXXfqSfpJhDm60OoZfvLHLCgm+J7NVqgHHJjG/m9ugZI77RwTsVd4fnBJuCFP6Ae6kTJb71UdS8g==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1': + resolution: {integrity: sha512-/Ah/xik0LaMYfv9DZ0S/t4pBlBNYOcqtRwusjgovHkvT8ixueWCLyJjsaF5kQIckjb4IT8Q6K6p/iPmZMixYgg==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.1': + resolution: {integrity: sha512-gqdFoVJlw444GvpnheZLHmvTzSxI/cOUUh2KSNejQjTcYkW062SVD+En0rUgD+QV91bz1XGIGtt1HJd48xUGbQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.3.1': + resolution: {integrity: sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.3.1': + resolution: {integrity: sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.3.1': + resolution: {integrity: sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.3.1': + resolution: {integrity: sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.1': + resolution: {integrity: sha512-aiNvSq9BsVk8V513lDKlrCFAgf8qBMPZTpgEhInL+NwQqs97mYmupVMrPrgBBSL8Pv/0zXu9MrMF9rMun1ZeNg==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.3.1': + resolution: {integrity: sha512-xDEyu1rg290472FEGaKHnzyDyh5QH+AlWvsU5hMoMtPpzmKlRI0jaYKCgSHDYtaQWZOYbMaduSyCwFwY4n1HmA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.3.1': + resolution: {integrity: sha512-yVPyo8RNkabVr3O2EhHEE0Rewu7YKzc1DhIqfL46LKveFrmu9XbDazNOJY7/GRuvw1h6u3utWnR29H/p5JPlgA==} + engines: {node: '>= 20'} + + '@tailwindcss/vite@4.3.1': + resolution: {integrity: sha512-hItDHuIIlEV61R+faXu66s1K36aTurO/Qw0e45Vskz57gXl9pWOT6eg3zmcEui6CZXddbN7zd41bwmvag4JGwQ==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 || ^8 + '@tanstack/react-virtual@3.14.1': resolution: {integrity: sha512-VJCw2DzKd16eMFoijSYpyAnwy0o8lzHTbYlGQOaSVHCWNptiZj4jlIsTE/LT7tzEHAMMRJC1ZIQA4OxAc99jLg==} peerDependencies: @@ -1386,6 +1495,9 @@ packages: resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} engines: {node: '>=8'} + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + cli-truncate@2.1.0: resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} engines: {node: '>=8'} @@ -1397,6 +1509,10 @@ packages: clone-response@1.0.3: resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + codemirror@6.0.2: resolution: {integrity: sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==} @@ -1503,6 +1619,10 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + detect-node@2.1.0: resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} @@ -1578,6 +1698,10 @@ packages: end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + enhanced-resolve@5.21.6: + resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==} + engines: {node: '>=10.13.0'} + env-paths@2.2.1: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} @@ -1954,6 +2078,80 @@ packages: lie@3.3.0: resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + listenercount@1.0.1: resolution: {integrity: sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==} @@ -2553,6 +2751,16 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} + tailwind-merge@3.6.0: + resolution: {integrity: sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==} + + tailwindcss@4.3.1: + resolution: {integrity: sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q==} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + tar-stream@2.2.0: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} engines: {node: '>=6'} @@ -3465,7 +3673,7 @@ snapshots: '@floating-ui/utils@0.2.11': {} - '@fontsource/source-code-pro@5.2.7': {} + '@fontsource/jetbrains-mono@5.2.8': {} '@isaacs/fs-minipass@4.0.1': dependencies: @@ -3620,6 +3828,74 @@ snapshots: dependencies: defer-to-connect: 2.0.1 + '@tailwindcss/node@4.3.1': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.21.6 + jiti: 2.7.0 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.3.1 + + '@tailwindcss/oxide-android-arm64@4.3.1': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.3.1': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.3.1': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.3.1': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.3.1': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.1': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.3.1': + optional: true + + '@tailwindcss/oxide@4.3.1': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.3.1 + '@tailwindcss/oxide-darwin-arm64': 4.3.1 + '@tailwindcss/oxide-darwin-x64': 4.3.1 + '@tailwindcss/oxide-freebsd-x64': 4.3.1 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.1 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.1 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.1 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.1 + '@tailwindcss/oxide-linux-x64-musl': 4.3.1 + '@tailwindcss/oxide-wasm32-wasi': 4.3.1 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.1 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.1 + + '@tailwindcss/vite@4.3.1(vite@7.3.5(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0))': + dependencies: + '@tailwindcss/node': 4.3.1 + '@tailwindcss/oxide': 4.3.1 + tailwindcss: 4.3.1 + vite: 7.3.5(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0) + '@tanstack/react-virtual@3.14.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@tanstack/virtual-core': 3.16.1 @@ -3771,7 +4047,7 @@ snapshots: - '@codemirror/lint' - '@codemirror/search' - '@vitejs/plugin-react@5.2.0(vite@7.3.5(@types/node@22.19.19)(jiti@2.7.0))': + '@vitejs/plugin-react@5.2.0(vite@7.3.5(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0))': dependencies: '@babel/core': 7.29.7 '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7) @@ -3779,7 +4055,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-rc.3 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 - vite: 7.3.5(@types/node@22.19.19)(jiti@2.7.0) + vite: 7.3.5(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0) transitivePeerDependencies: - supports-color @@ -3795,7 +4071,7 @@ snapshots: obug: 2.1.2 std-env: 4.1.0 tinyrainbow: 3.1.0 - vitest: 4.1.8(@types/node@22.19.19)(@vitest/coverage-v8@4.1.8)(vite@7.3.5(@types/node@22.19.19)(jiti@2.7.0)) + vitest: 4.1.8(@types/node@22.19.19)(@vitest/coverage-v8@4.1.8)(vite@7.3.5(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0)) '@vitest/expect@4.1.8': dependencies: @@ -3806,13 +4082,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.8(vite@7.3.5(@types/node@22.19.19)(jiti@2.7.0))': + '@vitest/mocker@4.1.8(vite@7.3.5(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0))': dependencies: '@vitest/spy': 4.1.8 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.5(@types/node@22.19.19)(jiti@2.7.0) + vite: 7.3.5(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0) '@vitest/pretty-format@4.1.8': dependencies: @@ -4152,6 +4428,10 @@ snapshots: ci-info@4.4.0: {} + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + cli-truncate@2.1.0: dependencies: slice-ansi: 3.0.0 @@ -4168,6 +4448,8 @@ snapshots: dependencies: mimic-response: 1.0.1 + clsx@2.1.1: {} + codemirror@6.0.2: dependencies: '@codemirror/autocomplete': 6.20.2 @@ -4272,6 +4554,8 @@ snapshots: delayed-stream@1.0.0: {} + detect-libc@2.1.2: {} + detect-node@2.1.0: optional: true @@ -4365,7 +4649,7 @@ snapshots: electron-to-chromium@1.5.364: {} - electron-vite@5.0.0(vite@7.3.5(@types/node@22.19.19)(jiti@2.7.0)): + electron-vite@5.0.0(vite@7.3.5(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0)): dependencies: '@babel/core': 7.29.7 '@babel/plugin-transform-arrow-functions': 7.29.7(@babel/core@7.29.7) @@ -4373,7 +4657,7 @@ snapshots: esbuild: 0.25.12 magic-string: 0.30.21 picocolors: 1.1.1 - vite: 7.3.5(@types/node@22.19.19)(jiti@2.7.0) + vite: 7.3.5(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0) transitivePeerDependencies: - supports-color @@ -4403,6 +4687,11 @@ snapshots: dependencies: once: 1.4.0 + enhanced-resolve@5.21.6: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + env-paths@2.2.1: {} err-code@2.0.3: {} @@ -4849,6 +5138,55 @@ snapshots: dependencies: immediate: 3.0.6 + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + listenercount@1.0.1: {} locate-path@5.0.0: @@ -5425,6 +5763,12 @@ snapshots: dependencies: has-flag: 4.0.0 + tailwind-merge@3.6.0: {} + + tailwindcss@4.3.1: {} + + tapable@2.3.3: {} + tar-stream@2.2.0: dependencies: bl: 4.1.0 @@ -5565,7 +5909,7 @@ snapshots: extsprintf: 1.4.1 optional: true - vite@7.3.5(@types/node@22.19.19)(jiti@2.7.0): + vite@7.3.5(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.4) @@ -5577,11 +5921,12 @@ snapshots: '@types/node': 22.19.19 fsevents: 2.3.3 jiti: 2.7.0 + lightningcss: 1.32.0 - vitest@4.1.8(@types/node@22.19.19)(@vitest/coverage-v8@4.1.8)(vite@7.3.5(@types/node@22.19.19)(jiti@2.7.0)): + vitest@4.1.8(@types/node@22.19.19)(@vitest/coverage-v8@4.1.8)(vite@7.3.5(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0)): dependencies: '@vitest/expect': 4.1.8 - '@vitest/mocker': 4.1.8(vite@7.3.5(@types/node@22.19.19)(jiti@2.7.0)) + '@vitest/mocker': 4.1.8(vite@7.3.5(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0)) '@vitest/pretty-format': 4.1.8 '@vitest/runner': 4.1.8 '@vitest/snapshot': 4.1.8 @@ -5598,7 +5943,7 @@ snapshots: tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 7.3.5(@types/node@22.19.19)(jiti@2.7.0) + vite: 7.3.5(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.19.19 diff --git a/src/renderer/src/components/common/Button.tsx b/src/renderer/src/components/common/Button.tsx index 25e97bc..057948b 100644 --- a/src/renderer/src/components/common/Button.tsx +++ b/src/renderer/src/components/common/Button.tsx @@ -1,6 +1,35 @@ import { type ButtonHTMLAttributes, type ReactNode } from 'react' +import { cva, type VariantProps } from 'class-variance-authority' +import { cn } from '@renderer/lib/utils' -export type ButtonVariant = 'default' | 'primary' | 'ghost' | 'danger' +/** + * The standard text action button — toolbar actions and dialog footers. + * + * shadcn-style: a ` diff --git a/src/renderer/src/components/common/Modal.tsx b/src/renderer/src/components/common/Modal.tsx index 854ac3a..711a441 100644 --- a/src/renderer/src/components/common/Modal.tsx +++ b/src/renderer/src/components/common/Modal.tsx @@ -1,6 +1,7 @@ import { useRef, type ReactNode } from 'react' import { useTranslation } from 'react-i18next' import { Dialog, DialogClose, DialogTitle } from '@renderer/components/ui/Dialog' +import { cn } from '@renderer/lib/utils' // Focusable controls we want to land initial focus on (scoped to the body, which // excludes the header ✕). Covers native fields plus the ui/* primitives, whose @@ -12,39 +13,53 @@ interface ModalProps { onClose: () => void children: ReactNode footer?: ReactNode + /** Width preset. `small` is kept for back-compat (= 'sm'). Default 'md'. */ small?: boolean + size?: 'sm' | 'md' | 'lg' } /** * Minimal accessible modal. Public API unchanged (consumers conditionally mount - * it, so mount = open); internally backed by Base UI Dialog, which provides Esc / - * outside-press dismissal, focus trap+restore, and auto aria-labelledby wiring - * from `Dialog.Title` — so the old keydown listener and backdrop handler are gone. + * it, so mount = open); internally backed by Base UI Dialog (Esc / outside-press + * dismissal, focus trap+restore, aria wiring). shadcn-style Tailwind shell: a + * roomy elevated card on a dimmed backdrop. Positioning + backdrop come from + * ui/Dialog. Three width presets keep dense dialogs tight and forms spacious. */ -export function Modal({ title, onClose, children, footer, small }: ModalProps): JSX.Element { +export function Modal({ title, onClose, children, footer, small, size }: ModalProps): JSX.Element { const { t } = useTranslation() const bodyRef = useRef(null) + const width = small ? 'sm' : (size ?? 'md') return ( { if (!open) onClose() }} - className={small ? 'modal small' : 'modal'} + className={cn( + 'flex max-h-[88vh] max-w-[92vw] flex-col overflow-hidden rounded-xl border border-[var(--border-strong)] bg-card text-foreground shadow-[0_24px_64px_rgba(0,0,0,0.5)]', + width === 'sm' && 'w-[480px]', + width === 'md' && 'w-[660px]', + width === 'lg' && 'w-[760px]' + )} // Focus the first field in the body on open (preserving the old per-input // autoFocus); fall back to Base UI's default if the body has no control. initialFocus={() => bodyRef.current?.querySelector(FOCUSABLE) ?? true} > -
+
}>{title} - +
-
+
{children}
- {footer &&
{footer}
} + {footer && ( +
{footer}
+ )}
) } diff --git a/src/renderer/src/components/settings/SettingsModal.tsx b/src/renderer/src/components/settings/SettingsModal.tsx index 4ebff66..67af4b0 100644 --- a/src/renderer/src/components/settings/SettingsModal.tsx +++ b/src/renderer/src/components/settings/SettingsModal.tsx @@ -25,7 +25,6 @@ export function SettingsModal({ onClose }: { onClose: () => void }): JSX.Element return ( diff --git a/src/renderer/src/components/sidebar/ConnectionForm.tsx b/src/renderer/src/components/sidebar/ConnectionForm.tsx index 6751f66..1e6500d 100644 --- a/src/renderer/src/components/sidebar/ConnectionForm.tsx +++ b/src/renderer/src/components/sidebar/ConnectionForm.tsx @@ -18,6 +18,7 @@ import { Field } from '@renderer/components/ui/Field' import { Input } from '@renderer/components/ui/Input' import { Select } from '@renderer/components/ui/Select' import { Checkbox } from '@renderer/components/ui/Checkbox' +import { cn } from '@renderer/lib/utils' import { parseMongoUri, PRESET_COLORS } from '@renderer/lib/connectionUri' type Tab = 'general' | 'auth' | 'ssh' | 'tls' @@ -446,6 +447,7 @@ export function ConnectionForm({ editing, onClose }: ConnectionFormProps): JSX.E {test && ( {test.ok ? [ @@ -466,11 +472,11 @@ export function ConnectionForm({ editing, onClose }: ConnectionFormProps): JSX.E )} {sshError && ( - + {sshError} )} - + @@ -488,10 +494,10 @@ export function ConnectionForm({ editing, onClose }: ConnectionFormProps): JSX.E {/* From URL / To URL: two independent one-way helpers, each in its own popup. From URL parses a pasted string INTO the fields; To URL exports the current fields OUT as a connection string. */} -
+
- - {parseNote && {parseNote}} + {parseNote && {parseNote}}
value={tab} onChange={setTab} + className="mb-5" items={[ { value: 'general', label: tFn('connection.tab.general') }, { value: 'auth', label: tFn('connection.tab.auth') }, @@ -524,21 +535,34 @@ export function ConnectionForm({ editing, onClose }: ConnectionFormProps): JSX.E setName(e.target.value)} placeholder="My MongoDB" /> -
- -
+
+ +
{PRESET_COLORS.map((c) => (