diff --git a/.env.example b/.env.example index bc9086d..df7acb0 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,2 @@ -NODE_ENV= \ No newline at end of file +NODE_ENV= +API_URL=https://kevisual.xiongxiao.me \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..501a050 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +node_modules + +.env +!.env*example +dist +.next +out \ No newline at end of file diff --git a/next.config.ts b/next.config.ts index bd40853..c310583 100644 --- a/next.config.ts +++ b/next.config.ts @@ -10,6 +10,10 @@ const nextConfig: NextConfig = { distDir: 'dist', basePath: basePath, trailingSlash: true, + transpilePackages: ['@kevisual/api'], + images: { + unoptimized: true, + }, }; export default nextConfig; diff --git a/package.json b/package.json index 55abc2a..e9b5a1b 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,20 @@ { - "name": "@kevisual/next-simple-template", + "name": "@kevisual/center", "version": "0.1.0", - "basename": "/root/next-simple-template", + "basename": "/root/center", "scripts": { "dev": "next dev", "build": "next build", "start": "next start", - "pub": "ev deploy ./dist -k next-simple-template -v 0.1.0 -u -y y", + "pub": "ev deploy ./dist -k center -v 0.1.0 -u -y y", "ui": "pnpm dlx shadcn@latest add " }, "dependencies": { - "@kevisual/query": "^0.0.35", - "@kevisual/router": "^0.0.53", + "@ant-design/icons": "^6.1.0", + "@kevisual/api": "^0.0.26", + "@kevisual/cache": "^0.0.5", + "@kevisual/query": "^0.0.38", + "@kevisual/router": "^0.0.60", "@radix-ui/react-checkbox": "^1.3.3", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", @@ -20,25 +23,33 @@ "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-switch": "^1.2.6", "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tooltip": "^1.2.8", - "antd": "^6.2.0", + "antd": "^6.2.1", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", + "copy-to-clipboard": "^3.3.3", + "date-fns": "^4.1.0", + "dayjs": "^1.11.19", "dotenv": "^17.2.3", - "es-toolkit": "^1.43.0", + "es-toolkit": "^1.44.0", "idb-keyval": "^6.2.2", "lucide-react": "^0.562.0", - "next": "16.1.2", + "marked": "^17.0.1", + "next": "16.1.4", "react": "19.2.3", + "react-day-picker": "^9.13.0", "react-dom": "19.2.3", + "react-hook-form": "^7.71.1", "sonner": "^2.0.7", "tailwind-merge": "^3.4.0", "valtio": "^2.3.0", "zustand": "^5.0.10" }, "devDependencies": { + "@kevisual/types": "^0.0.12", "@tailwindcss/postcss": "^4", "@types/node": "^25", "@types/react": "^19", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1414a97..f099063 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,12 +8,21 @@ importers: .: dependencies: + '@ant-design/icons': + specifier: ^6.1.0 + version: 6.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@kevisual/api': + specifier: ^0.0.26 + version: 0.0.26 + '@kevisual/cache': + specifier: ^0.0.5 + version: 0.0.5 '@kevisual/query': - specifier: ^0.0.35 - version: 0.0.35 + specifier: ^0.0.38 + version: 0.0.38 '@kevisual/router': - specifier: ^0.0.53 - version: 0.0.53 + specifier: ^0.0.60 + version: 0.0.60 '@radix-ui/react-checkbox': specifier: ^1.3.3 version: 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -38,6 +47,9 @@ importers: '@radix-ui/react-slot': specifier: ^1.2.4 version: 1.2.4(@types/react@19.2.7)(react@19.2.3) + '@radix-ui/react-switch': + specifier: ^1.2.6 + version: 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-tabs': specifier: ^1.1.13 version: 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -45,8 +57,8 @@ importers: specifier: ^1.2.8 version: 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) antd: - specifier: ^6.2.0 - version: 6.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + specifier: ^6.2.1 + version: 6.2.1(date-fns@4.1.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -56,30 +68,45 @@ importers: cmdk: specifier: ^1.1.1 version: 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + copy-to-clipboard: + specifier: ^3.3.3 + version: 3.3.3 + date-fns: + specifier: ^4.1.0 + version: 4.1.0 + dayjs: + specifier: ^1.11.19 + version: 1.11.19 dotenv: specifier: ^17.2.3 version: 17.2.3 es-toolkit: - specifier: ^1.43.0 - version: 1.43.0 + specifier: ^1.44.0 + version: 1.44.0 idb-keyval: specifier: ^6.2.2 version: 6.2.2 - jotai: - specifier: ^2.16.1 - version: 2.16.1(@types/react@19.2.7)(react@19.2.3) lucide-react: specifier: ^0.562.0 version: 0.562.0(react@19.2.3) + marked: + specifier: ^17.0.1 + version: 17.0.1 next: - specifier: 16.1.1 - version: 16.1.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + specifier: 16.1.4 + version: 16.1.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react: specifier: 19.2.3 version: 19.2.3 + react-day-picker: + specifier: ^9.13.0 + version: 9.13.0(react@19.2.3) react-dom: specifier: 19.2.3 version: 19.2.3(react@19.2.3) + react-hook-form: + specifier: ^7.71.1 + version: 7.71.1(react@19.2.3) sonner: specifier: ^2.0.7 version: 2.0.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -93,6 +120,9 @@ importers: specifier: ^5.0.10 version: 5.0.10(@types/react@19.2.7)(react@19.2.3) devDependencies: + '@kevisual/types': + specifier: ^0.0.12 + version: 0.0.12 '@tailwindcss/postcss': specifier: ^4 version: 4.1.18 @@ -130,8 +160,8 @@ packages: react: '>=18' react-dom: '>=18' - '@ant-design/cssinjs@2.0.2': - resolution: {integrity: sha512-7KDVIigtqlamOLtJ0hbjECX/sDGDaJXsM/KHala8I/1E4lpl9RAO585kbVvh/k1rIrFAV6JeGkXmdWyYj9XvuA==} + '@ant-design/cssinjs@2.0.3': + resolution: {integrity: sha512-HAo8SZ3a6G8v6jT0suCz1270na6EA3obeJWM4uzRijBhdwdoMAXWK2f4WWkwB28yUufsfk3CAhN1coGPQq4kNQ==} peerDependencies: react: '>=16.0.0' react-dom: '>=16.0.0' @@ -160,6 +190,9 @@ packages: resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} engines: {node: '>=6.9.0'} + '@date-fns/tz@1.4.1': + resolution: {integrity: sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==} + '@emnapi/runtime@1.8.1': resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} @@ -337,59 +370,74 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@kevisual/query@0.0.35': - resolution: {integrity: sha512-80dyy2LMCmEC72g+X4QWUKlZErhawQPgnGSBNR4yhrBcFgHIJQ14LR1Z+bS5S1I7db+1PDNpaxBTjIaoYoXunw==} + '@kevisual/api@0.0.26': + resolution: {integrity: sha512-u40PNMeVoWfUrUXWSEOc6/aacmt+flq7gE6kdmJJpgc9rfn8I6mXWlp3z0FPrAuqJopKAraiU9To+U72TL8U9g==} - '@kevisual/router@0.0.53': - resolution: {integrity: sha512-Bw9xYVWyxRhd230nF1ac7cyvzWDYKI/3V+Fr1Ew1Bfr0Ey8KuWb1MgPPopHkRHCCcUcysLtWXfu/JRiTAoBmGA==} + '@kevisual/cache@0.0.5': + resolution: {integrity: sha512-fgtUYGUUq/DY0KFV4CkWszNqvQUaA8XvMTUjoR9ZXRpau5IIDolD/Wen2TFsZ7G3Rfy+lef5dnaiZVDkZwdVKg==} - '@next/env@16.1.1': - resolution: {integrity: sha512-3oxyM97Sr2PqiVyMyrZUtrtM3jqqFxOQJVuKclDsgj/L728iZt/GyslkN4NwarledZATCenbk4Offjk1hQmaAA==} + '@kevisual/js-filter@0.0.5': + resolution: {integrity: sha512-+S+Sf3K/aP6XtZI2s7TgKOr35UuvUvtpJ9YDW30a+mY0/N8gRuzyKhieBzQN7Ykayzz70uoMavBXut2rUlLgzw==} - '@next/swc-darwin-arm64@16.1.1': - resolution: {integrity: sha512-JS3m42ifsVSJjSTzh27nW+Igfha3NdBOFScr9C80hHGrWx55pTrVL23RJbqir7k7/15SKlrLHhh/MQzqBBYrQA==} + '@kevisual/load@0.0.6': + resolution: {integrity: sha512-+3YTFehRcZ1haGel5DKYMUwmi5i6f2psyaPZlfkKU/cOXgkpwoG9/BEqPCnPjicKqqnksEpixVRkyHJ+5bjLVA==} + + '@kevisual/query@0.0.38': + resolution: {integrity: sha512-bfvbSodsZyMfwY+1T2SvDeOCKsT/AaIxlVe0+B1R/fNhlg2MDq2CP0L9HKiFkEm+OXrvXcYDMKPUituVUM5J6Q==} + + '@kevisual/router@0.0.60': + resolution: {integrity: sha512-2v/ZzUstsaq+Uqo+tZX9ys5E+/2erPggCtljv9jTb3NA88ZdHsYUAsd5wUFvLtf9QucpJCzyWEt+InDV/98FKw==} + + '@kevisual/types@0.0.12': + resolution: {integrity: sha512-zJXH2dosir3jVrQ6QG4i0+iLQeT9gJ3H+cKXs8ReWboxBSYzUZO78XssVeVrFPsJ33iaAqo4q3DWbSS1dWGn7Q==} + + '@next/env@16.1.4': + resolution: {integrity: sha512-gkrXnZyxPUy0Gg6SrPQPccbNVLSP3vmW8LU5dwEttEEC1RwDivk8w4O+sZIjFvPrSICXyhQDCG+y3VmjlJf+9A==} + + '@next/swc-darwin-arm64@16.1.4': + resolution: {integrity: sha512-T8atLKuvk13XQUdVLCv1ZzMPgLPW0+DWWbHSQXs0/3TjPrKNxTmUIhOEaoEyl3Z82k8h/gEtqyuoZGv6+Ugawg==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@16.1.1': - resolution: {integrity: sha512-hbyKtrDGUkgkyQi1m1IyD3q4I/3m9ngr+V93z4oKHrPcmxwNL5iMWORvLSGAf2YujL+6HxgVvZuCYZfLfb4bGw==} + '@next/swc-darwin-x64@16.1.4': + resolution: {integrity: sha512-AKC/qVjUGUQDSPI6gESTx0xOnOPQ5gttogNS3o6bA83yiaSZJek0Am5yXy82F1KcZCx3DdOwdGPZpQCluonuxg==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@16.1.1': - resolution: {integrity: sha512-/fvHet+EYckFvRLQ0jPHJCUI5/B56+2DpI1xDSvi80r/3Ez+Eaa2Yq4tJcRTaB1kqj/HrYKn8Yplm9bNoMJpwQ==} + '@next/swc-linux-arm64-gnu@16.1.4': + resolution: {integrity: sha512-POQ65+pnYOkZNdngWfMEt7r53bzWiKkVNbjpmCt1Zb3V6lxJNXSsjwRuTQ8P/kguxDC8LRkqaL3vvsFrce4dMQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@16.1.1': - resolution: {integrity: sha512-MFHrgL4TXNQbBPzkKKur4Fb5ICEJa87HM7fczFs2+HWblM7mMLdco3dvyTI+QmLBU9xgns/EeeINSZD6Ar+oLg==} + '@next/swc-linux-arm64-musl@16.1.4': + resolution: {integrity: sha512-3Wm0zGYVCs6qDFAiSSDL+Z+r46EdtCv/2l+UlIdMbAq9hPJBvGu/rZOeuvCaIUjbArkmXac8HnTyQPJFzFWA0Q==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@16.1.1': - resolution: {integrity: sha512-20bYDfgOQAPUkkKBnyP9PTuHiJGM7HzNBbuqmD0jiFVZ0aOldz+VnJhbxzjcSabYsnNjMPsE0cyzEudpYxsrUQ==} + '@next/swc-linux-x64-gnu@16.1.4': + resolution: {integrity: sha512-lWAYAezFinaJiD5Gv8HDidtsZdT3CDaCeqoPoJjeB57OqzvMajpIhlZFce5sCAH6VuX4mdkxCRqecCJFwfm2nQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@16.1.1': - resolution: {integrity: sha512-9pRbK3M4asAHQRkwaXwu601oPZHghuSC8IXNENgbBSyImHv/zY4K5udBusgdHkvJ/Tcr96jJwQYOll0qU8+fPA==} + '@next/swc-linux-x64-musl@16.1.4': + resolution: {integrity: sha512-fHaIpT7x4gA6VQbdEpYUXRGyge/YbRrkG6DXM60XiBqDM2g2NcrsQaIuj375egnGFkJow4RHacgBOEsHfGbiUw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@16.1.1': - resolution: {integrity: sha512-bdfQkggaLgnmYrFkSQfsHfOhk/mCYmjnrbRCGgkMcoOBZ4n+TRRSLmT/CU5SATzlBJ9TpioUyBW/vWFXTqQRiA==} + '@next/swc-win32-arm64-msvc@16.1.4': + resolution: {integrity: sha512-MCrXxrTSE7jPN1NyXJr39E+aNFBrQZtO154LoCz7n99FuKqJDekgxipoodLNWdQP7/DZ5tKMc/efybx1l159hw==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@16.1.1': - resolution: {integrity: sha512-Ncwbw2WJ57Al5OX0k4chM68DKhEPlrXBaSXDCi2kPi5f4d8b3ejr3RRJGfKBLrn2YJL5ezNS7w2TZLHSti8CMw==} + '@next/swc-win32-x64-msvc@16.1.4': + resolution: {integrity: sha512-JSVlm9MDhmTXw/sO2PE/MRj+G6XOSMZB+BcZ0a7d6KwVFZVpkHcb2okyoYFBaco6LeiL53BBklRlOrDDbOeE5w==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -697,6 +745,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-switch@1.2.6': + resolution: {integrity: sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-tabs@1.1.13': resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==} peerDependencies: @@ -983,8 +1044,8 @@ packages: react: '>=16.9.0' react-dom: '>=16.9.0' - '@rc-component/resize-observer@1.0.1': - resolution: {integrity: sha512-r+w+Mz1EiueGk1IgjB3ptNXLYSLZ5vnEfKHH+gfgj7JMupftyzvUUl3fRcMZe5uMM04x0n8+G2o/c6nlO2+Wag==} + '@rc-component/resize-observer@1.1.1': + resolution: {integrity: sha512-NfXXMmiR+SmUuKE1NwJESzEUYUFWIDUn2uXpxCTOLwiRUUakd62DRNFjRJArgzyFW8S5rsL4aX5XlyIXyC/vRA==} peerDependencies: react: '>=16.9.0' react-dom: '>=16.9.0' @@ -1068,8 +1129,8 @@ packages: react: '*' react-dom: '*' - '@rc-component/trigger@3.8.2': - resolution: {integrity: sha512-I6idYAk8YY3Ly6v5hB7ONqxfdTYTcVNUmV1ZjtSsGH6N/k5tss9+OAtusr+7zdlIcD7TwnlsoB5etfB14ORtMw==} + '@rc-component/trigger@3.9.0': + resolution: {integrity: sha512-X8btpwfrT27AgrZVOz4swclhEHTZcqaHeQMXXBgveagOiakTa36uObXbdwerXffgV8G9dH1fAAE0DHtVQs8EHg==} engines: {node: '>=8.x'} peerDependencies: react: '>=18.0.0' @@ -1196,8 +1257,8 @@ packages: '@types/react@19.2.7': resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} - antd@6.2.0: - resolution: {integrity: sha512-fwETatwHYExjfzKcV41fBtgPo4kp+g+9gp5YOSSGxwnJHljps8TbXef8WP7ZnaOn5dkcA9xIC0TyUecIybBG7w==} + antd@6.2.1: + resolution: {integrity: sha512-ycw/XX7So4MdrwYKGfvZJdkGiCYUOSTebAIi+ejE95WJ138b11oy/iJg7iH0qydaD/B5sFd7Tz8XfPBuW7CRmw==} peerDependencies: react: '>=18.0.0' react-dom: '>=18.0.0' @@ -1232,9 +1293,18 @@ packages: compute-scroll-into-view@3.1.1: resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==} + copy-to-clipboard@3.3.3: + resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} + csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + date-fns-jalali@4.1.0-0: + resolution: {integrity: sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==} + + date-fns@4.1.0: + resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + dayjs@1.11.19: resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==} @@ -1253,8 +1323,11 @@ packages: resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==} engines: {node: '>=10.13.0'} - es-toolkit@1.43.0: - resolution: {integrity: sha512-SKCT8AsWvYzBBuUqMk4NPwFlSdqLpJwmy6AP322ERn8W2YLIB6JBXnwMI2Qsh2gfphT3q7EKAxKb23cvFHFwKA==} + es-toolkit@1.44.0: + resolution: {integrity: sha512-6penXeZalaV88MM3cGkFZZfOoLGWshWWfdy0tWw/RlVVyhvMaWSBTOvXNeiW3e5FwdS5ePW0LGEu17zT139ktg==} + + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} get-nonce@1.0.1: resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} @@ -1263,6 +1336,10 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + hono@4.11.5: + resolution: {integrity: sha512-WemPi9/WfyMwZs+ZUXdiwcCh9Y+m7L+8vki9MzDw3jJ+W9Lc+12HGsd368Qc1vZi1xwW8BWMMsnK5efYKPdt4g==} + engines: {node: '>=16.9.0'} + idb-keyval@6.2.2: resolution: {integrity: sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==} @@ -1273,24 +1350,6 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true - jotai@2.16.1: - resolution: {integrity: sha512-vrHcAbo3P7Br37C8Bv6JshMtlKMPqqmx0DDREtTjT4nf3QChDrYdbH+4ik/9V0cXA57dK28RkJ5dctYvavcIlg==} - engines: {node: '>=12.20.0'} - peerDependencies: - '@babel/core': '>=7.0.0' - '@babel/template': '>=7.0.0' - '@types/react': '>=17.0.0' - react: '>=17.0.0' - peerDependenciesMeta: - '@babel/core': - optional: true - '@babel/template': - optional: true - '@types/react': - optional: true - react: - optional: true - json2mq@0.2.0: resolution: {integrity: sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==} @@ -1364,6 +1423,10 @@ packages: resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} engines: {node: '>= 12.0.0'} + lru-cache@11.2.4: + resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} + engines: {node: 20 || >=22} + lucide-react@0.562.0: resolution: {integrity: sha512-82hOAu7y0dbVuFfmO4bYF1XEwYk/mEbM5E+b1jgci/udUBEE/R7LF5Ip0CCEmXe8AybRM8L+04eP+LGZeDvkiw==} peerDependencies: @@ -1372,13 +1435,23 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + marked@17.0.1: + resolution: {integrity: sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg==} + engines: {node: '>= 20'} + hasBin: true + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - next@16.1.1: - resolution: {integrity: sha512-QI+T7xrxt1pF6SQ/JYFz95ro/mg/1Znk5vBebsWwbpejj1T0A23hO7GYEaVac9QUOT2BIMiuzm0L99ooq7k0/w==} + nanoid@5.1.6: + resolution: {integrity: sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==} + engines: {node: ^18 || >=20} + hasBin: true + + next@16.1.4: + resolution: {integrity: sha512-gKSecROqisnV7Buen5BfjmXAm7Xlpx9o2ueVQRo5DxQcjC8d330dOM1xiGWc2k3Dcnz0In3VybyRPOsudwgiqQ==} engines: {node: '>=20.9.0'} hasBin: true peerDependencies: @@ -1412,11 +1485,23 @@ packages: proxy-compare@3.0.1: resolution: {integrity: sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q==} + react-day-picker@9.13.0: + resolution: {integrity: sha512-euzj5Hlq+lOHqI53NiuNhCP8HWgsPf/bBAVijR50hNaY1XwjKjShAnIe8jm8RD2W9IJUvihDIZ+KrmqfFzNhFQ==} + engines: {node: '>=18'} + peerDependencies: + react: '>=16.8.0' + react-dom@19.2.3: resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} peerDependencies: react: ^19.2.3 + react-hook-form@7.71.1: + resolution: {integrity: sha512-9SUJKCGKo8HUSsCO+y0CtqkqI5nNuaDqTxyqPsZPqIwudpj4rCrAz/jZV+jn57bx5gtZKOh3neQu94DXMc+w5w==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 || ^19 + react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} @@ -1512,6 +1597,9 @@ packages: resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==} engines: {node: '>=12.22'} + toggle-selection@1.0.6: + resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -1586,13 +1674,13 @@ snapshots: '@ant-design/cssinjs-utils@2.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@ant-design/cssinjs': 2.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@ant-design/cssinjs': 2.0.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@babel/runtime': 7.28.6 '@rc-component/util': 1.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - '@ant-design/cssinjs@2.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@ant-design/cssinjs@2.0.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@babel/runtime': 7.28.6 '@emotion/hash': 0.8.0 @@ -1628,6 +1716,8 @@ snapshots: '@babel/runtime@7.28.6': {} + '@date-fns/tz@1.4.1': {} + '@emnapi/runtime@1.8.1': dependencies: tslib: 2.8.1 @@ -1770,36 +1860,60 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@kevisual/query@0.0.35': + '@kevisual/api@0.0.26': + dependencies: + '@kevisual/js-filter': 0.0.5 + '@kevisual/load': 0.0.6 + es-toolkit: 1.44.0 + eventemitter3: 5.0.4 + nanoid: 5.1.6 + + '@kevisual/cache@0.0.5': + dependencies: + idb-keyval: 6.2.2 + lru-cache: 11.2.4 + nanoid: 5.1.6 + + '@kevisual/js-filter@0.0.5': {} + + '@kevisual/load@0.0.6': + dependencies: + eventemitter3: 5.0.4 + + '@kevisual/query@0.0.38': dependencies: tslib: 2.8.1 - '@kevisual/router@0.0.53': {} + '@kevisual/router@0.0.60': + dependencies: + hono: 4.11.5 - '@next/env@16.1.1': {} + '@kevisual/types@0.0.12': {} - '@next/swc-darwin-arm64@16.1.1': + '@next/env@16.1.4': {} + + '@next/swc-darwin-arm64@16.1.4': optional: true - '@next/swc-darwin-x64@16.1.1': + '@next/swc-darwin-x64@16.1.4': optional: true - '@next/swc-linux-arm64-gnu@16.1.1': + '@next/swc-linux-arm64-gnu@16.1.4': optional: true - '@next/swc-linux-arm64-musl@16.1.1': + '@next/swc-linux-arm64-musl@16.1.4': optional: true - '@next/swc-linux-x64-gnu@16.1.1': + '@next/swc-linux-x64-gnu@16.1.4': optional: true - '@next/swc-linux-x64-musl@16.1.1': + '@next/swc-linux-x64-musl@16.1.4': optional: true - '@next/swc-win32-arm64-msvc@16.1.1': + '@next/swc-win32-arm64-msvc@16.1.4': optional: true - '@next/swc-win32-x64-msvc@16.1.1': + '@next/swc-win32-x64-msvc@16.1.4': optional: true '@radix-ui/number@1.1.1': {} @@ -2127,6 +2241,21 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 + '@radix-ui/react-switch@1.2.6(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.7)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.7)(react@19.2.3) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.7)(react@19.2.3) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.7)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -2291,7 +2420,7 @@ snapshots: '@rc-component/dropdown@1.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@rc-component/trigger': 3.8.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/trigger': 3.9.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/util': 1.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) clsx: 2.1.1 react: 19.2.3 @@ -2334,7 +2463,7 @@ snapshots: '@rc-component/input': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/menu': 1.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/textarea': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/trigger': 3.8.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/trigger': 3.9.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/util': 1.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) clsx: 2.1.1 react: 19.2.3 @@ -2344,7 +2473,7 @@ snapshots: dependencies: '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/overflow': 1.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/trigger': 3.8.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/trigger': 3.9.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/util': 1.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) clsx: 2.1.1 react: 19.2.3 @@ -2378,7 +2507,7 @@ snapshots: '@rc-component/overflow@1.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@babel/runtime': 7.28.6 - '@rc-component/resize-observer': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/resize-observer': 1.1.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/util': 1.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) clsx: 2.1.1 react: 19.2.3 @@ -2391,16 +2520,17 @@ snapshots: react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - '@rc-component/picker@1.9.0(dayjs@1.11.19)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@rc-component/picker@1.9.0(date-fns@4.1.0)(dayjs@1.11.19)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@rc-component/overflow': 1.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/resize-observer': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/trigger': 3.8.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/resize-observer': 1.1.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/trigger': 3.9.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/util': 1.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) clsx: 2.1.1 react: 19.2.3 react-dom: 19.2.3(react@19.2.3) optionalDependencies: + date-fns: 4.1.0 dayjs: 1.11.19 '@rc-component/portal@2.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': @@ -2430,7 +2560,7 @@ snapshots: react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - '@rc-component/resize-observer@1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@rc-component/resize-observer@1.1.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@rc-component/util': 1.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react: 19.2.3 @@ -2448,7 +2578,7 @@ snapshots: '@rc-component/select@1.5.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@rc-component/overflow': 1.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/trigger': 3.8.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/trigger': 3.9.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/util': 1.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/virtual-list': 1.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) clsx: 2.1.1 @@ -2479,7 +2609,7 @@ snapshots: '@rc-component/table@1.9.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@rc-component/context': 2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/resize-observer': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/resize-observer': 1.1.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/util': 1.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/virtual-list': 1.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) clsx: 2.1.1 @@ -2491,7 +2621,7 @@ snapshots: '@rc-component/dropdown': 1.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/menu': 1.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/resize-observer': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/resize-observer': 1.1.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/util': 1.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) clsx: 2.1.1 react: 19.2.3 @@ -2500,7 +2630,7 @@ snapshots: '@rc-component/textarea@1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@rc-component/input': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/resize-observer': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/resize-observer': 1.1.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/util': 1.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) clsx: 2.1.1 react: 19.2.3 @@ -2508,7 +2638,7 @@ snapshots: '@rc-component/tooltip@1.4.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@rc-component/trigger': 3.8.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/trigger': 3.9.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/util': 1.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) clsx: 2.1.1 react: 19.2.3 @@ -2517,7 +2647,7 @@ snapshots: '@rc-component/tour@2.3.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@rc-component/portal': 2.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/trigger': 3.8.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/trigger': 3.9.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/util': 1.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) clsx: 2.1.1 react: 19.2.3 @@ -2541,11 +2671,11 @@ snapshots: react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - '@rc-component/trigger@3.8.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@rc-component/trigger@3.9.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/portal': 2.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/resize-observer': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/resize-observer': 1.1.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/util': 1.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) clsx: 2.1.1 react: 19.2.3 @@ -2568,7 +2698,7 @@ snapshots: '@rc-component/virtual-list@1.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@babel/runtime': 7.28.6 - '@rc-component/resize-observer': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/resize-observer': 1.1.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/util': 1.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) clsx: 2.1.1 react: 19.2.3 @@ -2659,10 +2789,10 @@ snapshots: dependencies: csstype: 3.2.3 - antd@6.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + antd@6.2.1(date-fns@4.1.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: '@ant-design/colors': 8.0.1 - '@ant-design/cssinjs': 2.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@ant-design/cssinjs': 2.0.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@ant-design/cssinjs-utils': 2.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@ant-design/fast-color': 3.0.0 '@ant-design/icons': 6.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -2685,11 +2815,11 @@ snapshots: '@rc-component/mutate-observer': 2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/notification': 1.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/pagination': 1.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/picker': 1.9.0(dayjs@1.11.19)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/picker': 1.9.0(date-fns@4.1.0)(dayjs@1.11.19)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/progress': 1.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/qrcode': 1.1.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/rate': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/resize-observer': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/resize-observer': 1.1.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/segmented': 1.3.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/select': 1.5.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/slider': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -2702,7 +2832,7 @@ snapshots: '@rc-component/tour': 2.3.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/tree': 1.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/tree-select': 1.6.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/trigger': 3.8.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/trigger': 3.9.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/upload': 1.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@rc-component/util': 1.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) clsx: 2.1.1 @@ -2746,8 +2876,16 @@ snapshots: compute-scroll-into-view@3.1.1: {} + copy-to-clipboard@3.3.3: + dependencies: + toggle-selection: 1.0.6 + csstype@3.2.3: {} + date-fns-jalali@4.1.0-0: {} + + date-fns@4.1.0: {} + dayjs@1.11.19: {} detect-libc@2.1.2: {} @@ -2761,23 +2899,22 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.0 - es-toolkit@1.43.0: {} + es-toolkit@1.44.0: {} + + eventemitter3@5.0.4: {} get-nonce@1.0.1: {} graceful-fs@4.2.11: {} + hono@4.11.5: {} + idb-keyval@6.2.2: {} is-mobile@5.0.0: {} jiti@2.6.1: {} - jotai@2.16.1(@types/react@19.2.7)(react@19.2.3): - optionalDependencies: - '@types/react': 19.2.7 - react: 19.2.3 - json2mq@0.2.0: dependencies: string-convert: 0.2.1 @@ -2831,6 +2968,8 @@ snapshots: lightningcss-win32-arm64-msvc: 1.30.2 lightningcss-win32-x64-msvc: 1.30.2 + lru-cache@11.2.4: {} + lucide-react@0.562.0(react@19.2.3): dependencies: react: 19.2.3 @@ -2839,11 +2978,15 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + marked@17.0.1: {} + nanoid@3.3.11: {} - next@16.1.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + nanoid@5.1.6: {} + + next@16.1.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: - '@next/env': 16.1.1 + '@next/env': 16.1.4 '@swc/helpers': 0.5.15 baseline-browser-mapping: 2.9.11 caniuse-lite: 1.0.30001762 @@ -2852,14 +2995,14 @@ snapshots: react-dom: 19.2.3(react@19.2.3) styled-jsx: 5.1.6(react@19.2.3) optionalDependencies: - '@next/swc-darwin-arm64': 16.1.1 - '@next/swc-darwin-x64': 16.1.1 - '@next/swc-linux-arm64-gnu': 16.1.1 - '@next/swc-linux-arm64-musl': 16.1.1 - '@next/swc-linux-x64-gnu': 16.1.1 - '@next/swc-linux-x64-musl': 16.1.1 - '@next/swc-win32-arm64-msvc': 16.1.1 - '@next/swc-win32-x64-msvc': 16.1.1 + '@next/swc-darwin-arm64': 16.1.4 + '@next/swc-darwin-x64': 16.1.4 + '@next/swc-linux-arm64-gnu': 16.1.4 + '@next/swc-linux-arm64-musl': 16.1.4 + '@next/swc-linux-x64-gnu': 16.1.4 + '@next/swc-linux-x64-musl': 16.1.4 + '@next/swc-win32-arm64-msvc': 16.1.4 + '@next/swc-win32-x64-msvc': 16.1.4 sharp: 0.34.5 transitivePeerDependencies: - '@babel/core' @@ -2881,11 +3024,22 @@ snapshots: proxy-compare@3.0.1: {} + react-day-picker@9.13.0(react@19.2.3): + dependencies: + '@date-fns/tz': 1.4.1 + date-fns: 4.1.0 + date-fns-jalali: 4.1.0-0 + react: 19.2.3 + react-dom@19.2.3(react@19.2.3): dependencies: react: 19.2.3 scheduler: 0.27.0 + react-hook-form@7.71.1(react@19.2.3): + dependencies: + react: 19.2.3 + react-is@18.3.1: {} react-remove-scroll-bar@2.3.8(@types/react@19.2.7)(react@19.2.3): @@ -2982,6 +3136,8 @@ snapshots: throttle-debounce@5.0.2: {} + toggle-selection@1.0.6: {} + tslib@2.8.1: {} tw-animate-css@1.4.0: {} diff --git a/public/panda.jpg b/public/panda.jpg new file mode 100644 index 0000000..0f1beff Binary files /dev/null and b/public/panda.jpg differ diff --git a/src/app/about/page.tsx b/src/app/about/page.tsx deleted file mode 100644 index 39c259c..0000000 --- a/src/app/about/page.tsx +++ /dev/null @@ -1,9 +0,0 @@ -export default function AboutPage() { - return ( -
-
- About Light Code -
-
- ); -} \ No newline at end of file diff --git a/src/app/apps/app/AIEditorLink.tsx b/src/app/apps/app/AIEditorLink.tsx new file mode 100644 index 0000000..a759897 --- /dev/null +++ b/src/app/apps/app/AIEditorLink.tsx @@ -0,0 +1,47 @@ +'use client'; + +import { useLayoutStore } from '@/modules/layout/store'; +import { useShallow } from 'zustand/shallow'; +import { toast } from 'sonner'; +import { Folder } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; +import { openLink } from '@/modules/basename'; + +type Props = { + pathname?: string; +}; +export const AIEditorLink = (props: Props) => { + const layoutUser = useLayoutStore( + useShallow((state) => ({ + user: state.me?.username || '', + })), + ); + return ( + + + + + 打开对应的文件夹 + + ); +}; diff --git a/src/app/apps/app/page.tsx b/src/app/apps/app/page.tsx new file mode 100644 index 0000000..39bb2fc --- /dev/null +++ b/src/app/apps/app/page.tsx @@ -0,0 +1,378 @@ +'use client'; + +import { useAppVersionStore } from '../store'; +import { useShallow } from 'zustand/react/shallow'; +import { useCallback, useEffect, useLayoutEffect, useMemo, useState } from 'react'; +import { Plus, ChevronLeft, Upload, File, Trash2, ExternalLink } from 'lucide-react'; +import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; +import { isObjectNull } from '@/modules/is-null'; +import { FileUpload } from '../modules/FileUpload'; +import clsx from 'clsx'; +import { toast as message } from 'sonner'; +import { Button } from '@/components/ui/button'; +import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { Controller, useForm } from 'react-hook-form'; +import { pick } from 'es-toolkit'; +import { useAppDeleteModalStore, AppDeleteModal } from '../modules/AppDeleteModal'; +import { AIEditorLink } from './AIEditorLink'; +import { openLink } from '@/modules/basename'; +import { LayoutMain } from '@/modules/layout'; + +const FormModal = () => { + const { control, handleSubmit, reset } = useForm(); + const containerStore = useAppVersionStore( + useShallow((state) => { + return { + showEdit: state.showEdit, + setShowEdit: state.setShowEdit, + formData: state.formData, + updateData: state.updateData, + }; + }), + ); + + useEffect(() => { + const open = containerStore.showEdit; + if (open) { + const isNull = isObjectNull(containerStore.formData); + if (isNull) { + reset({}); + } else { + reset(containerStore.formData); + } + } + }, [containerStore.showEdit]); + + const onFinish = async (values: any) => { + const pickValues = pick(values, ['id', 'key', 'version']); + containerStore.updateData(pickValues); + }; + + const onClose = () => { + containerStore.setShowEdit(false); + reset(); + }; + + const isEdit = containerStore.formData.id; + + return ( + containerStore.setShowEdit(open)}> + + + {isEdit ? 'Edit' : 'Add'} + +
+
+ + } + /> +
+
+ + } + /> +
+
+ + +
+
+
+
+ ); +}; + +const getAppKey = () => { + const [appKey, setAppKey] = useState(''); + useLayoutEffect(() => { + if (typeof window === 'undefined') return; + const url = new URL(window.location.href); + const appKey = url.searchParams.get('appKey'); + setAppKey(appKey || ''); + }, []); + return appKey || ''; +} +export const AppVersionList = () => { + const appKey = getAppKey(); + const versionStore = useAppVersionStore( + useShallow((state) => { + return { + list: state.list, + getList: state.getList, + key: state.key, + setKey: state.setKey, + setShowEdit: state.setShowEdit, + formData: state.formData, + setFormData: state.setFormData, + deleteData: state.deleteData, + publishVersion: state.publishVersion, + app: state.app, + }; + }), + ); + const appDeleteModalStore = useAppDeleteModalStore( + useShallow((state) => { + return { + onClickDelete: state.onClickDelete, + }; + }), + ); + const [isUpload, setIsUpload] = useState(false); + useEffect(() => { + // fetch app version list + if (appKey) { + versionStore.setKey(appKey); + versionStore.getList(); + } + }, [appKey]); + const appVersion = useMemo(() => { + return versionStore.app?.version || ''; + }, [versionStore.app?.version]); + if (!appKey) { + return
App Key is required
; + } + return ( +
+
+ + + + + 添加版本 + +
+ +
+
+ + + + + 返回 + +
+ +
+
+
+ {versionStore.list.map((item, index) => { + const isPublish = item.version === appVersion; + const color = isPublish ? 'bg-green-500' : ''; + const isRunning = item.status === 'running'; + return ( +
+
+ {item.version} + + + +
+
+ {isPublish ? 'published' : ''} +
+
+
+ + + + + Delete + + + + + + 使用当前版本,发布为此版本 + + + + + + To Test App + + + + + + + 文件管理 + +
+
+ ); + })} +
+
+
+
+
+ {isUpload && ( +
+
+ + + + + 返回 + +
{versionStore.key}
+
+ +
+ )} +
+ + +
+ ); +}; +export const AppVersionFile = () => { + const versionStore = useAppVersionStore( + useShallow((state) => { + return { + formData: state.formData, + detectVersionList: state.detectVersionList, + }; + }), + ); + const versionFiles = useMemo(() => { + if (!versionStore.formData?.data) return []; + const files = versionStore.formData.data.files || []; + return files as any[]; + }, [versionStore.formData]); + const onDetect = useCallback(async () => { + console.log('formData', versionStore.formData); + if (!versionStore.formData.key || !versionStore.formData.version) { + message.error('请先选择应用和版本'); + return; + } + const res = await versionStore.detectVersionList({ + appKey: versionStore.formData.key, + version: versionStore.formData.version, + }); + console.log('res', res); + if (res.code === 200) { + message.success('检测实际文件成功'); + } else { + message.error(res.message || 'Detect failed'); + } + }, [versionStore.formData]); + return ( + <> +
version: {versionStore.formData.version}
+
+
+ Files + + + + + + 从同文件目录下检测已经上传的文件内容 + +
+
+
+ {versionFiles.map((file, index) => { + const prefix = versionStore.formData.key + '/' + versionStore.formData.version + '/'; + const _path = file.path || ''; + const path = _path.replace(prefix, ''); + return ( +
+ {/*
{file.name}
*/} +
+ +
+
{path}
+
+ ); + })} +
+
+
+ + ); +}; + +export default () => { + return + + +}; \ No newline at end of file diff --git a/src/app/apps/constants.ts b/src/app/apps/constants.ts new file mode 100644 index 0000000..db5722a --- /dev/null +++ b/src/app/apps/constants.ts @@ -0,0 +1,11 @@ +export const iText = { + share: { + title: '共享设置', + tips: `共享设置 + + 1. 设置公共可以直接访问 + 2. 设置受保护需要登录后访问 + 3. 设置私有只有自己可以访问。\n + 受保护可以设置密码,设置访问的用户名。切换共享状态后,需要重新设置密码和用户名。`, + }, +}; diff --git a/src/app/apps/layouts/index.tsx b/src/app/apps/layouts/index.tsx new file mode 100644 index 0000000..9851c52 --- /dev/null +++ b/src/app/apps/layouts/index.tsx @@ -0,0 +1,7 @@ +'use client'; + +import { LayoutMain } from '@/modules/layout'; + +export const Main = () => { + return ; +}; diff --git a/src/app/apps/modules/AppDeleteModal.tsx b/src/app/apps/modules/AppDeleteModal.tsx new file mode 100644 index 0000000..baf5bda --- /dev/null +++ b/src/app/apps/modules/AppDeleteModal.tsx @@ -0,0 +1,86 @@ +'use client'; + +import { Button } from '@/components/ui/button'; +import { useState } from 'react'; +import { create } from 'zustand'; +import { useAppVersionStore, useUserAppStore } from '../store'; +import { useShallow } from 'zustand/shallow'; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'; + +type AppDeleteModalStore = { + open: boolean; + setOpen: (open: boolean) => void; + app: any; + setApp: (app: any) => void; + type: 'user-app' | 'app-version'; + setType: (type: 'user-app' | 'app-version') => void; + onClickDelete: (type: 'user-app' | 'app-version', data: any) => void; +}; +export const useAppDeleteModalStore = create((set) => ({ + open: false, + setOpen: (open) => set({ open }), + app: null, + setApp: (app) => set({ app }), + type: 'user-app', + setType: (type) => set({ type }), + onClickDelete: (type, data) => { + set({ open: true, type, app: data }); + }, +})); + +export const AppDeleteModal = () => { + const { open, setOpen, app, type } = useAppDeleteModalStore(); + const userAppStore = useUserAppStore( + useShallow((state) => { + return { + deleteData: state.deleteData, + }; + }), + ); + const appVersionStore = useAppVersionStore( + useShallow((state) => { + return { + deleteData: state.deleteData, + }; + }), + ); + const onClose = () => { + setOpen(false); + }; + const onDelete = (deleteFile = false) => { + if (type === 'user-app') { + userAppStore.deleteData(app.id, deleteFile); + } else { + appVersionStore.deleteData(app.id, deleteFile); + } + setOpen(false); + }; + + return ( + + + + Tips + +
+

Delete App Introduce

+
+ + + + + +
+
+ ); +}; diff --git a/src/app/apps/modules/DatePicker.tsx b/src/app/apps/modules/DatePicker.tsx new file mode 100644 index 0000000..84283bd --- /dev/null +++ b/src/app/apps/modules/DatePicker.tsx @@ -0,0 +1,61 @@ +"use client" + +import * as React from "react" +import { Calendar as CalendarIcon } from "lucide-react" +import dayjs, { type Dayjs } from "dayjs" + +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" +import { Calendar } from "@/components/ui/calendar" +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover" + +interface DatePickerProps { + className?: string + value?: string | Dayjs + onChange?: (date: Dayjs) => void +} + +export function DatePicker({ className, value, onChange }: DatePickerProps) { + const [date, setDate] = React.useState( + value ? new Date(typeof value === 'string' ? value : value.toISOString()) : undefined + ) + + React.useEffect(() => { + if (value) { + const dateValue = typeof value === 'string' ? value : value.toISOString() + setDate(new Date(dateValue)) + } + }, [value]) + + const handleSelect = (selectedDate: Date | undefined) => { + setDate(selectedDate) + if (selectedDate && onChange) { + onChange(dayjs(selectedDate)) + } + } + + return ( + + + + + + + + + ) +} diff --git a/src/app/apps/modules/FileUpload.tsx b/src/app/apps/modules/FileUpload.tsx new file mode 100644 index 0000000..ef26dd0 --- /dev/null +++ b/src/app/apps/modules/FileUpload.tsx @@ -0,0 +1,111 @@ +'use client'; + +import { Button } from '@/components/ui/button'; +import { useCallback, useRef } from 'react'; +import { useAppVersionStore } from '../store'; +import { useShallow } from 'zustand/react/shallow'; +import { toast as message } from 'sonner'; + +export type FileType = { + name: string; + size: number; + lastModified: number; + webkitRelativePath: string; // 包含name +}; + +export const FileUpload = () => { + const ref = useRef(null); + const appVersionStore = useAppVersionStore( + useShallow((state) => { + return { + formData: state.formData, + setFormData: state.setFormData, + updateByFromData: state.updateByFromData, + }; + }), + ); + const onChange = useCallback( + async (e: any) => { + console.log(e.target.files); + // webkitRelativePath + let files = Array.from(e.target.files) as any[]; + console.log(files); + if (files.length === 0) { + message.error('请选择文件'); + return; + } + + // 过滤 文件 .DS_Store + files = files.filter((file) => { + if (file.webkitRelativePath.startsWith('__MACOSX')) { + return false; + } + // 过滤node_modules + if (file.webkitRelativePath.includes('node_modules')) { + return false; + } + // 过滤以.开头的文件 + return !file.name.startsWith('.'); + }); + if (files.length === 0) { + console.log('no files'); + return; + } + const root = files[0].webkitRelativePath.split('/')[0]; + const formData = new FormData(); + files.forEach((file) => { + // relativePath 去除第一级 + const webkitRelativePath = file.webkitRelativePath.replace(root + '/', ''); + formData.append('file', file, webkitRelativePath); // 保留文件夹路径 + }); + const key = appVersionStore.formData.key; + const version = appVersionStore.formData.version; + formData.append('appKey', key); + formData.append('version', version); + const res = await fetch('/api/app/upload', { + method: 'POST', + body: formData, // + headers: { + Authorization: 'Bearer ' + (typeof window !== 'undefined' ? localStorage.getItem('token') : ''), + }, + }).then((res) => res.json()); + if (res?.code === 200) { + appVersionStore.setFormData(res.data); + appVersionStore.updateByFromData(); + } else { + message.error(res.message || 'Request failed'); + } + // 清理之前上传的文件 + e.target.value = ''; + }, + [appVersionStore.formData], + ); + + return ( +
+ + +
+ ); +}; diff --git a/src/app/apps/modules/PermissionManager.tsx b/src/app/apps/modules/PermissionManager.tsx new file mode 100644 index 0000000..6e65d1b --- /dev/null +++ b/src/app/apps/modules/PermissionManager.tsx @@ -0,0 +1,149 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { KeyParse, getTips } from './key-parse'; +import { DatePicker } from './DatePicker'; +import { TagsInput } from './TagsInput'; +import { HelpCircle } from 'lucide-react'; +import clsx from 'clsx'; +import { Button } from '@/components/ui/button'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select" +import { Input } from "@/components/ui/input" +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip" + +export const KeyShareSelect = ({ value, onChange }: { value: string; onChange?: (value: string) => void }) => { + return ( + + ); +}; + +export const KeyTextField = ({ name, value, onChange }: { name: string; value: string; onChange?: (value: string) => void }) => { + return ( + onChange?.(e.target.value)} + className="w-full" + /> + ); +}; + +type PermissionManagerProps = { + value: Record; + onChange: (value: Record) => void; + className?: string; +}; + +export const PermissionManager = ({ value, onChange, className }: PermissionManagerProps) => { + const [formData, setFormData] = useState(value); + const [keys, setKeys] = useState([]); + + useEffect(() => { + const hasShare = value?.share && value?.share === 'protected'; + setFormData(KeyParse.parse(value || {})); + if (hasShare) { + setKeys(['password', 'usernames', 'expiration-time']); + } else { + setKeys([]); + } + }, [value]); + + const onChangeValue = (key: string, newValue: any) => { + let newFormData = { ...formData, [key]: newValue }; + if (key === 'share') { + if (newValue === 'protected') { + newFormData = { ...newFormData, password: '', usernames: [], 'expiration-time': null }; + onChange(KeyParse.stringify(newFormData)); + setKeys(['password', 'usernames', 'expiration-time']); + } else { + delete newFormData.password; + delete newFormData.usernames; + delete newFormData['expiration-time']; + onChange(KeyParse.stringify(newFormData)); + setKeys([]); + } + } else { + onChange(KeyParse.stringify(newFormData)); + } + }; + + const tips = getTips('share'); + + return ( + +
+
+
+ + {tips && ( + + + + + +

{tips}

+
+
+ )} +
+ onChangeValue('share', value)} /> +
+ + {keys.map((item: any) => { + const tips = getTips(item); + + return ( +
+
+ + {tips && ( + + + + + +

{tips}

+
+
+ )} +
+ {item === 'expiration-time' && ( + onChangeValue(item, date)} /> + )} + {item === 'usernames' && ( + onChangeValue(item, value)} /> + )} + {item !== 'expiration-time' && item !== 'usernames' && ( + onChangeValue(item, value)} /> + )} +
+ ); + })} +
+
+ ); +}; diff --git a/src/app/apps/modules/TagsInput.tsx b/src/app/apps/modules/TagsInput.tsx new file mode 100644 index 0000000..6d6aae1 --- /dev/null +++ b/src/app/apps/modules/TagsInput.tsx @@ -0,0 +1,62 @@ +"use client" + +import * as React from "react" +import { X } from "lucide-react" +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" + +type TagsInputProps = { + value: string[]; + onChange: (value: string[]) => void; + placeholder?: string; + className?: string; +}; + +export function TagsInput({ value, onChange, placeholder = "输入用户名,按回车添加", className }: TagsInputProps) { + const [inputValue, setInputValue] = React.useState("") + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "Enter" || e.key === ",") { + e.preventDefault() + const newValue = inputValue.trim() + if (newValue && !value.includes(newValue)) { + onChange([...value, newValue]) + } + setInputValue("") + } else if (e.key === "Backspace" && !inputValue && value.length > 0) { + onChange(value.slice(0, -1)) + } + } + + const removeTag = (tagToRemove: string) => { + onChange(value.filter((tag) => tag !== tagToRemove)) + } + + return ( +
+ {value.map((tag, index) => ( +
+ {tag} + +
+ ))} + setInputValue(e.target.value)} + onKeyDown={handleKeyDown} + placeholder={value.length === 0 ? placeholder : ""} + className="flex-1 min-w-[120px] h-8" + /> +
+ ) +} diff --git a/src/app/apps/modules/key-parse.ts b/src/app/apps/modules/key-parse.ts new file mode 100644 index 0000000..cdd573c --- /dev/null +++ b/src/app/apps/modules/key-parse.ts @@ -0,0 +1,109 @@ +'use client'; + +import dayjs from 'dayjs'; +export const getTips = (key: string, lang?: string) => { + const tip = keysTips.find((item) => item.key === key); + if (tip) { + if (lang === 'en') { + return tip.enTips; + } + return tip.tips; + } + return ''; +}; +export const keysTips = [ + { + key: 'share', + tips: `共享设置 + 1. 设置公共可以直接访问 + 2. 设置受保护需要登录后访问 + 3. 设置私有只有自己可以访问。\n + 受保护可以设置密码,设置访问的用户名。切换共享状态后,需要重新设置密码和用户名。 不设置,默认是只能自己访问。`, + enTips: `1. Set public to directly access + 2. Set protected to access after login + 3. Set private to access only yourself. + Protected can set a password and set the username for access. After switching the shared state, you need to reset the password and username. If not set, it defaults to only being accessible to yourself.`, + }, + { + key: 'content-type', + tips: `内容类型,设置文件的内容类型。默认不要修改。`, + enTips: `Content type, set the content type of the file. Default do not modify.`, + }, + { + key: 'app-source', + tips: `应用来源,上传方式。默认不要修改。`, + enTips: `App source, upload method. Default do not modify.`, + }, + { + key: 'cache-control', + tips: `缓存控制,设置文件的缓存控制。默认不要修改。`, + enTips: `Cache control, set the cache control of the file. Default do not modify.`, + }, + { + key: 'password', + tips: `密码,设置文件的密码。不设置默认是所有人都可以访问。`, + enTips: `Password, set the password of the file. If not set, it defaults to everyone can access.`, + }, + { + key: 'usernames', + tips: `用户名,设置文件的用户名。不设置默认是所有人都可以访问。`, + enTips: `Username, set the username of the file. If not set, it defaults to everyone can access.`, + parse: (value: string) => { + if (!value) { + return []; + } + return value.split(','); + }, + stringify: (value: string[]) => { + if (!value) { + return ''; + } + return value.join(','); + }, + }, + { + key: 'expiration-time', + tips: `过期时间,设置文件的过期时间。不设置默认是永久。`, + enTips: `Expiration time, set the expiration time of the file. If not set, it defaults to permanent.`, + parse: (value: Date) => { + if (!value) { + return null; + } + return dayjs(value); + }, + stringify: (value?: dayjs.Dayjs) => { + if (!value) { + return ''; + } + return value.toISOString(); + }, + }, +]; +export class KeyParse { + static parse(metadata: Record) { + const keys = Object.keys(metadata); + const newMetadata = {}; + keys.forEach((key) => { + const tip = keysTips.find((item) => item.key === key); + if (tip && tip.parse) { + newMetadata[key] = tip.parse(metadata[key]); + } else { + newMetadata[key] = metadata[key]; + } + }); + return newMetadata; + } + static stringify(metadata: Record) { + const keys = Object.keys(metadata); + const newMetadata = {}; + keys.forEach((key) => { + const tip = keysTips.find((item) => item.key === key); + if (tip && tip.stringify) { + newMetadata[key] = tip.stringify(metadata[key]); + } else { + newMetadata[key] = metadata[key]; + } + }); + return newMetadata; + } +} diff --git a/src/app/apps/page.tsx b/src/app/apps/page.tsx new file mode 100644 index 0000000..67aeea7 --- /dev/null +++ b/src/app/apps/page.tsx @@ -0,0 +1,494 @@ +'use client'; + +import { useShallow } from 'zustand/react/shallow'; +import { useAppVersionStore, useUserAppStore } from './store'; +import { useEffect, useMemo, useState } from 'react'; +// import { useModal } from '@kevisual/components/modal/Confirm.tsx'; + +import { Plus, Code, Link as LinkIcon, Edit, Trash2, Share2, RefreshCcw, ExternalLink, Folder } from 'lucide-react'; +import { isObjectNull } from '@/modules/is-null'; +import clsx from 'clsx'; +// import { IconButton } from '@kevisual/components/button/index.tsx'; +// import { Select } from '@kevisual/components/select/index.tsx'; +import { iText } from './constants'; +import { PermissionManager } from './modules/PermissionManager'; +import { toast as message } from 'sonner'; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { Button } from '@/components/ui/button'; +import { Textarea } from '@/components/ui/textarea'; +import { Controller, useForm } from 'react-hook-form'; +import { pick } from 'es-toolkit'; +import copy from 'copy-to-clipboard'; +import { useLayoutStore } from '@/modules/layout/store'; +import { useAppDeleteModalStore, AppDeleteModal } from './modules/AppDeleteModal'; +import { AppWindow, Folder as FolderIcon } from 'lucide-react'; +import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; +import { Switch } from '@/components/ui/switch'; +import { AIEditorLink } from './app/AIEditorLink'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { LayoutMain } from '@/modules/layout'; +import { openLink } from '@/modules/basename'; +export const IconButton = (props: any) => { + return ( + + ); +}; +const FormModal = () => { + const defaultValues = { + id: '', + title: '', + domain: '', + key: '', + description: '', + proxy: true, + status: 'running', + }; + const { control, handleSubmit, reset } = useForm({ + defaultValues, + }); + const containerStore = useUserAppStore( + useShallow((state) => { + return { + showEdit: state.showEdit, + setShowEdit: state.setShowEdit, + userApp: state.userApp, + updateData: state.updateData, + }; + }), + ); + useEffect(() => { + const open = containerStore.showEdit; + if (open) { + const isNull = isObjectNull(containerStore.userApp); + if (isNull) { + reset(defaultValues); + } else { + reset(containerStore.userApp); + } + } + }, [containerStore.showEdit, containerStore.userApp]); + const onFinish = async (values: any) => { + const pickValues = pick(values, ['id', 'title', 'domain', 'key', 'description', 'proxy', 'status']); + containerStore.updateData(pickValues); + }; + const onClose = () => { + containerStore.setShowEdit(false); + reset(); + }; + const isEdit = containerStore?.userApp?.id; + const isAdmin = useLayoutStore(useShallow((state) => state.isAdmin)); + + return ( + containerStore.setShowEdit(open)}> + + + {isEdit ? '编辑' : '添加'} + +
+
+ + } /> +
+
+ + } /> +
+
+ +