Removed notes and removed files that should be ignored.
This commit is contained in:
parent
a62e2d744d
commit
d1d436b020
19 changed files with 88 additions and 609 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
backend/notes.db
BIN
backend/notes.db
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><sqlb_project><db path="notes.db" readonly="0" foreign_keys="1" case_sensitive_like="0" temp_store="0" wal_autocheckpoint="1000" synchronous="2"/><attached/><window><main_tabs open="structure browser pragmas query" current="1"/></window><tab_structure><column_width id="0" width="300"/><column_width id="1" width="0"/><column_width id="2" width="100"/><column_width id="3" width="2087"/><column_width id="4" width="0"/><expanded_item id="0" parent="1"/><expanded_item id="1" parent="1"/><expanded_item id="2" parent="1"/><expanded_item id="3" parent="1"/></tab_structure><tab_browse><table title="user" custom_title="0" dock_id="1" table="4,4:mainuser"/><dock_state state="000000ff00000000fd00000001000000020000030e000004effc0100000001fb000000160064006f0063006b00420072006f007700730065003101000000000000030e0000012000ffffff000002a80000000000000004000000040000000800000008fc00000000"/><default_encoding codec=""/><browse_table_settings><table schema="main" name="folder" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths><column index="1" value="23"/><column index="2" value="187"/><column index="3" value="64"/><column index="4" value="210"/><column index="5" value="53"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table><table schema="main" name="session" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths><column index="1" value="23"/><column index="2" value="300"/><column index="3" value="53"/><column index="4" value="210"/><column index="5" value="210"/><column index="6" value="73"/><column index="7" value="300"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table><table schema="main" name="user" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths><column index="1" value="23"/><column index="2" value="67"/><column index="3" value="124"/><column index="4" value="300"/><column index="5" value="179"/><column index="6" value="210"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table></browse_table_settings></tab_browse><tab_sql><sql name="SQL 1"></sql><current_tab id="0"/></tab_sql></sqlb_project>
|
||||
45
frontend/package-lock.json
generated
45
frontend/package-lock.json
generated
|
|
@ -151,7 +151,6 @@
|
|||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
|
|
@ -728,7 +727,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.3.tgz",
|
||||
"integrity": "sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.23.0",
|
||||
|
|
@ -818,7 +816,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz",
|
||||
"integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@marijn/find-cluster-break": "^1.0.0"
|
||||
}
|
||||
|
|
@ -828,7 +825,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.8.tgz",
|
||||
"integrity": "sha512-XcE9fcnkHCbWkjeKyi0lllwXmBLtyYb5dt89dJyx23I9+LSh5vZDIuk7OLG4VM1lgrXZQcY6cxyZyk5WVPRv/A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.5.0",
|
||||
"crelt": "^1.0.6",
|
||||
|
|
@ -979,7 +975,6 @@
|
|||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
|
|
@ -1026,7 +1021,6 @@
|
|||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
|
|
@ -1503,7 +1497,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz",
|
||||
"integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@floating-ui/core": "^1.7.3",
|
||||
"@floating-ui/utils": "^0.2.10"
|
||||
|
|
@ -1557,7 +1550,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-7.1.0.tgz",
|
||||
"integrity": "sha512-fNxRUk1KhjSbnbuBxlWSnBLKLBNun52ZBTcs22H/xEEzM6Ap81ZFTQ4bZBxVQGQgVY0xugKGoRcCbaKjLQ3XZA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-common-types": "7.1.0"
|
||||
},
|
||||
|
|
@ -1922,7 +1914,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz",
|
||||
"integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.3.0"
|
||||
}
|
||||
|
|
@ -3510,7 +3501,6 @@
|
|||
"integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.21.3",
|
||||
"@svgr/babel-preset": "8.1.0",
|
||||
|
|
@ -3861,7 +3851,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.12.tgz",
|
||||
"integrity": "sha512-graRZspg7EoEaw0a8faiUASCyJrqjKPdqJ9EwuDRUF9mEYJ1YPczI9H+/agJ0mOJkPCJDk0lsz5QTrLZ/jQ2rg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@tanstack/query-core": "5.90.12"
|
||||
},
|
||||
|
|
@ -3985,7 +3974,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.12.1.tgz",
|
||||
"integrity": "sha512-dn5uTnsTUjMze26iRhcus8+2auW9+/vOpk6suXg/lhBp+UzOM+EALKE3S5086ANJNgBh1PDHoBX+r1T7wEmheg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
|
|
@ -4208,7 +4196,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.12.1.tgz",
|
||||
"integrity": "sha512-v3WC9TR8QRVwmubuKjUplAXeTzTq2hiVKGHBbW15LTqqfsEJwt1YHUl/Sc+pSAeJfY7th5wheNfZFCsCBCW3qg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
|
|
@ -4327,7 +4314,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.12.1.tgz",
|
||||
"integrity": "sha512-Xtg2Ot3oebg6+ponJ3yp8VcxPtdaHaub62Eoh8DKvBexyfqp+lMDtOpJZXA9NImVG3gKn+5EAIq8kx5AtrVlJQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
|
|
@ -4342,7 +4328,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.12.1.tgz",
|
||||
"integrity": "sha512-YGv8uZrTraXzB3DPQYsyIB90Girx5QZdZOBSDj0R2bWSXc2Huqdb9PaulXqDQjEv/dp9x6w6+Q2VNIagCPUQwA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"prosemirror-changeset": "^2.3.0",
|
||||
"prosemirror-collab": "^1.3.1",
|
||||
|
|
@ -4436,7 +4421,8 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
|
||||
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
"version": "7.20.5",
|
||||
|
|
@ -4583,7 +4569,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.6.tgz",
|
||||
"integrity": "sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.2.2"
|
||||
}
|
||||
|
|
@ -4593,7 +4578,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
|
||||
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.2.0"
|
||||
}
|
||||
|
|
@ -4707,7 +4691,6 @@
|
|||
"integrity": "sha512-sxSyJMaKp45zI0u+lHrPuZM1ZJQ8FaVD35k+UxVrha1yyvQ+TZuUYllUixwvQXlB7ixoDc7skf3lQPopZIvaQw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vitest/utils": "4.0.15",
|
||||
"fflate": "^0.8.2",
|
||||
|
|
@ -4743,7 +4726,6 @@
|
|||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
|
|
@ -4792,6 +4774,7 @@
|
|||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
|
|
@ -4802,6 +4785,7 @@
|
|||
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
|
|
@ -4941,7 +4925,6 @@
|
|||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.8.25",
|
||||
"caniuse-lite": "^1.0.30001754",
|
||||
|
|
@ -5401,7 +5384,8 @@
|
|||
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
|
||||
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/dot-case": {
|
||||
"version": "3.0.4",
|
||||
|
|
@ -6224,6 +6208,7 @@
|
|||
"resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz",
|
||||
"integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "GitHub Sponsors ❤",
|
||||
"url": "https://github.com/sponsors/dmonad"
|
||||
|
|
@ -6272,7 +6257,6 @@
|
|||
"integrity": "sha512-GtldT42B8+jefDUC4yUKAvsaOrH7PDHmZxZXNgF2xMmymjUbRYJvpAybZAKEmXDGTM0mCsz8duOa4vTm5AY2Kg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@acemir/cssom": "^0.9.28",
|
||||
"@asamuzakjp/dom-selector": "^6.7.6",
|
||||
|
|
@ -6379,6 +6363,7 @@
|
|||
"resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.114.tgz",
|
||||
"integrity": "sha512-gcxmNFzA4hv8UYi8j43uPlQ7CGcyMJ2KQb5kZASw6SnAKAf10hK12i2fjrS3Cl/ugZa5Ui6WwIu1/6MIXiHttQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"isomorphic.js": "^0.2.4"
|
||||
},
|
||||
|
|
@ -8111,7 +8096,6 @@
|
|||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.11",
|
||||
"picocolors": "^1.1.1",
|
||||
|
|
@ -8140,6 +8124,7 @@
|
|||
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1",
|
||||
"ansi-styles": "^5.0.0",
|
||||
|
|
@ -8293,7 +8278,6 @@
|
|||
"resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz",
|
||||
"integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"orderedmap": "^2.0.0"
|
||||
}
|
||||
|
|
@ -8323,7 +8307,6 @@
|
|||
"resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz",
|
||||
"integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-transform": "^1.0.0",
|
||||
|
|
@ -8384,7 +8367,6 @@
|
|||
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.4.tgz",
|
||||
"integrity": "sha512-WkKgnyjNncri03Gjaz3IFWvCAE94XoiEgvtr0/r2Xw7R8/IjK3sKLSiDoCHWcsXSAinVaKlGRZDvMCsF1kbzjA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.20.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
|
|
@ -8421,7 +8403,6 @@
|
|||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
},
|
||||
|
|
@ -8443,7 +8424,6 @@
|
|||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.23.2"
|
||||
|
|
@ -8938,8 +8918,7 @@
|
|||
"version": "4.1.17",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz",
|
||||
"integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.3.0",
|
||||
|
|
@ -9128,7 +9107,6 @@
|
|||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
|
@ -9359,7 +9337,6 @@
|
|||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
|
||||
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.21.3",
|
||||
"postcss": "^8.4.43",
|
||||
|
|
@ -9435,7 +9412,6 @@
|
|||
"integrity": "sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vitest/expect": "4.0.15",
|
||||
"@vitest/mocker": "4.0.15",
|
||||
|
|
@ -9984,7 +9960,6 @@
|
|||
"integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.5.0",
|
||||
|
|
|
|||
|
|
@ -1,89 +0,0 @@
|
|||
import { client } from "./client";
|
||||
import { components } from "@/types/api";
|
||||
import { encryptString, decryptTagTree } from "./encryption";
|
||||
import { useAuthStore } from "../stores/authStore";
|
||||
import { CamelCasedPropertiesDeep } from "type-fest";
|
||||
|
||||
export type Tag = CamelCasedPropertiesDeep<components["schemas"]["Tag"]>;
|
||||
|
||||
export type TagTreeNode = CamelCasedPropertiesDeep<
|
||||
components["schemas"]["TagTreeNode"]
|
||||
>;
|
||||
export type TagCreate = CamelCasedPropertiesDeep<
|
||||
components["schemas"]["TagCreate"]
|
||||
>;
|
||||
export type TagRead = CamelCasedPropertiesDeep<
|
||||
components["schemas"]["TagRead"]
|
||||
>;
|
||||
export type TagTreeResponse = CamelCasedPropertiesDeep<
|
||||
components["schemas"]["TagTreeResponse"]
|
||||
>;
|
||||
|
||||
const fetchTags = async () => {
|
||||
const encryptionKey = useAuthStore.getState().encryptionKey;
|
||||
if (!encryptionKey) throw new Error("Not authenticated");
|
||||
|
||||
const response = await client.GET("/api/tags/tree", {});
|
||||
|
||||
if (response.error) throw new Error("Failed to fetch tags");
|
||||
if (!response.data) throw new Error("No data returned");
|
||||
|
||||
const data = response.data;
|
||||
|
||||
const tags = decryptTagTree(data.tags as any, encryptionKey);
|
||||
return tags;
|
||||
};
|
||||
|
||||
const createTag = async (tag: TagCreate): Promise<TagTreeNode> => {
|
||||
const encryptionKey = useAuthStore.getState().encryptionKey;
|
||||
if (!encryptionKey) throw new Error("Not authenticated");
|
||||
|
||||
const tagName = await encryptString(tag.name, encryptionKey);
|
||||
|
||||
// Use the exact structure from TagCreate schema
|
||||
const { data, error } = await client.POST("/api/tags/", {
|
||||
body: {
|
||||
name: tagName,
|
||||
parentId: tag.parentId || null,
|
||||
},
|
||||
});
|
||||
|
||||
if (error) throw new Error("Failed to create tag");
|
||||
return data as unknown as TagTreeNode;
|
||||
};
|
||||
|
||||
const addTagToNote = async (tagId: number, noteId: number) => {
|
||||
const { data, error } = await client.POST(
|
||||
"/api/tags/note/{note_id}/tag/{tag_id}",
|
||||
{
|
||||
params: {
|
||||
path: {
|
||||
note_id: noteId,
|
||||
tag_id: tagId,
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (error) throw new Error("Failed to add tag to note");
|
||||
return data;
|
||||
};
|
||||
|
||||
const deleteTag = async (tagId: number) => {
|
||||
const { error } = await client.DELETE("/api/tags/{tag_id}", {
|
||||
params: {
|
||||
path: {
|
||||
tag_id: tagId,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (error) throw new Error("Failed to delete tag");
|
||||
};
|
||||
|
||||
export const tagsApi = {
|
||||
list: fetchTags,
|
||||
create: createTag,
|
||||
addToNote: addTagToNote,
|
||||
delete: deleteTag,
|
||||
};
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { Tag, TagCreate, TagRead, tagsApi } from "@/api/tags";
|
||||
import { useAuthStore } from "@/stores/authStore";
|
||||
import { DecryptedTagNode } from "@/api/encryption";
|
||||
|
||||
export const useTagTree = () => {
|
||||
const { encryptionKey } = useAuthStore();
|
||||
|
||||
return useQuery({
|
||||
queryKey: ["tags", "tree"],
|
||||
queryFn: tagsApi.list,
|
||||
enabled: !!encryptionKey,
|
||||
});
|
||||
};
|
||||
|
||||
export const useCreateTag = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (tag: TagCreate) => tagsApi.create(tag),
|
||||
|
||||
onMutate: async (newTag) => {
|
||||
await queryClient.cancelQueries({ queryKey: ["tags", "tree"] });
|
||||
|
||||
const previousTags = queryClient.getQueryData(["tags", "tree"]);
|
||||
|
||||
queryClient.setQueryData(["tags", "tree"], (old: Tag[] | undefined) => {
|
||||
const tempTag: DecryptedTagNode = {
|
||||
id: -Date.now(),
|
||||
name: newTag.name,
|
||||
parentId: newTag.parentId,
|
||||
parentPath: "",
|
||||
createdAt: new Date().toISOString(),
|
||||
children: [],
|
||||
};
|
||||
return [...(old || []), tempTag];
|
||||
});
|
||||
|
||||
return { previousTags };
|
||||
},
|
||||
|
||||
onError: (err, newTag, context) => {
|
||||
queryClient.setQueryData(["tags", "tree"], context?.previousTags);
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["tags", "tree"] });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useAddTagToNote = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ tagId, noteId }: { tagId: number; noteId: number }) =>
|
||||
tagsApi.addToNote(tagId, noteId),
|
||||
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["tags", "tree"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["folders", "tree"] });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
@ -7,10 +7,8 @@ import { Login } from "../Login";
|
|||
import { TiptapEditor } from "../TipTap";
|
||||
import { Sidebar } from "./components/sidebar/SideBar";
|
||||
import { StatusIndicator } from "./components/StatusIndicator";
|
||||
import { useAddTagToNote, useCreateTag, useTagTree } from "@/hooks/useTags";
|
||||
import { useFolderTree, useUpdateNote } from "@/hooks/useFolders";
|
||||
import { Note, NoteRead } from "@/api/notes";
|
||||
import { DecryptedTagNode } from "@/api/encryption";
|
||||
import { useUpdateNote } from "@/hooks/useFolders";
|
||||
import { NoteRead } from "@/api/notes";
|
||||
// @ts-ignore
|
||||
import XmarkIcon from "@/assets/fontawesome/svg/xmark.svg?react";
|
||||
// @ts-ignore
|
||||
|
|
@ -31,7 +29,6 @@ function Home() {
|
|||
const { showModal, setUpdating, selectedNote } = useUIStore();
|
||||
const newFolderRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const folderTree = useFolderTree();
|
||||
const updateNoteMutation = useUpdateNote();
|
||||
|
||||
// Sync editingNote with selectedNote when selection changes
|
||||
|
|
@ -138,23 +135,6 @@ function Home() {
|
|||
onChange={(e) => setTitle(e.target.value)}
|
||||
className="w-full p-4 pb-0 text-3xl font-semibold bg-transparent focus:outline-none border-transparent focus:border-ctp-mauve transition-colors placeholder:text-ctp-overlay0 text-ctp-text"
|
||||
/>
|
||||
<TagSelector
|
||||
editingNote={editingNote}
|
||||
setEditingNote={setEditingNote}
|
||||
/>
|
||||
<div className="px-4 py-2 border-ctp-surface2 flex items-center gap-2 flex-wrap">
|
||||
{editingNote?.tags &&
|
||||
editingNote.tags.map((tag) => (
|
||||
<button
|
||||
onClick={() => null}
|
||||
key={tag.id}
|
||||
className="bg-ctp-surface0 hover:bg-ctp-surface1 px-2 py-0.5 text-sm rounded-full transition-colors"
|
||||
>
|
||||
{tag.parentId && "..."}
|
||||
{tag.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<TiptapEditor
|
||||
key={editingNote?.id}
|
||||
|
|
@ -197,310 +177,7 @@ const Modal = () => {
|
|||
<XmarkIcon className="w-5 h-5 fill-ctp-overlay0 group-hover:fill-ctp-text transition-colors" />
|
||||
</button>
|
||||
<Login />
|
||||
{/*<TagSelector />*/}
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
|
||||
export const TagSelector = ({
|
||||
editingNote,
|
||||
setEditingNote,
|
||||
}: {
|
||||
editingNote: NoteRead | null;
|
||||
setEditingNote: (note: NoteRead | null) => void;
|
||||
}) => {
|
||||
const [value, setValue] = useState("");
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const { data: tagTree, isLoading, error } = useTagTree();
|
||||
const createTag = useCreateTag();
|
||||
const addTagToNote = useAddTagToNote();
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
// Parse path from input (using > as separator)
|
||||
const parsedPath = value.includes(">")
|
||||
? value
|
||||
.split(">")
|
||||
.map((part) => part.trim())
|
||||
.filter(Boolean)
|
||||
: null;
|
||||
|
||||
// Filter existing tags based on search
|
||||
const filteredTags = tagTree
|
||||
? tagTree.filter((tag) => {
|
||||
if (value === "") return false;
|
||||
// Don't show filtered tags if user is typing a path
|
||||
if (parsedPath) return false;
|
||||
return (tag.name + tag.parentPath)
|
||||
.toLowerCase()
|
||||
.includes(value.toLowerCase());
|
||||
})
|
||||
: [];
|
||||
|
||||
// Close when clicking outside the entire component
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (
|
||||
containerRef.current &&
|
||||
!containerRef.current.contains(event.target as Node)
|
||||
) {
|
||||
handleClose();
|
||||
}
|
||||
};
|
||||
|
||||
if (expanded) {
|
||||
document.addEventListener("mousedown", handleClickOutside);
|
||||
return () =>
|
||||
document.removeEventListener("mousedown", handleClickOutside);
|
||||
}
|
||||
}, [expanded]);
|
||||
|
||||
// Focus input when expanded
|
||||
useEffect(() => {
|
||||
if (expanded && inputRef.current) {
|
||||
inputRef.current.focus();
|
||||
}
|
||||
}, [expanded]);
|
||||
|
||||
const handleEnter = () => {
|
||||
createTag.mutate(
|
||||
{ name: value },
|
||||
{
|
||||
onSuccess: (createdTag) => {
|
||||
if (editingNote && createdTag.id) {
|
||||
addTagToNote.mutate(
|
||||
{
|
||||
tagId: createdTag.id,
|
||||
noteId: editingNote.id,
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
setEditingNote({
|
||||
...editingNote,
|
||||
tags: [
|
||||
...(editingNote.tags || []),
|
||||
{ ...createdTag, name: value },
|
||||
],
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const handleSelectExistingTag = (tag: DecryptedTagNode) => {
|
||||
if (editingNote && tag.id) {
|
||||
addTagToNote.mutate(
|
||||
{
|
||||
tagId: tag.id,
|
||||
noteId: editingNote.id,
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
setEditingNote({
|
||||
...editingNote,
|
||||
tags: [
|
||||
...(editingNote.tags || []),
|
||||
{ id: tag.id, name: tag.name, parentId: tag.parentId },
|
||||
],
|
||||
});
|
||||
setValue("");
|
||||
setExpanded(false);
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
function handleClose() {
|
||||
// If there's a value, submit it; otherwise just collapse
|
||||
if (value.trim()) {
|
||||
handleEnter();
|
||||
}
|
||||
setValue("");
|
||||
setExpanded(false);
|
||||
}
|
||||
|
||||
function handleKey(e: React.KeyboardEvent<HTMLInputElement>) {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
handleClose();
|
||||
} else if (e.key === "Escape") {
|
||||
// Abort without submitting
|
||||
setValue("");
|
||||
setExpanded(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative ml-4" ref={containerRef}>
|
||||
<div className="inline-flex bg-ctp-surface0 rounded-full px-1 py-0.5 items-center">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setExpanded(true)}
|
||||
className={`text-xs bg-transparent flex items-center justify-center
|
||||
focus:outline-none transition-all duration-200 ease-out whitespace-nowrap text-ctp-subtext0
|
||||
${expanded ? "opacity-0 w-0 px-0 pointer-events-none overflow-hidden" : "opacity-100 px-0.5"}
|
||||
`}
|
||||
>
|
||||
<PlusIcon className="w-3 h-3 mr-1 fill-ctp-subtext0" />
|
||||
{"Add"}
|
||||
</button>
|
||||
|
||||
<input
|
||||
ref={inputRef}
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
onKeyDown={handleKey}
|
||||
placeholder={"Type here…"}
|
||||
className={`text-xs z-30
|
||||
bg-transparent px-1.5
|
||||
focus:outline-none focus:ring-0
|
||||
transition-all duration-200 ease-out
|
||||
${expanded ? "w-32 opacity-100" : "w-0 opacity-0 pointer-events-none"}
|
||||
`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Dropdown */}
|
||||
{expanded && value && (
|
||||
<div className="absolute top-full left-0 mt-1 bg-ctp-surface0 rounded-lg shadow-lg border border-ctp-surface2 overflow-hidden min-w-[200px] max-w-[300px] z-20">
|
||||
{/* Show hierarchical preview if path is being typed */}
|
||||
{parsedPath && parsedPath.length > 0 && (
|
||||
<div className="p-3 border-b border-ctp-surface2">
|
||||
<div className="text-xs text-ctp-overlay1 mb-2">
|
||||
Create tag hierarchy:
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
{parsedPath.map((part, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center text-xs text-ctp-text"
|
||||
style={{ paddingLeft: `${index * 12}px` }}
|
||||
>
|
||||
{index > 0 && (
|
||||
<span className="text-ctp-overlay0 mr-2">└─</span>
|
||||
)}
|
||||
<span className="bg-ctp-surface1 px-2 py-0.5 rounded">
|
||||
{part}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="text-xs text-ctp-overlay0 mt-2">
|
||||
Press Enter to create
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Show filtered existing tags */}
|
||||
{!parsedPath && filteredTags.length > 0 && (
|
||||
<div className="max-h-[200px] overflow-y-auto">
|
||||
{filteredTags.map((tag) => (
|
||||
<TagTreeClickable
|
||||
key={tag.id}
|
||||
tag={tag}
|
||||
onSelect={handleSelectExistingTag}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Show "create new" option for simple tags */}
|
||||
{!parsedPath && filteredTags.length === 0 && (
|
||||
<div className="p-2 text-xs text-ctp-overlay1">
|
||||
Press Enter to create "{value}"
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Clickable version of TagTree for selection
|
||||
export const TagTreeClickable = ({
|
||||
tag,
|
||||
depth = 0,
|
||||
onSelect,
|
||||
}: {
|
||||
tag: DecryptedTagNode;
|
||||
depth?: number;
|
||||
onSelect: (tag: DecryptedTagNode) => void;
|
||||
}) => {
|
||||
const [collapse, setCollapse] = useState(false);
|
||||
|
||||
return (
|
||||
<div key={tag.id} className="flex flex-col">
|
||||
<button
|
||||
onClick={() => onSelect(tag)}
|
||||
className="flex items-center px-3 py-2 hover:bg-ctp-surface1 transition-colors text-left text-xs"
|
||||
style={{ paddingLeft: `${12 + depth * 16}px` }}
|
||||
>
|
||||
<span className="text-ctp-text">{tag.name}</span>
|
||||
{tag.parentPath && (
|
||||
<span className="ml-2 text-ctp-overlay0 text-[10px]">
|
||||
{tag.parentPath}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* Show children */}
|
||||
{tag.children && tag.children.length > 0 && (
|
||||
<div>
|
||||
{tag.children.map((child) => (
|
||||
<TagTreeClickable
|
||||
key={child.id}
|
||||
tag={child}
|
||||
depth={depth + 1}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const TagTree = ({
|
||||
tag,
|
||||
depth = 0,
|
||||
}: {
|
||||
tag: DecryptedTagNode;
|
||||
depth?: number;
|
||||
}) => {
|
||||
const [collapse, setCollapse] = useState(false);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={tag.id}
|
||||
className="flex flex-col relative bg-ctp-surface0 pt-3 -translate-y-3 w-[136px]"
|
||||
>
|
||||
<div onClick={() => setCollapse(!collapse)}>{tag.name}</div>
|
||||
<AnimatePresence>
|
||||
{collapse && (
|
||||
<motion.div
|
||||
initial={{ height: 0, opacity: 0 }}
|
||||
animate={{ height: "auto", opacity: 1 }}
|
||||
exit={{ height: 0, opacity: 0 }}
|
||||
transition={{ duration: 0.2, ease: "easeInOut" }}
|
||||
className="overflow-hidden flex flex-col"
|
||||
>
|
||||
{/* The line container */}
|
||||
<div className="ml-2 pl-3 border-l border-ctp-surface2">
|
||||
{/* Child tags */}
|
||||
{tag.children.map((child) => (
|
||||
<TagTree key={child.id} tag={child} depth={depth + 1} />
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import { FolderTree } from "./subcomponents/FolderTree.tsx";
|
|||
import { SidebarHeader } from "./subcomponents/SideBarHeader.tsx";
|
||||
import { useAuthStore } from "@/stores/authStore.ts";
|
||||
import { useUIStore } from "@/stores/uiStore.ts";
|
||||
import { TagSelector } from "../../Home.tsx";
|
||||
import {
|
||||
useCreateFolder,
|
||||
useFolderTree,
|
||||
|
|
@ -42,8 +41,7 @@ export const Sidebar = () => {
|
|||
|
||||
const { encryptionKey } = useAuthStore();
|
||||
|
||||
const { setSideBarResize, sideBarResize, sideBarView, setSideBarView } =
|
||||
useUIStore();
|
||||
const { setSideBarResize, sideBarResize } = useUIStore();
|
||||
useEffect(() => {
|
||||
if (newFolder && newFolderRef.current) {
|
||||
newFolderRef.current.focus();
|
||||
|
|
@ -174,95 +172,85 @@ export const Sidebar = () => {
|
|||
>
|
||||
<SidebarHeader setNewFolder={setNewFolder} />
|
||||
<div className="flex-1 overflow-y-auto bg-ctp-mantle border-r border-ctp-surface2">
|
||||
{sideBarView == "folders" ? (
|
||||
<>
|
||||
<div
|
||||
className="w-full p-4 sm:block hidden"
|
||||
onDragOver={(e) => e.preventDefault()}
|
||||
onTouchMove={(e) => e.preventDefault()}
|
||||
>
|
||||
{/* New folder input */}
|
||||
{newFolder && (
|
||||
<div className="mb-2">
|
||||
<input
|
||||
onBlur={() => setNewFolder(false)}
|
||||
onChange={(e) => setNewFolderText(e.target.value)}
|
||||
value={newFolderText}
|
||||
type="text"
|
||||
placeholder="Folder name..."
|
||||
className="standard-input"
|
||||
ref={newFolderRef}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
handleCreateFolder();
|
||||
}
|
||||
if (e.key === "Escape") {
|
||||
setNewFolder(false);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<>
|
||||
<div
|
||||
className="w-full p-4 sm:block hidden"
|
||||
onDragOver={(e) => e.preventDefault()}
|
||||
onTouchMove={(e) => e.preventDefault()}
|
||||
>
|
||||
{/* New folder input */}
|
||||
{newFolder && (
|
||||
<div className="mb-2">
|
||||
<input
|
||||
onBlur={() => setNewFolder(false)}
|
||||
onChange={(e) => setNewFolderText(e.target.value)}
|
||||
value={newFolderText}
|
||||
type="text"
|
||||
placeholder="Folder name..."
|
||||
className="standard-input"
|
||||
ref={newFolderRef}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
handleCreateFolder();
|
||||
}
|
||||
if (e.key === "Escape") {
|
||||
setNewFolder(false);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Loading state */}
|
||||
{isLoading && (
|
||||
<div className="flex items-center justify-center py-8 text-ctp-subtext0">
|
||||
<div className="text-sm">Loading folders...</div>
|
||||
</div>
|
||||
)}
|
||||
{/* Loading state */}
|
||||
{isLoading && (
|
||||
<div className="flex items-center justify-center py-8 text-ctp-subtext0">
|
||||
<div className="text-sm">Loading folders...</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Error state */}
|
||||
{error && (
|
||||
<div className="flex items-center justify-center py-8 text-ctp-red">
|
||||
<div className="text-sm">Failed to load folders</div>
|
||||
</div>
|
||||
)}
|
||||
{/* Error state */}
|
||||
{error && (
|
||||
<div className="flex items-center justify-center py-8 text-ctp-red">
|
||||
<div className="text-sm">Failed to load folders</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Folder tree */}
|
||||
{!isLoading && !error && (
|
||||
<>
|
||||
<div className="flex flex-col gap-1">
|
||||
{folderTree?.folders.map((folder) => (
|
||||
<FolderTree
|
||||
key={folder.id}
|
||||
folder={folder}
|
||||
depth={0}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Orphaned notes */}
|
||||
{folderTree?.orphanedNotes &&
|
||||
folderTree.orphanedNotes.length > 0 && (
|
||||
<div className="mt-4 flex flex-col gap-1">
|
||||
{folderTree.orphanedNotes.map((note) => (
|
||||
<DraggableNote key={note.id} note={note} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<DragOverlay>
|
||||
{activeItem?.type === "note" && (
|
||||
<div className="bg-ctp-surface0 rounded-md px-2 py-1 shadow-lg border border-ctp-mauve">
|
||||
{activeItem.data.title}
|
||||
{/* Folder tree */}
|
||||
{!isLoading && !error && (
|
||||
<>
|
||||
<div className="flex flex-col gap-1">
|
||||
{folderTree?.folders.map((folder) => (
|
||||
<FolderTree key={folder.id} folder={folder} depth={0} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{activeItem?.type === "folder" && (
|
||||
<div className="bg-ctp-surface0 rounded-md px-1 py-0.5 shadow-lg flex items-center gap-1 text-sm">
|
||||
<FolderIcon className="w-3 h-3 fill-ctp-mauve mr-1" />
|
||||
{activeItem.data.name}
|
||||
</div>
|
||||
)}
|
||||
</DragOverlay>
|
||||
</>
|
||||
) : (
|
||||
<div className="w-full p-4 sm:block hidden">
|
||||
<TagSelector />
|
||||
|
||||
{/* Orphaned notes */}
|
||||
{folderTree?.orphanedNotes &&
|
||||
folderTree.orphanedNotes.length > 0 && (
|
||||
<div className="mt-4 flex flex-col gap-1">
|
||||
{folderTree.orphanedNotes.map((note) => (
|
||||
<DraggableNote key={note.id} note={note} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<DragOverlay>
|
||||
{activeItem?.type === "note" && (
|
||||
<div className="bg-ctp-surface0 rounded-md px-2 py-1 shadow-lg border border-ctp-mauve">
|
||||
{activeItem.data.title}
|
||||
</div>
|
||||
)}
|
||||
{activeItem?.type === "folder" && (
|
||||
<div className="bg-ctp-surface0 rounded-md px-1 py-0.5 shadow-lg flex items-center gap-1 text-sm">
|
||||
<FolderIcon className="w-3 h-3 fill-ctp-mauve mr-1" />
|
||||
{activeItem.data.name}
|
||||
</div>
|
||||
)}
|
||||
</DragOverlay>
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export const SidebarHeader = ({
|
|||
}: {
|
||||
setNewFolder: React.Dispatch<SetStateAction<boolean>>;
|
||||
}) => {
|
||||
const { setSideBarView, sideBarView, selectedFolder } = useUIStore();
|
||||
const { selectedFolder } = useUIStore();
|
||||
const createNote = useCreateNote();
|
||||
const handleCreate = async () => {
|
||||
createNote.mutate({
|
||||
|
|
@ -33,15 +33,6 @@ export const SidebarHeader = ({
|
|||
>
|
||||
<FolderPlusIcon className="w-5 h-5 group-hover:fill-ctp-base transition-all duration-200 fill-ctp-mauve" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() =>
|
||||
setSideBarView(sideBarView == "tags" ? "folders" : "tags")
|
||||
}
|
||||
className={`${sideBarView === "tags" ? "bg-ctp-mauve/20" : ""} hover:bg-ctp-mauve active:scale-95 group transition-all duration-200 rounded-md p-2 hover:shadow-md`}
|
||||
title="Tags"
|
||||
>
|
||||
<TagsIcon className="w-5 h-5 group-hover:fill-ctp-base transition-all duration-200 fill-ctp-mauve" />
|
||||
</button>
|
||||
<button
|
||||
onClick={handleCreate}
|
||||
className="hover:bg-ctp-mauve active:scale-95 group transition-all duration-200 rounded-md p-2 hover:shadow-md"
|
||||
|
|
|
|||
Loading…
Reference in a new issue