From 51df699ca1ffcc16e8a8a523b761d7b1f4f168ac Mon Sep 17 00:00:00 2001 From: Sourish Ghosh Date: Fri, 6 Mar 2026 21:05:49 +0530 Subject: [PATCH 1/4] Better ci : Husky integration --- .github/workflows/ci.yml | 142 ++++++++- .github/workflows/setup.yml | 28 ++ .husky/pre-commit | 8 + .prettierrc | 7 + lint-staged.config.js | 5 + package-lock.json | 555 ++++++++++++++++++++++++++++++++++++ package.json | 7 +- 7 files changed, 744 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/setup.yml create mode 100644 .husky/pre-commit create mode 100644 .prettierrc create mode 100644 lint-staged.config.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d592b93..738f941 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,10 +7,16 @@ on: branches: [main] jobs: - verify: - name: Verify PR - runs-on: ubuntu-latest + setup: + name: Setup + uses: ./.github/workflows/setup.yml + + # ── Frontend checks (run when src/ or config files change) ── + lint: + name: Lint + needs: setup + runs-on: ubuntu-latest steps: - name: Checkout repo uses: actions/checkout@v4 @@ -18,17 +24,139 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: 18 - cache: npm + node-version: 22 - - name: Install dependencies - run: npm ci + - name: Restore node_modules + uses: actions/cache/restore@v4 + with: + path: node_modules + key: node-modules-${{ github.sha }} - name: Lint run: npm run lint + typecheck: + name: Typecheck + needs: setup + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Restore node_modules + uses: actions/cache/restore@v4 + with: + path: node_modules + key: node-modules-${{ github.sha }} + - name: Typecheck run: npm run typecheck + prettier: + name: Prettier + needs: setup + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Restore node_modules + uses: actions/cache/restore@v4 + with: + path: node_modules + key: node-modules-${{ github.sha }} + + - name: Check formatting + run: npm run format:check + + build: + name: Build (Vite) + needs: setup + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Restore node_modules + uses: actions/cache/restore@v4 + with: + path: node_modules + key: node-modules-${{ github.sha }} + - name: Build run: npm run build + + # ── Tauri build (only when src-tauri/ changes) ── + + check-tauri-changes: + name: Check Tauri Changes + runs-on: ubuntu-latest + outputs: + tauri_changed: ${{ steps.filter.outputs.tauri }} + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Check for src-tauri changes + uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + tauri: + - 'src-tauri/**' + + tauri-build: + name: Build (Tauri) + needs: [build, check-tauri-changes] + if: needs.check-tauri-changes.outputs.tauri_changed == 'true' + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Restore node_modules + uses: actions/cache/restore@v4 + with: + path: node_modules + key: node-modules-${{ github.sha }} + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + + - name: Cache Rust dependencies + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + src-tauri/target + key: rust-${{ runner.os }}-${{ hashFiles('src-tauri/Cargo.lock') }} + restore-keys: rust-${{ runner.os }}- + + - name: Build Tauri app + run: npm run tauri:build diff --git a/.github/workflows/setup.yml b/.github/workflows/setup.yml new file mode 100644 index 0000000..b412130 --- /dev/null +++ b/.github/workflows/setup.yml @@ -0,0 +1,28 @@ +name: Setup + +on: + workflow_call: + +jobs: + setup: + name: Install Dependencies + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Cache node_modules + uses: actions/cache/save@v4 + with: + path: node_modules + key: node-modules-${{ github.sha }} diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..4c1f138 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,8 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +echo "Running Pre-commit Checks (Lint & Format)..." +npx lint-staged + +echo "Running Typecheck..." +npm run typecheck diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..eba5a68 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "singleQuote": true, + "trailingComma": "es5", + "printWidth": 80, + "tabWidth": 2 +} diff --git a/lint-staged.config.js b/lint-staged.config.js new file mode 100644 index 0000000..85ff644 --- /dev/null +++ b/lint-staged.config.js @@ -0,0 +1,5 @@ +export default { + 'src/**/*.{ts,tsx}': ['eslint', 'prettier --check'], + 'src/**/*.css': 'prettier --check', + 'src-tauri/**/*.rs': () => 'cargo fmt --check --manifest-path src-tauri/Cargo.toml', +}; diff --git a/package-lock.json b/package-lock.json index c840416..ec5a918 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,7 +43,10 @@ "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.4.24", "globals": "^16.5.0", + "husky": "^9.1.7", + "lint-staged": "^16.3.2", "postcss": "^8.5.6", + "prettier": "^3.8.1", "tailwindcss": "^4.1.18", "typescript": "~5.9.3", "typescript-eslint": "^8.46.4", @@ -2653,6 +2656,7 @@ "version": "19.2.9", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.9.tgz", "integrity": "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==", + "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -3063,6 +3067,35 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -3161,6 +3194,19 @@ "concat-map": "0.0.1" } }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/browserslist": { "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", @@ -3293,6 +3339,39 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz", + "integrity": "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^8.0.0", + "string-width": "^8.2.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/codemirror": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz", @@ -3328,6 +3407,13 @@ "dev": true, "license": "MIT" }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, "node_modules/comma-separated-tokens": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", @@ -3398,6 +3484,7 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, "license": "MIT" }, "node_modules/debug": { @@ -3476,6 +3563,13 @@ "dev": true, "license": "ISC" }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, "node_modules/enhanced-resolve": { "version": "5.18.4", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz", @@ -3502,6 +3596,19 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/esbuild": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", @@ -3761,6 +3868,13 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "dev": true, + "license": "MIT" + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -3819,6 +3933,19 @@ "node": ">=16.0.0" } }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -3896,6 +4023,19 @@ "node": ">=6.9.0" } }, + "node_modules/get-east-asian-width": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", + "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -4158,6 +4298,22 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4245,6 +4401,22 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -4268,6 +4440,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/is-plain-obj": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", @@ -4665,6 +4847,58 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lint-staged": { + "version": "16.3.2", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.3.2.tgz", + "integrity": "sha512-xKqhC2AeXLwiAHXguxBjuChoTTWFC6Pees0SHPwOpwlvI3BH7ZADFPddAdN3pgo3aiKgPUx/bxE78JfUnxQnlg==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^14.0.3", + "listr2": "^9.0.5", + "micromatch": "^4.0.8", + "string-argv": "^0.3.2", + "tinyexec": "^1.0.2", + "yaml": "^2.8.2" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/listr2": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", + "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^5.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -4688,6 +4922,56 @@ "dev": true, "license": "MIT" }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -5649,6 +5933,46 @@ ], "license": "MIT" }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -5701,6 +6025,22 @@ "dev": true, "license": "MIT" }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -5900,6 +6240,22 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/property-information": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", @@ -6144,6 +6500,30 @@ "node": ">=4" } }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, "node_modules/rollup": { "version": "4.56.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.56.0.tgz", @@ -6228,6 +6608,49 @@ "node": ">=8" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slice-ansi": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz", + "integrity": "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.3", + "is-fullwidth-code-point": "^5.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -6248,6 +6671,33 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.0.tgz", + "integrity": "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/stringify-entities": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", @@ -6262,6 +6712,22 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -6316,6 +6782,7 @@ "version": "4.1.18", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", + "dev": true, "license": "MIT" }, "node_modules/tapable": { @@ -6332,6 +6799,16 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -6349,6 +6826,19 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/trim-lines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", @@ -6761,6 +7251,55 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -6768,6 +7307,22 @@ "dev": true, "license": "ISC" }, + "node_modules/yaml": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 81a9020..5b51c3e 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,9 @@ "lint": "eslint .", "typecheck": "tsc -b", "tauri:dev": "npx tauri dev", - "tauri:build": "npx tauri build" + "tauri:build": "npx tauri build", + "format:check": "prettier --check \"src/**/*.{ts,tsx,css}\"", + "prepare": "husky" }, "dependencies": { "@codemirror/commands": "^6.10.2", @@ -47,7 +49,10 @@ "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.4.24", "globals": "^16.5.0", + "husky": "^9.1.7", + "lint-staged": "^16.3.2", "postcss": "^8.5.6", + "prettier": "^3.8.1", "tailwindcss": "^4.1.18", "typescript": "~5.9.3", "typescript-eslint": "^8.46.4", From 19f0569766307be357934c3bcc93b4adba560f91 Mon Sep 17 00:00:00 2001 From: Sourish Ghosh Date: Fri, 6 Mar 2026 21:07:40 +0530 Subject: [PATCH 2/4] Formate better --- .github/ISSUE_TEMPLATE/bug.yml | 4 +- .github/ISSUE_TEMPLATE/feature_request.yml | 4 +- .husky/pre-commit | 3 +- README.md | 38 +- docs/changelogs/cinder-v0.2.md | 12 + eslint.config.js | 16 +- lint-staged.config.js | 3 +- postcss.config.js | 2 +- src/App.tsx | 18 +- src/components/features/FloatingHub.tsx | 48 +- src/components/features/SearchPanel.tsx | 186 +++---- src/components/features/ThemeSwitcher.tsx | 70 +-- .../features/activity-bar/ActivityBar.tsx | 6 +- .../features/activity-bar/ActivityBarItem.tsx | 18 +- .../features/settings/AccountSettings.tsx | 16 +- .../features/settings/GeneralSettings.tsx | 34 +- .../features/settings/ThemeSettings.tsx | 172 +++--- src/components/layout/MainLayout.tsx | 40 +- src/components/layout/WelcomePage.tsx | 58 +- .../layout/editor/CodeMirrorEditor.tsx | 22 +- src/components/layout/editor/Editor.tsx | 62 +-- src/components/layout/editor/EditorHeader.tsx | 102 ++-- src/components/layout/editor/EditorPane.tsx | 20 +- .../layout/editor/EditorStatusBar.tsx | 32 +- .../layout/editor/EditorStatusBarItem.tsx | 10 +- src/components/layout/editor/EditorTabs.tsx | 78 +-- .../layout/editor/MarkdownPreview.tsx | 30 +- .../layout/editor/markdownStylingPlugin.ts | 40 +- .../layout/explorer/FileExplorer.tsx | 52 +- .../layout/explorer/FileTreeItem.tsx | 110 ++-- .../onboarding/WorkspaceWelcome.tsx | 6 +- src/hooks/useFileSystem.ts | 36 +- src/hooks/useKeyboardShortcuts.ts | 22 +- src/hooks/useWorkspace.ts | 22 +- src/index.css | 12 +- src/main.tsx | 12 +- src/store/useAppStore.ts | 259 ++++----- src/theme/cinderTheme.ts | 509 +++++++++--------- src/theme/editor.css | 10 +- src/theme/markdown.css | 18 +- src/types/fileSystem.ts | 2 +- src/util/codeProtector.ts | 10 +- src/util/contextMenu.ts | 98 ++-- src/util/markdownpipeline.ts | 14 +- src/util/normalizeCodeblock.ts | 8 +- src/util/normalizeLatex.ts | 40 +- src/util/sanitize.ts | 8 +- tailwind.config.js | 6 +- vite.config.ts | 4 +- 49 files changed, 1216 insertions(+), 1186 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index df58d4c..5ff4872 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -1,7 +1,7 @@ name: Bug Report description: File a bug report to help us improve Cinder Notes -title: "[Bug]: " -labels: ["bug", "triage"] +title: '[Bug]: ' +labels: ['bug', 'triage'] body: - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 4a2db6e..356b842 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,7 +1,7 @@ name: Feature Request description: Suggest an idea for this project -title: "[Feat]: " -labels: ["enhancement"] +title: '[Feat]: ' +labels: ['enhancement'] body: - type: markdown attributes: diff --git a/.husky/pre-commit b/.husky/pre-commit index 4c1f138..aa5da76 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,5 +1,4 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" + echo "Running Pre-commit Checks (Lint & Format)..." npx lint-staged diff --git a/README.md b/README.md index 2f6a795..24e0704 100644 --- a/README.md +++ b/README.md @@ -58,15 +58,15 @@ There are plenty of note-taking apps out there, but most are either bloated Elec ### Keyboard Shortcuts -| Shortcut | Action | -| --- | --- | -| Cmd+S | Force save | -| Cmd+N | New file | -| Cmd+Shift+N | New folder | -| Cmd+W | Close current tab | -| Cmd+B | Toggle sidebar | -| Cmd+Shift+F | Global search | -| Cmd+F | Find/replace (in editor) | +| Shortcut | Action | +| ----------- | ------------------------ | +| Cmd+S | Force save | +| Cmd+N | New file | +| Cmd+Shift+N | New folder | +| Cmd+W | Close current tab | +| Cmd+B | Toggle sidebar | +| Cmd+Shift+F | Global search | +| Cmd+F | Find/replace (in editor) | ### Theming & UI @@ -120,16 +120,16 @@ Compiled binaries will be generated in `src-tauri/target/release/`. ## Tech Stack -| Layer | Technology | -| --- | --- | -| Framework | [Tauri](https://tauri.app/) -- Lightweight native apps with a Rust backend | -| Frontend | [React 19](https://react.dev/) + [TypeScript](https://www.typescriptlang.org/) | -| Build | [Vite](https://vite.dev/) | -| Styling | [Tailwind CSS v4](https://tailwindcss.com/) + CSS Variables | -| State | [Zustand](https://github.com/pmndrs/zustand) -- Minimal state management | -| Editor | [CodeMirror](https://codemirror.net/) via [@uiw/react-codemirror](https://github.com/uiwjs/react-codemirror) | -| Markdown | [react-markdown](https://github.com/remarkjs/react-markdown) + remark-gfm + remark-math + rehype-katex + rehype-highlight + rehype-sanitize | -| Icons | [Lucide](https://lucide.dev/) + [React Icons](https://react-icons.github.io/react-icons/) | +| Layer | Technology | +| --------- | ------------------------------------------------------------------------------------------------------------------------------------------- | +| Framework | [Tauri](https://tauri.app/) -- Lightweight native apps with a Rust backend | +| Frontend | [React 19](https://react.dev/) + [TypeScript](https://www.typescriptlang.org/) | +| Build | [Vite](https://vite.dev/) | +| Styling | [Tailwind CSS v4](https://tailwindcss.com/) + CSS Variables | +| State | [Zustand](https://github.com/pmndrs/zustand) -- Minimal state management | +| Editor | [CodeMirror](https://codemirror.net/) via [@uiw/react-codemirror](https://github.com/uiwjs/react-codemirror) | +| Markdown | [react-markdown](https://github.com/remarkjs/react-markdown) + remark-gfm + remark-math + rehype-katex + rehype-highlight + rehype-sanitize | +| Icons | [Lucide](https://lucide.dev/) + [React Icons](https://react-icons.github.io/react-icons/) | **Why Tauri over Electron?** Tauri apps are significantly smaller (~10-20MB vs ~150MB), use less memory, and leverage the OS's native webview. The Rust backend ensures security and performance. diff --git a/docs/changelogs/cinder-v0.2.md b/docs/changelogs/cinder-v0.2.md index 0adfc82..27bbce6 100644 --- a/docs/changelogs/cinder-v0.2.md +++ b/docs/changelogs/cinder-v0.2.md @@ -11,16 +11,19 @@ After auditing the full codebase, here are the features that a notes app **must There is no way to search within a note or across the workspace. For a notes app this is non-negotiable. **Scope:** + - **In-editor find** -- Already handled by CodeMirror's `@codemirror/search` extension. Just needs to be enabled. - **Global search across files** -- A new Tauri command to grep workspace files, plus a search UI panel. #### Frontend + - [MODIFY] [CodeMirrorEditor.tsx](file:///Users/7sg56/Developer/My%20Shit/Desktop_Apps/Cinder-notes/src/components/layout/editor/CodeMirrorEditor.tsx) -- Add `@codemirror/search` extension to enable built-in Cmd+F find/replace inside the editor. - [NEW] [SearchPanel.tsx](file:///Users/7sg56/Developer/My%20Shit/Desktop_Apps/Cinder-notes/src/components/features/SearchPanel.tsx) -- A global search UI (opened via Cmd+Shift+F) that displays results across all workspace files with file name, line number, and content preview. Clicking a result opens the file. - [MODIFY] [useKeyboardShortcuts.ts](file:///Users/7sg56/Developer/My%20Shit/Desktop_Apps/Cinder-notes/src/hooks/useKeyboardShortcuts.ts) -- Add Cmd+Shift+F shortcut to toggle the global search panel. - [MODIFY] [useAppStore.ts](file:///Users/7sg56/Developer/My%20Shit/Desktop_Apps/Cinder-notes/src/store/useAppStore.ts) -- Add `isSearchOpen` / `searchQuery` / `searchResults` state + actions. #### Backend + - [MODIFY] [commands.rs](file:///Users/7sg56/Developer/My%20Shit/Desktop_Apps/Cinder-notes/src-tauri/src/commands.rs) -- Add `search_workspace` command that recursively searches `.md` files for a query string, returning matching file paths, line numbers, and snippets. - [MODIFY] [lib.rs](file:///Users/7sg56/Developer/My%20Shit/Desktop_Apps/Cinder-notes/src-tauri/src/lib.rs) -- Register the new command. @@ -33,10 +36,12 @@ The app loses the workspace every time it restarts. Users have to re-select thei **Scope:** Use Tauri's `tauri-plugin-store` (a JSON key-value store) to persist the last workspace path and restore it on launch. #### Backend / Config + - [MODIFY] [Cargo.toml](file:///Users/7sg56/Developer/My%20Shit/Desktop_Apps/Cinder-notes/src-tauri/Cargo.toml) -- Add `tauri-plugin-store` dependency. - [MODIFY] [lib.rs](file:///Users/7sg56/Developer/My%20Shit/Desktop_Apps/Cinder-notes/src-tauri/src/lib.rs) -- Register the store plugin. #### Frontend + - [MODIFY] [useWorkspace.ts](file:///Users/7sg56/Developer/My%20Shit/Desktop_Apps/Cinder-notes/src/hooks/useWorkspace.ts) -- After loading a workspace, persist the path to the store. On app init, read the stored path and auto-load the workspace. - [MODIFY] [App.tsx](file:///Users/7sg56/Developer/My%20Shit/Desktop_Apps/Cinder-notes/src/App.tsx) -- On mount, attempt to restore the last workspace before showing the welcome screen. @@ -49,6 +54,7 @@ Currently `deleteFile` and `deleteFolder` execute immediately with no confirmati **Scope:** Show a native Tauri confirmation dialog before deleting files/folders. #### Frontend + - [MODIFY] [useAppStore.ts](file:///Users/7sg56/Developer/My%20Shit/Desktop_Apps/Cinder-notes/src/store/useAppStore.ts) -- Wrap `deleteFile` and `deleteFolder` with `await ask()` from `@tauri-apps/plugin-dialog` so the user must confirm before deletion. --- @@ -60,11 +66,13 @@ Currently `deleteFile` and `deleteFolder` execute immediately with no confirmati The app auto-saves, but when auto-save is off (manual mode), there is no visual indicator that a file has unsaved changes, and closing a tab silently discards edits. **Scope:** + - Track a `dirtyFiles` set in the store (files with unsaved changes). - Show a dot/indicator on dirty tabs. - Prompt before closing a dirty tab. #### Frontend + - [MODIFY] [useAppStore.ts](file:///Users/7sg56/Developer/My%20Shit/Desktop_Apps/Cinder-notes/src/store/useAppStore.ts) -- Add `dirtyFiles: Set` state. Mark files dirty on edit, clean on save. Before closing a dirty file, prompt with a save dialog. - [MODIFY] [EditorTabs.tsx](file:///Users/7sg56/Developer/My%20Shit/Desktop_Apps/Cinder-notes/src/components/layout/editor/EditorTabs.tsx) -- Show a dot indicator on tabs that have unsaved changes. @@ -77,6 +85,7 @@ Users need to be able to export their notes. At minimum: copy as Markdown and ex **Scope:** Add export options to the editor's `MoreVertical` menu (which is currently a no-op button). #### Frontend + - [MODIFY] [EditorHeader.tsx](file:///Users/7sg56/Developer/My%20Shit/Desktop_Apps/Cinder-notes/src/components/layout/editor/EditorHeader.tsx) -- Wire the `MoreVertical` button to show a dropdown with "Export as Markdown" and "Export as HTML" options. - [NEW] [exportUtils.ts](file:///Users/7sg56/Developer/My%20Shit/Desktop_Apps/Cinder-notes/src/util/exportUtils.ts) -- Utility functions: `exportAsMarkdown` (save `.md` file via Tauri save dialog) and `exportAsHTML` (render the markdown to HTML with styles, save via Tauri save dialog). @@ -89,6 +98,7 @@ The `GeneralSettings` panel is a visual shell -- the dropdowns don't persist or **Scope:** For v1/production, the simplest approach is to make the settings functional or clearly mark them as "Coming Soon." #### Frontend + - [MODIFY] [GeneralSettings.tsx](file:///Users/7sg56/Developer/My%20Shit/Desktop_Apps/Cinder-notes/src/components/features/settings/GeneralSettings.tsx) -- Either connect the dropdowns to persisted state (via `tauri-plugin-store`), or add "Coming Soon" badges and disable the selects. --- @@ -126,10 +136,12 @@ Allow users to pin frequently accessed notes to the top of the explorer or tabs. ## Verification Plan ### Automated Tests + - The Rust `workspace.rs` already has unit tests. New `search_workspace` logic will get similar tests using `tempdir` + test files. - Run Rust tests: `cd src-tauri && cargo test` ### Manual Verification + - **Search:** Open the app, press Cmd+F in editor to verify in-file search works. Press Cmd+Shift+F to verify global search panel opens and returns results. - **Workspace Persistence:** Open a workspace, quit the app, relaunch -- verify it auto-loads the last workspace without prompting. - **Delete Confirmation:** Right-click a file, click "Move to Trash" -- verify a native confirmation dialog appears before deletion. diff --git a/eslint.config.js b/eslint.config.js index 539f150..78d8388 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,14 +1,14 @@ -import js from "@eslint/js"; -import globals from "globals"; -import reactHooks from "eslint-plugin-react-hooks"; -import reactRefresh from "eslint-plugin-react-refresh"; -import tseslint from "typescript-eslint"; -import { defineConfig } from "eslint/config"; +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; +import { defineConfig } from 'eslint/config'; export default defineConfig([ - { ignores: ["dist", "src-tauri/**"] }, + { ignores: ['dist', 'src-tauri/**'] }, { - files: ["**/*.{ts,tsx}"], + files: ['**/*.{ts,tsx}'], extends: [ js.configs.recommended, tseslint.configs.recommended, diff --git a/lint-staged.config.js b/lint-staged.config.js index 85ff644..e3ae5cd 100644 --- a/lint-staged.config.js +++ b/lint-staged.config.js @@ -1,5 +1,6 @@ export default { 'src/**/*.{ts,tsx}': ['eslint', 'prettier --check'], 'src/**/*.css': 'prettier --check', - 'src-tauri/**/*.rs': () => 'cargo fmt --check --manifest-path src-tauri/Cargo.toml', + 'src-tauri/**/*.rs': () => + 'cargo fmt --check --manifest-path src-tauri/Cargo.toml', }; diff --git a/postcss.config.js b/postcss.config.js index f69c5d4..51a6e4e 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,6 +1,6 @@ export default { plugins: { - "@tailwindcss/postcss": {}, + '@tailwindcss/postcss': {}, autoprefixer: {}, }, }; diff --git a/src/App.tsx b/src/App.tsx index cc80ad3..c4524a2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,12 +1,12 @@ -import "./App.css"; -import { MainLayout } from "./components/layout/MainLayout"; -import { FileExplorer } from "./components/layout/explorer/FileExplorer"; -import { EditorPane } from "./components/layout/editor/EditorPane"; -import { FloatingHub } from "./components/features/FloatingHub"; -import { SearchPanel } from "./components/features/SearchPanel"; -import { WorkspaceWelcome } from "./components/onboarding/WorkspaceWelcome"; -import { useAppStore } from "./store/useAppStore"; -import { useKeyboardShortcuts } from "./hooks/useKeyboardShortcuts"; +import './App.css'; +import { MainLayout } from './components/layout/MainLayout'; +import { FileExplorer } from './components/layout/explorer/FileExplorer'; +import { EditorPane } from './components/layout/editor/EditorPane'; +import { FloatingHub } from './components/features/FloatingHub'; +import { SearchPanel } from './components/features/SearchPanel'; +import { WorkspaceWelcome } from './components/onboarding/WorkspaceWelcome'; +import { useAppStore } from './store/useAppStore'; +import { useKeyboardShortcuts } from './hooks/useKeyboardShortcuts'; function App() { const workspacePath = useAppStore((state) => state.workspacePath); diff --git a/src/components/features/FloatingHub.tsx b/src/components/features/FloatingHub.tsx index 0fd1521..a3277f2 100644 --- a/src/components/features/FloatingHub.tsx +++ b/src/components/features/FloatingHub.tsx @@ -1,7 +1,7 @@ -import { useState, useEffect, useRef } from "react"; -import { createPortal } from "react-dom"; -import { User, Palette, Settings } from "lucide-react"; -import { useAppStore } from "../../store/useAppStore"; +import { useState, useEffect, useRef } from 'react'; +import { createPortal } from 'react-dom'; +import { User, Palette, Settings } from 'lucide-react'; +import { useAppStore } from '../../store/useAppStore'; export function FloatingHub() { const [isOpen, setIsOpen] = useState(false); @@ -16,18 +16,18 @@ export function FloatingHub() { return; } const target = e.target as HTMLElement; - if (target.closest(".hub-menu")) { + if (target.closest('.hub-menu')) { return; } setIsOpen(false); }; if (isOpen) { - document.addEventListener("mousedown", handleClickOutside); + document.addEventListener('mousedown', handleClickOutside); } return () => { - document.removeEventListener("mousedown", handleClickOutside); + document.removeEventListener('mousedown', handleClickOutside); }; }, [isOpen]); @@ -42,14 +42,14 @@ export function FloatingHub() { role="button" className="fixed bottom-5 left-5 z-[99999] w-14 h-14 rounded-full flex items-center justify-center cursor-pointer transition-all duration-200 ease-out group shadow-xl hover:scale-110 active:scale-95" style={{ - backgroundColor: "var(--bg-tertiary)", // Provide a background + backgroundColor: 'var(--bg-tertiary)', // Provide a background border: isOpen || isHovered - ? "1px solid var(--editor-header-accent)" - : "1px solid var(--border-primary)", + ? '1px solid var(--editor-header-accent)' + : '1px solid var(--border-primary)', boxShadow: - isOpen || isHovered ? "0 0 15px var(--accent-glow)" : "none", - overflow: "hidden", // Clip the square image to circle + isOpen || isHovered ? '0 0 15px var(--accent-glow)' : 'none', + overflow: 'hidden', // Clip the square image to circle }} title="Cinder Hub" > @@ -58,7 +58,7 @@ export function FloatingHub() { alt="Cinder Hub" className="w-full h-full object-cover" style={{ - filter: isOpen ? "brightness(1.1)" : "brightness(1)", + filter: isOpen ? 'brightness(1.1)' : 'brightness(1)', }} /> @@ -69,16 +69,16 @@ export function FloatingHub() {
Cinder Hub
@@ -86,11 +86,11 @@ export function FloatingHub() {
, - document.body, + document.body )} ); diff --git a/src/components/features/SearchPanel.tsx b/src/components/features/SearchPanel.tsx index d1b466b..c7c479d 100644 --- a/src/components/features/SearchPanel.tsx +++ b/src/components/features/SearchPanel.tsx @@ -1,7 +1,7 @@ -import { useEffect, useRef, useState, useCallback } from "react"; -import { invoke } from "@tauri-apps/api/core"; -import { useAppStore, type SearchResult } from "../../store/useAppStore"; -import { Search, FileText, X } from "lucide-react"; +import { useEffect, useRef, useState, useCallback } from 'react'; +import { invoke } from '@tauri-apps/api/core'; +import { useAppStore, type SearchResult } from '../../store/useAppStore'; +import { Search, FileText, X } from 'lucide-react'; export function SearchPanel() { const { @@ -37,19 +37,19 @@ export function SearchPanel() { setIsLoading(true); try { - const results = await invoke("search_workspace", { + const results = await invoke('search_workspace', { path: workspacePath, query: query.trim(), }); setSearchResults(results); } catch (error) { - console.error("Search failed:", error); + console.error('Search failed:', error); setSearchResults([]); } finally { setIsLoading(false); } }, - [workspacePath, setSearchResults], + [workspacePath, setSearchResults] ); // Debounce user input @@ -65,29 +65,29 @@ export function SearchPanel() { return (
setSearchOpen(false)} >
e.stopPropagation()} @@ -95,18 +95,18 @@ export function SearchPanel() { {/* Search input row */}
@@ -117,19 +117,19 @@ export function SearchPanel() { value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} onKeyDown={(e) => { - if (e.key === "Escape") { + if (e.key === 'Escape') { e.preventDefault(); setSearchOpen(false); } }} style={{ flex: 1, - backgroundColor: "transparent", - color: "var(--text-primary)", - border: "none", - outline: "none", - fontSize: "15px", - fontFamily: "inherit", + backgroundColor: 'transparent', + color: 'var(--text-primary)', + border: 'none', + outline: 'none', + fontSize: '15px', + fontFamily: 'inherit', }} /> {isLoading && ( @@ -137,10 +137,10 @@ export function SearchPanel() { style={{ width: 16, height: 16, - border: "2px solid var(--editor-header-accent)", - borderTopColor: "transparent", - borderRadius: "50%", - animation: "spin 0.6s linear infinite", + border: '2px solid var(--editor-header-accent)', + borderTopColor: 'transparent', + borderRadius: '50%', + animation: 'spin 0.6s linear infinite', flexShrink: 0, }} /> @@ -148,23 +148,23 @@ export function SearchPanel() {
{/* Results */} -
+
{searchResults.length > 0 ? ( -
+
{searchResults.map((result, i) => { const isHovered = hoveredIndex === i; return ( @@ -111,17 +111,17 @@ export function ThemeSwitcher() {
Studio Themes
@@ -138,13 +138,13 @@ export function ThemeSwitcher() { style={{ color: currentTheme === theme.value - ? "var(--editor-header-accent)" - : "var(--text-primary)", + ? 'var(--editor-header-accent)' + : 'var(--text-primary)', }} > {theme.name} @@ -156,7 +156,7 @@ export function ThemeSwitcher() { ))}
, - document.body, + document.body )} ); diff --git a/src/components/features/activity-bar/ActivityBar.tsx b/src/components/features/activity-bar/ActivityBar.tsx index a7858b7..aaa485c 100644 --- a/src/components/features/activity-bar/ActivityBar.tsx +++ b/src/components/features/activity-bar/ActivityBar.tsx @@ -5,15 +5,15 @@ import { CircleUser, Lock, Settings, -} from "lucide-react"; -import { ActivityBarItem } from "./ActivityBarItem"; +} from 'lucide-react'; +import { ActivityBarItem } from './ActivityBarItem'; export function ActivityBar() { return (
diff --git a/src/components/features/activity-bar/ActivityBarItem.tsx b/src/components/features/activity-bar/ActivityBarItem.tsx index 1b6b00f..9d0a5d9 100644 --- a/src/components/features/activity-bar/ActivityBarItem.tsx +++ b/src/components/features/activity-bar/ActivityBarItem.tsx @@ -1,4 +1,4 @@ -import type { LucideIcon } from "lucide-react"; +import type { LucideIcon } from 'lucide-react'; interface ActivityBarItemProps { icon: LucideIcon; @@ -11,26 +11,26 @@ export function ActivityBarItem({ }: ActivityBarItemProps) { return (
{ if (!active) { - e.currentTarget.style.color = "var(--activity-item-text-hover)"; + e.currentTarget.style.color = 'var(--activity-item-text-hover)'; } }} onMouseLeave={(e) => { if (!active) { - e.currentTarget.style.color = "var(--activity-item-text-default)"; + e.currentTarget.style.color = 'var(--activity-item-text-default)'; } }} > diff --git a/src/components/features/settings/AccountSettings.tsx b/src/components/features/settings/AccountSettings.tsx index 85a0283..70bc31e 100644 --- a/src/components/features/settings/AccountSettings.tsx +++ b/src/components/features/settings/AccountSettings.tsx @@ -1,5 +1,5 @@ -import { useRef } from "react"; -import { User, Shield, KeyRound, Globe, Save } from "lucide-react"; +import { useRef } from 'react'; +import { User, Shield, KeyRound, Globe, Save } from 'lucide-react'; export function AccountSettings() { const nameRef = useRef(null); @@ -88,15 +88,15 @@ export function AccountSettings() { {[ { icon: KeyRound, - title: "Password", - desc: "Last changed 3 months ago", - action: "Update", + title: 'Password', + desc: 'Last changed 3 months ago', + action: 'Update', }, { icon: Shield, - title: "Two-Factor Authentication", - desc: "Currently disabled", - action: "Enable", + title: 'Two-Factor Authentication', + desc: 'Currently disabled', + action: 'Enable', }, ].map((item, idx) => (
(
(
localStorage.getItem("cinder-theme") || "", + () => localStorage.getItem('cinder-theme') || '' ); - const [colorMode, setColorMode] = useState<"light" | "dark" | "system">( - "dark", + const [colorMode, setColorMode] = useState<'light' | 'dark' | 'system'>( + 'dark' ); // Simplified for now // Apply theme changes @@ -117,7 +117,7 @@ export function ThemeSettings() { if (currentTheme) { document.documentElement.classList.add(currentTheme); } - localStorage.setItem("cinder-theme", currentTheme); + localStorage.setItem('cinder-theme', currentTheme); }, [currentTheme]); return ( @@ -157,34 +157,34 @@ export function ThemeSettings() {
{[ - { id: "light", label: "Light mode", icon: Sun }, - { id: "dark", label: "Dark mode", icon: Moon }, - { id: "system", label: "System", icon: Monitor }, + { id: 'light', label: 'Light mode', icon: Sun }, + { id: 'dark', label: 'Dark mode', icon: Moon }, + { id: 'system', label: 'System', icon: Monitor }, ].map((mode) => (