diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000000000000000000000000000000000..14061f572a98ac84dc0505dda07afc88e4caeee6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{js,jsx,ts,tsx,json,jsonc,css,md,yml,yaml}] +indent_style = space +indent_size = 2 + +[*.rs] +indent_style = space +indent_size = 4 + +[*.md] +trim_trailing_whitespace = false diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 0000000000000000000000000000000000000000..03da83a829969cd0b8804a12d4039256d61be4c8 --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,22 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "semi": false, + "singleQuote": true, + "jsxSingleQuote": true, + "sortImports": { + "internalPattern": ["@/"], + "newlinesBetween": true, + "groups": [ + "builtin", + "external", + "internal", + ["parent", "sibling", "index"], + "style", + "unknown" + ] + }, + "sortTailwindcss": { + "stylesheet": "ui/app/globals.css", + "functions": ["cn", "clsx", "cva", "tw"] + } +} diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 99092e48bd41f3b5ea8ea7479df6716149fdeb61..0000000000000000000000000000000000000000 --- a/.prettierrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "plugins": ["prettier-plugin-tailwindcss"], - "semi": false, - "singleQuote": true, - "jsxSingleQuote": true -} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000000000000000000000000000000000000..7302972d5098e3473b5ab4d4c6baeff5ce3919d5 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "oxc.oxc-vscode", + "EditorConfig.EditorConfig", + "bradlc.vscode-tailwindcss", + "rust-lang.rust-analyzer" + ], + "unwantedRecommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"] +} diff --git a/bun.lock b/bun.lock index c42207096fd5aa51c42ce0936193036a65dc6354..b5d6a67074e752600a091bea88fe1497ff8be1c5 100644 --- a/bun.lock +++ b/bun.lock @@ -7,8 +7,7 @@ "@playwright/test": "^1.59.1", "@tauri-apps/cli": "^2.10.1", "git-cliff": "^2.12.0", - "prettier": "^3.8.3", - "prettier-plugin-tailwindcss": "^0.7.2", + "oxfmt": "^0.45.0", }, }, "ui": { @@ -371,6 +370,44 @@ "@orval/zod": ["@orval/zod@8.8.0", "", { "dependencies": { "@orval/core": "8.8.0", "remeda": "^2.33.6" } }, "sha512-ouKzo7srJXuwsZmNzp8nXkM6lwiYCZoSRYFH1JFAYF77SK7AEoFul8AYObzebqmHIXC4GLUNOsxHT9N0NE8zmQ=="], + "@oxfmt/binding-android-arm-eabi": ["@oxfmt/binding-android-arm-eabi@0.45.0", "", { "os": "android", "cpu": "arm" }, "sha512-A/UMxFob1fefCuMeGxQBulGfFE38g2Gm23ynr3u6b+b7fY7/ajGbNsa3ikMIkGMLJW/TRoQaMoP1kME7S+815w=="], + + "@oxfmt/binding-android-arm64": ["@oxfmt/binding-android-arm64@0.45.0", "", { "os": "android", "cpu": "arm64" }, "sha512-L63z4uZmHjgvvqvMJD7mwff8aSBkM0+X4uFr6l6U5t6+Qc9DCLVZWIunJ7Gm4fn4zHPdSq6FFQnhu9yqqobxIg=="], + + "@oxfmt/binding-darwin-arm64": ["@oxfmt/binding-darwin-arm64@0.45.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-UV34dd623FzqT+outIGndsCA/RBB+qgB3XVQhgmmJ9PJwa37NzPC9qzgKeOhPKxVk2HW+JKldQrVL54zs4Noww=="], + + "@oxfmt/binding-darwin-x64": ["@oxfmt/binding-darwin-x64@0.45.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-pMNJv0CMa1pDefVPeNbuQxibh8ITpWDFEhMC/IBB9Zlu76EbgzYwrzI4Cb11mqX2+rIYN70UTrh3z06TM59ptQ=="], + + "@oxfmt/binding-freebsd-x64": ["@oxfmt/binding-freebsd-x64@0.45.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-xTcRoxbbo61sW2+ZRPeH+vp/o9G8gkdhiVumFU+TpneiPm14c79l6GFlxPXlCE9bNWikigbsrvJw46zCVAQFfg=="], + + "@oxfmt/binding-linux-arm-gnueabihf": ["@oxfmt/binding-linux-arm-gnueabihf@0.45.0", "", { "os": "linux", "cpu": "arm" }, "sha512-hWL8Hdni+3U1mPFx1UtWeGp3tNb6EhBAUHRMbKUxVkOp3WwoJbpVO2bfUVbS4PfpledviXXNHSTl1veTa6FhkQ=="], + + "@oxfmt/binding-linux-arm-musleabihf": ["@oxfmt/binding-linux-arm-musleabihf@0.45.0", "", { "os": "linux", "cpu": "arm" }, "sha512-6Blt/0OBT7vvfQpqYuYbpbFLPqSiaYpEJzUUWhinPEuADypDbtV1+LdjM0vYBNGPvnj85ex7lTerEX6JGcPt9w=="], + + "@oxfmt/binding-linux-arm64-gnu": ["@oxfmt/binding-linux-arm64-gnu@0.45.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-jLjoLfe+hGfjhA8hNBSdw85yCA8ePKq7ME4T+g6P9caQXvmt6IhE2X7iVjnVdkmYUWEzZrxlh4p6RkDmAMJY/A=="], + + "@oxfmt/binding-linux-arm64-musl": ["@oxfmt/binding-linux-arm64-musl@0.45.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-XQKXZIKYJC3GQJ8FnD3iMntpw69Wd9kDDK/Xt79p6xnFYlGGxSNv2vIBvRTDg5CKByWFWWZLCRDOXoP/m6YN4g=="], + + "@oxfmt/binding-linux-ppc64-gnu": ["@oxfmt/binding-linux-ppc64-gnu@0.45.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-+g5RiG+xOkdrCWkKodv407nTvMq4vYM18Uox2MhZBm/YoqFxxJpWKsloskFFG5NU13HGPw1wzYjjOVcyd9moCA=="], + + "@oxfmt/binding-linux-riscv64-gnu": ["@oxfmt/binding-linux-riscv64-gnu@0.45.0", "", { "os": "linux", "cpu": "none" }, "sha512-V7dXKoSyEbWAkkSF4JJNtF+NJZDmJoSarSoP30WCsB3X636Rehd3CvxBj49FIJxEBFWhvcUjGSHVeU8Erck1bQ=="], + + "@oxfmt/binding-linux-riscv64-musl": ["@oxfmt/binding-linux-riscv64-musl@0.45.0", "", { "os": "linux", "cpu": "none" }, "sha512-Vdelft1sAEYojVGgcODEFXSWYQYlIvoyIGWebKCuUibd1tvS1TjTx413xG2ZLuHpYj45CkN/ztMLMX6jrgqpgg=="], + + "@oxfmt/binding-linux-s390x-gnu": ["@oxfmt/binding-linux-s390x-gnu@0.45.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-RR7xKgNpqwENnK0aYCGYg0JycY2n93J0reNjHyes+I9Gq52dH95x+CBlnlAQHCPfz6FGnKA9HirgUl14WO6o7w=="], + + "@oxfmt/binding-linux-x64-gnu": ["@oxfmt/binding-linux-x64-gnu@0.45.0", "", { "os": "linux", "cpu": "x64" }, "sha512-U/QQ0+BQNSHxjuXR/utvXnQ50Vu5kUuqEomZvQ1/3mhgbBiMc2WU9q5kZ5WwLp3gnFIx9ibkveoRSe2EZubkqg=="], + + "@oxfmt/binding-linux-x64-musl": ["@oxfmt/binding-linux-x64-musl@0.45.0", "", { "os": "linux", "cpu": "x64" }, "sha512-o5TLOUCF0RWQjsIS06yVC+kFgp092/yLe6qBGSUvtnmTVw9gxjpdQSXc3VN5Cnive4K11HNstEZF8ROKHfDFSw=="], + + "@oxfmt/binding-openharmony-arm64": ["@oxfmt/binding-openharmony-arm64@0.45.0", "", { "os": "none", "cpu": "arm64" }, "sha512-RnGcV3HgPuOjsGx/k9oyRNKmOp+NBLGzZTdPDYbc19r7NGeYPplnUU/BfU35bX2Y/O4ejvHxcfkvW2WoYL/gsg=="], + + "@oxfmt/binding-win32-arm64-msvc": ["@oxfmt/binding-win32-arm64-msvc@0.45.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-v3Vj7iKKsUFwt9w5hsqIIoErKVoENC6LoqfDlteOQ5QMDCXihlqLoxpmviUhXnNncg4zV6U9BPwlBbwa+qm4wg=="], + + "@oxfmt/binding-win32-ia32-msvc": ["@oxfmt/binding-win32-ia32-msvc@0.45.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-N8yotPBX6ph0H3toF4AEpdCeVPrdcSetj+8eGiZGsrLsng3bs/Q5HPu4bbSxip5GBPx5hGbGHrZwH4+rcrjhHA=="], + + "@oxfmt/binding-win32-x64-msvc": ["@oxfmt/binding-win32-x64-msvc@0.45.0", "", { "os": "win32", "cpu": "x64" }, "sha512-w5MMTRCK1dpQeRA+HHqXQXyN33DlG/N2LOYxJmaT4fJjcmZrbNnqw7SmIk7I2/a2493PPLZ+2E/Ar6t2iKVMug=="], + "@oxlint/binding-android-arm-eabi": ["@oxlint/binding-android-arm-eabi@1.60.0", "", { "os": "android", "cpu": "arm" }, "sha512-YdeJKaZckDQL1qa62a1aKq/goyq48aX3yOxaaWqWb4sau4Ee4IiLbamftNLU3zbePky6QsDj6thnSSzHRBjDfA=="], "@oxlint/binding-android-arm64": ["@oxlint/binding-android-arm64@1.60.0", "", { "os": "android", "cpu": "arm64" }, "sha512-7ANS7PpXCfq84xZQ8E5WPs14gwcuPcl+/8TFNXfpSu0CQBXz3cUo2fDpHT8v8HJN+Ut02eacvMAzTnc9s6X4tw=="], @@ -1211,6 +1248,8 @@ "orval": ["orval@8.8.0", "", { "dependencies": { "@commander-js/extra-typings": "^14.0.0", "@orval/angular": "8.8.0", "@orval/axios": "8.8.0", "@orval/core": "8.8.0", "@orval/fetch": "8.8.0", "@orval/hono": "8.8.0", "@orval/mcp": "8.8.0", "@orval/mock": "8.8.0", "@orval/query": "8.8.0", "@orval/solid-start": "8.8.0", "@orval/swr": "8.8.0", "@orval/zod": "8.8.0", "@scalar/json-magic": "^0.12.4", "@scalar/openapi-parser": "^0.25.6", "@scalar/openapi-types": "0.6.1", "chokidar": "^5.0.0", "commander": "^14.0.2", "enquirer": "^2.4.1", "execa": "^9.6.1", "find-up": "8.0.0", "fs-extra": "^11.3.2", "jiti": "^2.6.1", "js-yaml": "4.1.1", "remeda": "^2.33.6", "string-argv": "^0.3.2", "tsconfck": "^3.1.6", "typedoc": "^0.28.17", "typedoc-plugin-coverage": "^4.0.2", "typedoc-plugin-markdown": "^4.10.0" }, "peerDependencies": { "prettier": ">=3.0.0" }, "optionalPeers": ["prettier"], "bin": { "orval": "dist/bin/orval.mjs" } }, "sha512-jcHcAmXCvC0g+1acsUOv722ICuXCJ0tmRl3+e0kj1lgFVsuGYame+jRZUrNtpkEZgF1RlDtmL91yFsVHv3X1eA=="], + "oxfmt": ["oxfmt@0.45.0", "", { "dependencies": { "tinypool": "2.1.0" }, "optionalDependencies": { "@oxfmt/binding-android-arm-eabi": "0.45.0", "@oxfmt/binding-android-arm64": "0.45.0", "@oxfmt/binding-darwin-arm64": "0.45.0", "@oxfmt/binding-darwin-x64": "0.45.0", "@oxfmt/binding-freebsd-x64": "0.45.0", "@oxfmt/binding-linux-arm-gnueabihf": "0.45.0", "@oxfmt/binding-linux-arm-musleabihf": "0.45.0", "@oxfmt/binding-linux-arm64-gnu": "0.45.0", "@oxfmt/binding-linux-arm64-musl": "0.45.0", "@oxfmt/binding-linux-ppc64-gnu": "0.45.0", "@oxfmt/binding-linux-riscv64-gnu": "0.45.0", "@oxfmt/binding-linux-riscv64-musl": "0.45.0", "@oxfmt/binding-linux-s390x-gnu": "0.45.0", "@oxfmt/binding-linux-x64-gnu": "0.45.0", "@oxfmt/binding-linux-x64-musl": "0.45.0", "@oxfmt/binding-openharmony-arm64": "0.45.0", "@oxfmt/binding-win32-arm64-msvc": "0.45.0", "@oxfmt/binding-win32-ia32-msvc": "0.45.0", "@oxfmt/binding-win32-x64-msvc": "0.45.0" }, "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-0o/COoN9fY50bjVeM7PQsNgbhndKurBIeTIcspW033OumksjJJmIVDKjAk5HMwU/GHTxSOdGDdhJ6BRzGPmsHg=="], + "oxlint": ["oxlint@1.60.0", "", { "optionalDependencies": { "@oxlint/binding-android-arm-eabi": "1.60.0", "@oxlint/binding-android-arm64": "1.60.0", "@oxlint/binding-darwin-arm64": "1.60.0", "@oxlint/binding-darwin-x64": "1.60.0", "@oxlint/binding-freebsd-x64": "1.60.0", "@oxlint/binding-linux-arm-gnueabihf": "1.60.0", "@oxlint/binding-linux-arm-musleabihf": "1.60.0", "@oxlint/binding-linux-arm64-gnu": "1.60.0", "@oxlint/binding-linux-arm64-musl": "1.60.0", "@oxlint/binding-linux-ppc64-gnu": "1.60.0", "@oxlint/binding-linux-riscv64-gnu": "1.60.0", "@oxlint/binding-linux-riscv64-musl": "1.60.0", "@oxlint/binding-linux-s390x-gnu": "1.60.0", "@oxlint/binding-linux-x64-gnu": "1.60.0", "@oxlint/binding-linux-x64-musl": "1.60.0", "@oxlint/binding-openharmony-arm64": "1.60.0", "@oxlint/binding-win32-arm64-msvc": "1.60.0", "@oxlint/binding-win32-ia32-msvc": "1.60.0", "@oxlint/binding-win32-x64-msvc": "1.60.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.18.0" }, "optionalPeers": ["oxlint-tsgolint"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-tnRzTWiWJ9pg3ftRWnD0+Oqh78L6ZSwcEudvCZaER0PIqiAnNyXj5N1dPwjmNpDalkKS9m/WMLN1CTPUBPmsgw=="], "p-limit": ["p-limit@4.0.0", "", { "dependencies": { "yocto-queue": "^1.0.0" } }, "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ=="], @@ -1255,8 +1294,6 @@ "prettier": ["prettier@3.8.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw=="], - "prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.7.2", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-hermes": "*", "@prettier/plugin-oxc": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-hermes", "@prettier/plugin-oxc", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-svelte"] }, "sha512-LkphyK3Fw+q2HdMOoiEHWf93fNtYJwfamoKPl7UwtjFQdei/iIBoX11G6j706FzN3ymX9mPVi97qIY8328vdnA=="], - "pretty-ms": ["pretty-ms@9.3.0", "", { "dependencies": { "parse-ms": "^4.0.0" } }, "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ=="], "progress": ["progress@2.0.3", "", {}, "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="], @@ -1375,6 +1412,8 @@ "terser-webpack-plugin": ["terser-webpack-plugin@5.4.0", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", "schema-utils": "^4.3.0", "terser": "^5.31.1" }, "peerDependencies": { "webpack": "^5.1.0" } }, "sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g=="], + "tinypool": ["tinypool@2.1.0", "", {}, "sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw=="], + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], diff --git a/package.json b/package.json index cc8a64dce98fee23a98f012b1e134856eddc55e5..f5db63adfe355dbe8c65150fc849a89341d2fe06 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,20 @@ { - "devDependencies": { - "@playwright/test": "^1.59.1", - "@tauri-apps/cli": "^2.10.1", - "git-cliff": "^2.12.0", - "prettier": "^3.8.3", - "prettier-plugin-tailwindcss": "^0.7.2" - }, + "workspaces": [ + "ui/" + ], "scripts": { "dev": "bun run scripts/dev.ts tauri dev -- --profile=release-with-debug", "build": "bun run scripts/dev.ts tauri build --no-bundle", "cargo": "bun run scripts/dev.ts cargo", "lint:ui": "bun run --filter ui lint", - "format": "prettier --write ui/ --ignore-path ui/.gitignore", + "format": "oxfmt ui package.json .oxfmtrc.json .vscode", + "format:check": "oxfmt --check ui package.json .oxfmtrc.json .vscode", "test:e2e": "playwright test" }, - "workspaces": [ - "ui/" - ] + "devDependencies": { + "@playwright/test": "^1.59.1", + "@tauri-apps/cli": "^2.10.1", + "git-cliff": "^2.12.0", + "oxfmt": "^0.45.0" + } } diff --git a/ui/.oxlintrc.json b/ui/.oxlintrc.json index 8b323020423f935cfceb82f5b7012b8c421aef72..f1c796096ed7449a1c8a17829c2459a65c92bf09 100644 --- a/ui/.oxlintrc.json +++ b/ui/.oxlintrc.json @@ -1,12 +1,6 @@ { "$schema": "../node_modules/oxlint/configuration_schema.json", - "plugins": [ - "eslint", - "typescript", - "unicorn", - "oxc", - "react" - ], + "plugins": ["eslint", "typescript", "unicorn", "oxc", "react"], "categories": { "correctness": "error" }, @@ -30,11 +24,5 @@ "typescript/no-this-alias": "warn", "unicorn/prefer-string-starts-ends-with": "warn" }, - "ignorePatterns": [ - ".next/", - "out/", - "dist/", - "build/", - "coverage/" - ] + "ignorePatterns": [".next/", "out/", "dist/", "build/", "coverage/"] } diff --git a/ui/app/(app)/layout.tsx b/ui/app/(app)/layout.tsx index 5963d122c42fe33e1f7ae4e485969a1e4e836c9d..994a9eac1854ac316e7350d029fa64c1f5e6a162 100644 --- a/ui/app/(app)/layout.tsx +++ b/ui/app/(app)/layout.tsx @@ -4,7 +4,7 @@ import { MenuBar } from '@/components/MenuBar' export default function AppLayout({ children }: { children: React.ReactNode }) { return ( -
+
{children}
diff --git a/ui/app/(app)/page.tsx b/ui/app/(app)/page.tsx index 6193bf1e55bf07aed9bc302ffbd2beae703fa2b6..eee232fd600430c8b1743446f1f4e2b6e8b3e503 100644 --- a/ui/app/(app)/page.tsx +++ b/ui/app/(app)/page.tsx @@ -1,18 +1,14 @@ 'use client' -import { Panels } from '@/components/Panels' -import { Workspace, StatusBar } from '@/components/Canvas' -import { Navigator } from '@/components/Navigator' +import { Group, Panel, Separator, useDefaultLayout } from 'react-resizable-panels' + import { ActivityBubble } from '@/components/ActivityBubble' +import { AppErrorBoundary } from '@/components/AppErrorBoundary' import { AppInitializationSkeleton } from '@/components/AppInitializationSkeleton' +import { Workspace, StatusBar } from '@/components/Canvas' +import { Navigator } from '@/components/Navigator' +import { Panels } from '@/components/Panels' import { useGetMeta } from '@/lib/api/system/system' -import { - Group, - Panel, - Separator, - useDefaultLayout, -} from 'react-resizable-panels' -import { AppErrorBoundary } from '@/components/AppErrorBoundary' const LAYOUT_ID = 'koharu-main-layout-v2' @@ -46,7 +42,7 @@ export default function Page() { - +
@@ -55,7 +51,7 @@ export default function Page() {
- + diff --git a/ui/app/global-error.tsx b/ui/app/global-error.tsx index 7cbcc1fbf74b14edd2214ec86cf2d684f0131b9c..0bb0be580334ecbb93861030d398e7b8086499bb 100644 --- a/ui/app/global-error.tsx +++ b/ui/app/global-error.tsx @@ -4,11 +4,7 @@ import * as Sentry from '@sentry/nextjs' import NextError from 'next/error' import { useEffect } from 'react' -export default function GlobalError({ - error, -}: { - error: Error & { digest?: string } -}) { +export default function GlobalError({ error }: { error: Error & { digest?: string } }) { useEffect(() => { Sentry.captureException(error) }, [error]) diff --git a/ui/app/globals.css b/ui/app/globals.css index 41dc8563c8b226dfcf95cad24693b41f77a08ec5..733bfb310db23eb5e29f558fb6d7e4ecbc236aea 100644 --- a/ui/app/globals.css +++ b/ui/app/globals.css @@ -137,8 +137,8 @@ body { @apply bg-background text-foreground select-none; font-family: - var(--font-inter), var(--font-noto-sc), var(--font-noto-tc), - var(--font-noto-jp), ui-sans-serif, system-ui, sans-serif; + var(--font-inter), var(--font-noto-sc), var(--font-noto-tc), var(--font-noto-jp), + ui-sans-serif, system-ui, sans-serif; } input, textarea { diff --git a/ui/app/layout.tsx b/ui/app/layout.tsx index 4fc09191ad760ba60f06f9933da19492fd1ff574..478fd9a8512a7c125509da9028fcb40ccb6748da 100644 --- a/ui/app/layout.tsx +++ b/ui/app/layout.tsx @@ -1,10 +1,6 @@ import type { Metadata } from 'next' -import { - Inter, - Noto_Sans_JP, - Noto_Sans_SC, - Noto_Sans_TC, -} from 'next/font/google' +import { Inter, Noto_Sans_JP, Noto_Sans_SC, Noto_Sans_TC } from 'next/font/google' + import './globals.css' import Providers from '@/app/providers' diff --git a/ui/app/providers.tsx b/ui/app/providers.tsx index 106b5d502b69b845fd711e148a667816a5a8d0f7..8fe2499175c3e290d5d43f327427661971d8e81b 100644 --- a/ui/app/providers.tsx +++ b/ui/app/providers.tsx @@ -1,12 +1,13 @@ 'use client' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { ThemeProvider } from 'next-themes' import { useEffect, type ReactNode } from 'react' import { I18nextProvider } from 'react-i18next' -import { ThemeProvider } from 'next-themes' -import { QueryClient, QueryClientProvider } from '@tanstack/react-query' + import ClientOnly from '@/components/ClientOnly' -import { UpdaterProvider } from '@/components/Updater' import { TooltipProvider } from '@/components/ui/tooltip' +import { UpdaterProvider } from '@/components/Updater' import i18n from '@/lib/i18n' import { ProcessingProvider } from '@/lib/machines' diff --git a/ui/components/ActivityBubble.tsx b/ui/components/ActivityBubble.tsx index 4a71064f284f53e8bde700346639f88a97abb6d5..d4c7005efcf95614233c29a8095d3e71b0d40aec 100644 --- a/ui/components/ActivityBubble.tsx +++ b/ui/components/ActivityBubble.tsx @@ -1,12 +1,13 @@ 'use client' +import { CircleXIcon } from 'lucide-react' import { type ReactNode } from 'react' import { useTranslation } from 'react-i18next' -import { CircleXIcon } from 'lucide-react' -import { useListDownloads } from '@/lib/api/downloads/downloads' + import { Button } from '@/components/ui/button' -import { useEditorUiStore } from '@/lib/stores/editorUiStore' +import { useListDownloads } from '@/lib/api/downloads/downloads' import { useProcessing } from '@/lib/machines' +import { useEditorUiStore } from '@/lib/stores/editorUiStore' type TranslateFunc = ReturnType['t'] @@ -17,7 +18,7 @@ const clampProgress = (value?: number) => { function BubbleCard({ children }: { children: ReactNode }) { return ( -
+
{children}
) @@ -26,18 +27,18 @@ function BubbleCard({ children }: { children: ReactNode }) { function ProgressBar({ percent }: { percent?: number }) { return (
-
+
{typeof percent === 'number' ? (
) : ( -
+
)}
{typeof percent === 'number' && ( - + {percent}% )} @@ -57,14 +58,10 @@ function DownloadCard({ return (
-
+
-
- {t('download.title')} -
-
- {filename} -
+
{t('download.title')}
+
{filename}
@@ -82,7 +79,7 @@ function ErrorCard({ t: TranslateFunc }) { return ( -
+
@@ -140,14 +137,9 @@ function OperationCard({ }) { const ctx = machineState.context const hasProgressNumbers = ctx.total > 0 - const progress = clampProgress( - hasProgressNumbers ? (ctx.current / ctx.total) * 100 : undefined, - ) + const progress = clampProgress(hasProgressNumbers ? (ctx.current / ctx.total) * 100 : undefined) const displayCurrent = hasProgressNumbers - ? Math.min( - ctx.total, - Math.floor(ctx.current) + (ctx.current >= ctx.total ? 0 : 1), - ) + ? Math.min(ctx.total, Math.floor(ctx.current) + (ctx.current >= ctx.total ? 0 : 1)) : undefined const total = hasProgressNumbers ? ctx.total : undefined @@ -182,27 +174,24 @@ function OperationCard({ const subtitleParts = isPipelineAll ? [stepLabel] : [imageText, stepText ?? stepLabel].filter(Boolean) - const subtitle = - subtitleParts.filter(Boolean).join(' \u00b7 ') || t('operations.inProgress') + const subtitle = subtitleParts.filter(Boolean).join(' \u00b7 ') || t('operations.inProgress') const title = getOperationTitle(machineState, t) return (
-
+
-
- {title} -
-
+
{title}
+
{subtitle || t('operations.inProgress')}
{isPipelineAll && total && typeof displayCurrent === 'number' ? ( - + {t('operations.imageProgress', { current: displayCurrent, total, @@ -248,22 +237,16 @@ export function ActivityBubble() { .filter((d) => d.status === 'started' || d.status === 'downloading') .map((d) => ({ ...d, - percent: - d.total && d.total > 0 - ? Math.round((d.downloaded / d.total) * 100) - : undefined, + percent: d.total && d.total > 0 ? Math.round((d.downloaded / d.total) * 100) : undefined, })) const errorMessage = uiError?.message - if (!errorMessage && !isProcessing && activeDownloads.length === 0) - return null + if (!errorMessage && !isProcessing && activeDownloads.length === 0) return null return (
- {errorMessage && ( - - )} + {errorMessage && } {isProcessing && ( )} {activeDownloads.map((d) => ( - + ))}
) diff --git a/ui/components/AppErrorBoundary.tsx b/ui/components/AppErrorBoundary.tsx index 82ffc6c82d490c7903292bf09abce079d5a675ed..a36eeaf79dfc29255621cd77227941af0ca86a82 100644 --- a/ui/components/AppErrorBoundary.tsx +++ b/ui/components/AppErrorBoundary.tsx @@ -2,22 +2,20 @@ import * as Sentry from '@sentry/nextjs' import { useQueryClient } from '@tanstack/react-query' -import { ErrorBoundary, type FallbackProps } from 'react-error-boundary' import { type ReactNode } from 'react' +import { ErrorBoundary, type FallbackProps } from 'react-error-boundary' + import { Button } from '@/components/ui/button' import { useEditorUiStore } from '@/lib/stores/editorUiStore' function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) { const queryClient = useQueryClient() - const errorMessage = - error instanceof Error ? error.message : 'Unexpected error' + const errorMessage = error instanceof Error ? error.message : 'Unexpected error' return ( -
-

- Something went wrong. -

-

{errorMessage}

+
+

Something went wrong.

+

{errorMessage}

@@ -415,13 +381,9 @@ function WindowControls() { onClick={() => { void windowControls.toggleMaximize().then(updateMaximized) }} - className='hover:bg-accent flex h-full w-11 items-center justify-center' + className='flex h-full w-11 items-center justify-center hover:bg-accent' > - {maximized ? ( - - ) : ( - - )} + {maximized ? : } ) diff --git a/ui/components/PageManagerDialog.tsx b/ui/components/PageManagerDialog.tsx index 65fa1aeb240e4d69abce65f3c233ce578c048ef3..987f481b6f7a7e9290a2c015fd8f96432303ed8c 100644 --- a/ui/components/PageManagerDialog.tsx +++ b/ui/components/PageManagerDialog.tsx @@ -1,6 +1,5 @@ 'use client' -import { useCallback, useEffect, useMemo, useState } from 'react' import { DndContext, KeyboardSensor, @@ -20,35 +19,27 @@ import { import { CSS } from '@dnd-kit/utilities' import { useQueryClient } from '@tanstack/react-query' import { GripVerticalIcon, Loader2Icon } from 'lucide-react' +import { useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' + +import { Button } from '@/components/ui/button' +import { Dialog, DialogContent, DialogDescription, DialogTitle } from '@/components/ui/dialog' import { getGetDocumentThumbnailUrl, getListDocumentsQueryKey, useListDocuments, useReorderDocuments, } from '@/lib/api/documents/documents' -import { Button } from '@/components/ui/button' -import { - Dialog, - DialogContent, - DialogDescription, - DialogTitle, -} from '@/components/ui/dialog' const THUMBNAIL_DPR = - typeof window !== 'undefined' - ? Math.min(Math.ceil(window.devicePixelRatio || 1), 3) - : 2 + typeof window !== 'undefined' ? Math.min(Math.ceil(window.devicePixelRatio || 1), 3) : 2 type PageManagerDialogProps = { open: boolean onOpenChange: (open: boolean) => void } -export function PageManagerDialog({ - open, - onOpenChange, -}: PageManagerDialogProps) { +export function PageManagerDialog({ open, onOpenChange }: PageManagerDialogProps) { const { data: documents = [] } = useListDocuments() const queryClient = useQueryClient() const { t } = useTranslation() @@ -56,10 +47,7 @@ export function PageManagerDialog({ const [orderedIds, setOrderedIds] = useState([]) - const docsById = useMemo( - () => Object.fromEntries(documents.map((d) => [d.id, d])), - [documents], - ) + const docsById = useMemo(() => Object.fromEntries(documents.map((d) => [d.id, d])), [documents]) useEffect(() => { if (open) { @@ -116,9 +104,7 @@ export function PageManagerDialog({
{t('navigator.pageManager.title')} - - {t('navigator.pageManager.description')} - + {t('navigator.pageManager.description')}
@@ -128,28 +114,20 @@ export function PageManagerDialog({ collisionDetection={closestCenter} onDragEnd={handleDragEnd} > - +
{orderedIds.map((id, index) => ( - + ))}
-
+
@@ -180,14 +156,9 @@ type SortablePageCardProps = { } function SortablePageCard({ id, index, name }: SortablePageCardProps) { - const { - attributes, - listeners, - setNodeRef, - transform, - transition, - isDragging, - } = useSortable({ id }) + const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ + id, + }) const style: React.CSSProperties = { transform: CSS.Transform.toString(transform), @@ -215,8 +186,8 @@ function PageCard({ id, index, name, dragging }: PageCardProps) { return (
@@ -228,9 +199,9 @@ function PageCard({ id, index, name, dragging }: PageCardProps) { className='max-h-full max-w-full rounded object-contain' />
-
+
- {index + 1} + {index + 1}
) diff --git a/ui/components/Panels.tsx b/ui/components/Panels.tsx index 1174b18ebb1326b03d04fd1c05c24715a5cf88b4..e569586a4254cf13cb90a285740b6d4f3960c02d 100644 --- a/ui/components/Panels.tsx +++ b/ui/components/Panels.tsx @@ -1,39 +1,32 @@ 'use client' -import { useTranslation } from 'react-i18next' import { LayersIcon, SlidersHorizontalIcon } from 'lucide-react' +import { useTranslation } from 'react-i18next' + import { LayersPanel } from '@/components/panels/LayersPanel' import { RenderControlsPanel } from '@/components/panels/RenderControlsPanel' import { TextBlocksPanel } from '@/components/panels/TextBlocksPanel' -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { ScrollArea } from '@/components/ui/scroll-area' +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' export function Panels() { const { t } = useTranslation() return ( -
+
- - + + {t('layers.title')} - + {t('panels.render')} @@ -56,10 +49,7 @@ export function Panels() { className='min-h-0 flex-1 px-2 pb-2 data-[state=inactive]:hidden' data-testid='panels-layout' > - +
diff --git a/ui/components/SettingsDialog.tsx b/ui/components/SettingsDialog.tsx index 1c3a607043a37a3904f4ddc22ed10dc8d5e406a8..55976f50f5af4f7a2e44e91a0df11a8e3472daa3 100644 --- a/ui/components/SettingsDialog.tsx +++ b/ui/components/SettingsDialog.tsx @@ -1,9 +1,6 @@ 'use client' -import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react' import { useQueryClient } from '@tanstack/react-query' -import { useTheme } from 'next-themes' -import { useTranslation } from 'react-i18next' import { SunIcon, MoonIcon, @@ -21,29 +18,16 @@ import { RotateCcwIcon, AlertTriangleIcon, } from 'lucide-react' -import { - Dialog, - DialogContent, - DialogTitle, - DialogDescription, -} from '@/components/ui/dialog' +import { useTheme } from 'next-themes' +import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react' +import { useTranslation } from 'react-i18next' + import { Accordion, AccordionItem, AccordionTrigger, AccordionContent, } from '@/components/ui/accordion' -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select' -import { ScrollArea } from '@/components/ui/scroll-area' -import { Button } from '@/components/ui/button' -import { Input } from '@/components/ui/input' -import { Label } from '@/components/ui/label' import { AlertDialog, AlertDialogAction, @@ -52,15 +36,29 @@ import { AlertDialogDescription, AlertDialogTitle, } from '@/components/ui/alert-dialog' -import { useUpdater, type UpdaterStatus } from '@/components/Updater' -import { isTauri, openExternalUrl } from '@/lib/backend' +import { Button } from '@/components/ui/button' +import { Dialog, DialogContent, DialogTitle, DialogDescription } from '@/components/ui/dialog' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' +import { ScrollArea } from '@/components/ui/scroll-area' import { - getConfig, - getEngineCatalog, - getMeta, - updateConfig, -} from '@/lib/api/system/system' + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select' +import { useUpdater, type UpdaterStatus } from '@/components/Updater' import { getLlmCatalog, getGetLlmCatalogQueryKey } from '@/lib/api/llm/llm' +import type { + UpdateConfigBody, + ProviderConfig, + LlmProviderCatalog, + GetEngineCatalog200, +} from '@/lib/api/schemas' +import { getConfig, getEngineCatalog, getMeta, updateConfig } from '@/lib/api/system/system' +import { isTauri, openExternalUrl } from '@/lib/backend' +import { supportedLanguages } from '@/lib/i18n' import { getPlatform, formatShortcut, @@ -68,14 +66,7 @@ import { areShortcutsEqual, isModifierKey, } from '@/lib/shortcutUtils' -import { supportedLanguages } from '@/lib/i18n' import { usePreferencesStore } from '@/lib/stores/preferencesStore' -import type { - UpdateConfigBody, - ProviderConfig, - LlmProviderCatalog, - GetEngineCatalog200, -} from '@/lib/api/schemas' const GITHUB_REPO = 'mayocream/koharu' @@ -113,20 +104,15 @@ export function SettingsDialog({ }, [defaultTab, open]) const [appConfig, setAppConfig] = useState(null) - const [providerCatalogs, setProviderCatalogs] = useState< - LlmProviderCatalog[] - >([]) + const [providerCatalogs, setProviderCatalogs] = useState([]) const [apiKeyDrafts, setApiKeyDrafts] = useState>({}) const [dataPathDraft, setDataPathDraft] = useState('') const [httpConnectTimeoutDraft, setHttpConnectTimeoutDraft] = useState('') const [httpReadTimeoutDraft, setHttpReadTimeoutDraft] = useState('') const [httpMaxRetriesDraft, setHttpMaxRetriesDraft] = useState('') - const [storageSettingsError, setStorageSettingsError] = useState< - string | null - >(null) + const [storageSettingsError, setStorageSettingsError] = useState(null) const [isSavingStorageSettings, setIsSavingStorageSettings] = useState(false) - const [engineCatalog, setEngineCatalog] = - useState(null) + const [engineCatalog, setEngineCatalog] = useState(null) const [appVersion, setAppVersion] = useState() const updater = useUpdater() @@ -142,7 +128,7 @@ export function SettingsDialog({ setAppConfig(config) setProviderCatalogs(catalog.providers) setEngineCatalog(engines) - } catch { } + } catch {} })() }, [open]) @@ -174,12 +160,8 @@ export function SettingsDialog({ setHttpConnectTimeoutDraft( String(appConfig.http?.connect_timeout ?? DEFAULT_HTTP_CONNECT_TIMEOUT), ) - setHttpReadTimeoutDraft( - String(appConfig.http?.read_timeout ?? DEFAULT_HTTP_READ_TIMEOUT), - ) - setHttpMaxRetriesDraft( - String(appConfig.http?.max_retries ?? DEFAULT_HTTP_MAX_RETRIES), - ) + setHttpReadTimeoutDraft(String(appConfig.http?.read_timeout ?? DEFAULT_HTTP_READ_TIMEOUT)) + setHttpMaxRetriesDraft(String(appConfig.http?.max_retries ?? DEFAULT_HTTP_MAX_RETRIES)) setStorageSettingsError(null) }, [appConfig]) @@ -196,10 +178,7 @@ export function SettingsDialog({ } } - const upsertProvider = ( - id: string, - updater: (p: ProviderConfig) => ProviderConfig, - ) => { + const upsertProvider = (id: string, updater: (p: ProviderConfig) => ProviderConfig) => { if (!appConfig) return const providers = [...(appConfig.providers ?? [])] const idx = providers.findIndex((p) => p.id === id) @@ -263,13 +242,10 @@ export function SettingsDialog({ const storageSettingsUnchanged = dataPathDraft.trim() === appConfig?.data?.path && httpConnectTimeoutDraft.trim() === - String( - appConfig?.http?.connect_timeout ?? DEFAULT_HTTP_CONNECT_TIMEOUT, - ) && + String(appConfig?.http?.connect_timeout ?? DEFAULT_HTTP_CONNECT_TIMEOUT) && httpReadTimeoutDraft.trim() === - String(appConfig?.http?.read_timeout ?? DEFAULT_HTTP_READ_TIMEOUT) && - httpMaxRetriesDraft.trim() === - String(appConfig?.http?.max_retries ?? DEFAULT_HTTP_MAX_RETRIES) + String(appConfig?.http?.read_timeout ?? DEFAULT_HTTP_READ_TIMEOUT) && + httpMaxRetriesDraft.trim() === String(appConfig?.http?.max_retries ?? DEFAULT_HTTP_MAX_RETRIES) return ( @@ -279,8 +255,8 @@ export function SettingsDialog({
{/* Sidebar */} -