feat: add search functionality
All checks were successful
Deploy eolas-app / deploy (push) Successful in 1m45s
All checks were successful
Deploy eolas-app / deploy (push) Successful in 1m45s
This commit is contained in:
parent
01b915552c
commit
7f9e227f6b
17 changed files with 1188 additions and 248 deletions
424
package-lock.json
generated
424
package-lock.json
generated
|
|
@ -9,10 +9,10 @@
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-collapsible": "^1.1.7",
|
"@radix-ui/react-collapsible": "^1.1.7",
|
||||||
"@radix-ui/react-dialog": "^1.1.10",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-hover-card": "^1.1.15",
|
"@radix-ui/react-hover-card": "^1.1.15",
|
||||||
"@radix-ui/react-label": "^2.1.7",
|
"@radix-ui/react-label": "^2.1.8",
|
||||||
"@radix-ui/react-separator": "^1.1.4",
|
"@radix-ui/react-separator": "^1.1.8",
|
||||||
"@radix-ui/react-slot": "^1.2.3",
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
"@radix-ui/react-switch": "^1.2.5",
|
"@radix-ui/react-switch": "^1.2.5",
|
||||||
"@radix-ui/react-tabs": "^1.1.12",
|
"@radix-ui/react-tabs": "^1.1.12",
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"@react-sigma/core": "^5.0.4",
|
"@react-sigma/core": "^5.0.4",
|
||||||
"@react-sigma/layout-forceatlas2": "^5.0.6",
|
"@react-sigma/layout-forceatlas2": "^5.0.6",
|
||||||
"@tailwindcss/vite": "^4.1.4",
|
"@tailwindcss/vite": "^4.1.4",
|
||||||
|
"@tanstack/react-form": "^1.27.1",
|
||||||
"@tanstack/react-query": "^5.83.0",
|
"@tanstack/react-query": "^5.83.0",
|
||||||
"@tanstack/react-query-devtools": "^5.83.0",
|
"@tanstack/react-query-devtools": "^5.83.0",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
|
|
@ -37,6 +38,7 @@
|
||||||
"react-resizable-panels": "^3.0.4",
|
"react-resizable-panels": "^3.0.4",
|
||||||
"react-router": "^7.7.0",
|
"react-router": "^7.7.0",
|
||||||
"rehype-katex": "^7.0.1",
|
"rehype-katex": "^7.0.1",
|
||||||
|
"rehype-raw": "^7.0.0",
|
||||||
"remark-gfm": "^4.0.1",
|
"remark-gfm": "^4.0.1",
|
||||||
"remark-math": "^6.0.0",
|
"remark-math": "^6.0.0",
|
||||||
"shiki": "^3.9.1",
|
"shiki": "^3.9.1",
|
||||||
|
|
@ -1175,21 +1177,22 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@radix-ui/react-dialog": {
|
"node_modules/@radix-ui/react-dialog": {
|
||||||
"version": "1.1.10",
|
"version": "1.1.15",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.10.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz",
|
||||||
"integrity": "sha512-m6pZb0gEM5uHPSb+i2nKKGQi/HMSVjARMsLMWQfKDP+eJ6B+uqryHnXhpnohTWElw+vEcMk/o4wJODtdRKHwqg==",
|
"integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/primitive": "1.1.2",
|
"@radix-ui/primitive": "1.1.3",
|
||||||
"@radix-ui/react-compose-refs": "1.1.2",
|
"@radix-ui/react-compose-refs": "1.1.2",
|
||||||
"@radix-ui/react-context": "1.1.2",
|
"@radix-ui/react-context": "1.1.2",
|
||||||
"@radix-ui/react-dismissable-layer": "1.1.7",
|
"@radix-ui/react-dismissable-layer": "1.1.11",
|
||||||
"@radix-ui/react-focus-guards": "1.1.2",
|
"@radix-ui/react-focus-guards": "1.1.3",
|
||||||
"@radix-ui/react-focus-scope": "1.1.4",
|
"@radix-ui/react-focus-scope": "1.1.7",
|
||||||
"@radix-ui/react-id": "1.1.1",
|
"@radix-ui/react-id": "1.1.1",
|
||||||
"@radix-ui/react-portal": "1.1.6",
|
"@radix-ui/react-portal": "1.1.9",
|
||||||
"@radix-ui/react-presence": "1.1.3",
|
"@radix-ui/react-presence": "1.1.5",
|
||||||
"@radix-ui/react-primitive": "2.1.0",
|
"@radix-ui/react-primitive": "2.1.3",
|
||||||
"@radix-ui/react-slot": "1.2.0",
|
"@radix-ui/react-slot": "1.2.3",
|
||||||
"@radix-ui/react-use-controllable-state": "1.2.2",
|
"@radix-ui/react-use-controllable-state": "1.2.2",
|
||||||
"aria-hidden": "^1.2.4",
|
"aria-hidden": "^1.2.4",
|
||||||
"react-remove-scroll": "^2.6.3"
|
"react-remove-scroll": "^2.6.3"
|
||||||
|
|
@ -1209,21 +1212,107 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": {
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/primitive": {
|
||||||
"version": "1.2.0",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
|
||||||
"integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==",
|
"integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-dismissable-layer": {
|
||||||
|
"version": "1.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz",
|
||||||
|
"integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-compose-refs": "1.1.2"
|
"@radix-ui/primitive": "1.1.3",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2",
|
||||||
|
"@radix-ui/react-primitive": "2.1.3",
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.1.1",
|
||||||
|
"@radix-ui/react-use-escape-keydown": "1.1.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "*",
|
"@types/react": "*",
|
||||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@types/react": {
|
"@types/react": {
|
||||||
"optional": true
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-portal": {
|
||||||
|
"version": "1.1.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
|
||||||
|
"integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-primitive": "2.1.3",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-presence": {
|
||||||
|
"version": "1.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
|
||||||
|
"integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-primitive": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": "1.2.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -1269,9 +1358,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@radix-ui/react-focus-guards": {
|
"node_modules/@radix-ui/react-focus-guards": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz",
|
||||||
"integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==",
|
"integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==",
|
||||||
|
"license": "MIT",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "*",
|
"@types/react": "*",
|
||||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
|
@ -1283,12 +1373,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@radix-ui/react-focus-scope": {
|
"node_modules/@radix-ui/react-focus-scope": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz",
|
||||||
"integrity": "sha512-r2annK27lIW5w9Ho5NyQgqs0MmgZSTIKXWpVCJaLC1q2kZrZkcqnmHkCHMEmv8XLvsLlurKMPT+kbKkRkm/xVA==",
|
"integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-compose-refs": "1.1.2",
|
"@radix-ui/react-compose-refs": "1.1.2",
|
||||||
"@radix-ui/react-primitive": "2.1.0",
|
"@radix-ui/react-primitive": "2.1.3",
|
||||||
"@radix-ui/react-use-callback-ref": "1.1.1"
|
"@radix-ui/react-use-callback-ref": "1.1.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
|
@ -1306,6 +1397,29 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-focus-scope/node_modules/@radix-ui/react-primitive": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": "1.2.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-hover-card": {
|
"node_modules/@radix-ui/react-hover-card": {
|
||||||
"version": "1.1.15",
|
"version": "1.1.15",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.15.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.15.tgz",
|
||||||
|
|
@ -1514,12 +1628,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@radix-ui/react-label": {
|
"node_modules/@radix-ui/react-label": {
|
||||||
"version": "2.1.7",
|
"version": "2.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.8.tgz",
|
||||||
"integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==",
|
"integrity": "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-primitive": "2.1.3"
|
"@radix-ui/react-primitive": "2.1.4"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "*",
|
"@types/react": "*",
|
||||||
|
|
@ -1537,12 +1651,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive": {
|
"node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz",
|
||||||
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
"integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-slot": "1.2.3"
|
"@radix-ui/react-slot": "1.2.4"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "*",
|
"@types/react": "*",
|
||||||
|
|
@ -1559,6 +1673,24 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-slot": {
|
||||||
|
"version": "1.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz",
|
||||||
|
"integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-popper": {
|
"node_modules/@radix-ui/react-popper": {
|
||||||
"version": "1.2.4",
|
"version": "1.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.4.tgz",
|
||||||
|
|
@ -1731,12 +1863,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@radix-ui/react-separator": {
|
"node_modules/@radix-ui/react-separator": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.8.tgz",
|
||||||
"integrity": "sha512-2fTm6PSiUm8YPq9W0E4reYuv01EE3aFSzt8edBiXqPHshF8N9+Kymt/k0/R+F3dkY5lQyB/zPtrP82phskLi7w==",
|
"integrity": "sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-primitive": "2.1.0"
|
"@radix-ui/react-primitive": "2.1.4"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "*",
|
"@types/react": "*",
|
||||||
|
|
@ -1753,6 +1885,47 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive": {
|
||||||
|
"version": "2.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz",
|
||||||
|
"integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": "1.2.4"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot": {
|
||||||
|
"version": "1.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz",
|
||||||
|
"integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-slot": {
|
"node_modules/@radix-ui/react-slot": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||||
|
|
@ -2904,6 +3077,51 @@
|
||||||
"vite": "^5.2.0 || ^6"
|
"vite": "^5.2.0 || ^6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tanstack/devtools-event-client": {
|
||||||
|
"version": "0.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/devtools-event-client/-/devtools-event-client-0.3.5.tgz",
|
||||||
|
"integrity": "sha512-RL1f5ZlfZMpghrCIdzl6mLOFLTuhqmPNblZgBaeKfdtk5rfbjykurv+VfYydOFXj0vxVIoA2d/zT7xfD7Ph8fw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tanstack/form-core": {
|
||||||
|
"version": "1.27.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/form-core/-/form-core-1.27.1.tgz",
|
||||||
|
"integrity": "sha512-hPM+0tUnZ2C2zb2TE1lar1JJ0S0cbnQHlUwFcCnVBpMV3rjtUzkoM766gUpWrlmTGCzNad0GbJ0aTxVsjT6J8g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@tanstack/devtools-event-client": "^0.3.5",
|
||||||
|
"@tanstack/pacer": "^0.15.3",
|
||||||
|
"@tanstack/store": "^0.7.7"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tanstack/pacer": {
|
||||||
|
"version": "0.15.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/pacer/-/pacer-0.15.4.tgz",
|
||||||
|
"integrity": "sha512-vGY+CWsFZeac3dELgB6UZ4c7OacwsLb8hvL2gLS6hTgy8Fl0Bm/aLokHaeDIP+q9F9HUZTnp360z9uv78eg8pg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@tanstack/devtools-event-client": "^0.3.2",
|
||||||
|
"@tanstack/store": "^0.7.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tanstack/query-core": {
|
"node_modules/@tanstack/query-core": {
|
||||||
"version": "5.83.0",
|
"version": "5.83.0",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.83.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.83.0.tgz",
|
||||||
|
|
@ -2924,6 +3142,28 @@
|
||||||
"url": "https://github.com/sponsors/tannerlinsley"
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tanstack/react-form": {
|
||||||
|
"version": "1.27.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/react-form/-/react-form-1.27.1.tgz",
|
||||||
|
"integrity": "sha512-HKP0Ew2ae9AL5vU1PkJ+oAC2p+xBtA905u0fiNLzlfn1vLkBxenfg5L6TOA+rZITHpQsSo10tqwc5Yw6qn8Mpg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@tanstack/form-core": "1.27.1",
|
||||||
|
"@tanstack/react-store": "^0.8.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@tanstack/react-start": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tanstack/react-query": {
|
"node_modules/@tanstack/react-query": {
|
||||||
"version": "5.83.0",
|
"version": "5.83.0",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.83.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.83.0.tgz",
|
||||||
|
|
@ -2958,6 +3198,34 @@
|
||||||
"react": "^18 || ^19"
|
"react": "^18 || ^19"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tanstack/react-store": {
|
||||||
|
"version": "0.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/react-store/-/react-store-0.8.0.tgz",
|
||||||
|
"integrity": "sha512-1vG9beLIuB7q69skxK9r5xiLN3ztzIPfSQSs0GfeqWGO2tGIyInZx0x1COhpx97RKaONSoAb8C3dxacWksm1ow==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@tanstack/store": "0.8.0",
|
||||||
|
"use-sync-external-store": "^1.6.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tanstack/react-store/node_modules/@tanstack/store": {
|
||||||
|
"version": "0.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/store/-/store-0.8.0.tgz",
|
||||||
|
"integrity": "sha512-Om+BO0YfMZe//X2z0uLF2j+75nQga6TpTJgLJQBiq85aOyZNIhkCgleNcud2KQg4k4v9Y9l+Uhru3qWMPGTOzQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tanstack/react-table": {
|
"node_modules/@tanstack/react-table": {
|
||||||
"version": "8.21.3",
|
"version": "8.21.3",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz",
|
||||||
|
|
@ -2978,6 +3246,16 @@
|
||||||
"react-dom": ">=16.8"
|
"react-dom": ">=16.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tanstack/store": {
|
||||||
|
"version": "0.7.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/store/-/store-0.7.7.tgz",
|
||||||
|
"integrity": "sha512-xa6pTan1bcaqYDS9BDpSiS63qa6EoDkPN9RsRaxHuDdVDNntzq3xNwR5YKTU/V3SkSyC9T4YVOPh2zRQN0nhIQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tanstack/table-core": {
|
"node_modules/@tanstack/table-core": {
|
||||||
"version": "8.21.3",
|
"version": "8.21.3",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz",
|
||||||
|
|
@ -4641,6 +4919,31 @@
|
||||||
"url": "https://opencollective.com/unified"
|
"url": "https://opencollective.com/unified"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/hast-util-raw": {
|
||||||
|
"version": "9.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz",
|
||||||
|
"integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/hast": "^3.0.0",
|
||||||
|
"@types/unist": "^3.0.0",
|
||||||
|
"@ungap/structured-clone": "^1.0.0",
|
||||||
|
"hast-util-from-parse5": "^8.0.0",
|
||||||
|
"hast-util-to-parse5": "^8.0.0",
|
||||||
|
"html-void-elements": "^3.0.0",
|
||||||
|
"mdast-util-to-hast": "^13.0.0",
|
||||||
|
"parse5": "^7.0.0",
|
||||||
|
"unist-util-position": "^5.0.0",
|
||||||
|
"unist-util-visit": "^5.0.0",
|
||||||
|
"vfile": "^6.0.0",
|
||||||
|
"web-namespaces": "^2.0.0",
|
||||||
|
"zwitch": "^2.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/unified"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/hast-util-to-html": {
|
"node_modules/hast-util-to-html": {
|
||||||
"version": "9.0.5",
|
"version": "9.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz",
|
||||||
|
|
@ -4691,6 +4994,25 @@
|
||||||
"url": "https://opencollective.com/unified"
|
"url": "https://opencollective.com/unified"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/hast-util-to-parse5": {
|
||||||
|
"version": "8.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz",
|
||||||
|
"integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/hast": "^3.0.0",
|
||||||
|
"comma-separated-tokens": "^2.0.0",
|
||||||
|
"devlop": "^1.0.0",
|
||||||
|
"property-information": "^7.0.0",
|
||||||
|
"space-separated-tokens": "^2.0.0",
|
||||||
|
"web-namespaces": "^2.0.0",
|
||||||
|
"zwitch": "^2.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/unified"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/hast-util-to-text": {
|
"node_modules/hast-util-to-text": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz",
|
||||||
|
|
@ -6687,6 +7009,21 @@
|
||||||
"url": "https://opencollective.com/unified"
|
"url": "https://opencollective.com/unified"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/rehype-raw": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/hast": "^3.0.0",
|
||||||
|
"hast-util-raw": "^9.0.0",
|
||||||
|
"vfile": "^6.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/unified"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/remark-gfm": {
|
"node_modules/remark-gfm": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
|
||||||
|
|
@ -7362,6 +7699,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/use-sync-external-store": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vfile": {
|
"node_modules/vfile": {
|
||||||
"version": "6.0.3",
|
"version": "6.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,10 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-collapsible": "^1.1.7",
|
"@radix-ui/react-collapsible": "^1.1.7",
|
||||||
"@radix-ui/react-dialog": "^1.1.10",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-hover-card": "^1.1.15",
|
"@radix-ui/react-hover-card": "^1.1.15",
|
||||||
"@radix-ui/react-label": "^2.1.7",
|
"@radix-ui/react-label": "^2.1.8",
|
||||||
"@radix-ui/react-separator": "^1.1.4",
|
"@radix-ui/react-separator": "^1.1.8",
|
||||||
"@radix-ui/react-slot": "^1.2.3",
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
"@radix-ui/react-switch": "^1.2.5",
|
"@radix-ui/react-switch": "^1.2.5",
|
||||||
"@radix-ui/react-tabs": "^1.1.12",
|
"@radix-ui/react-tabs": "^1.1.12",
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
"@react-sigma/core": "^5.0.4",
|
"@react-sigma/core": "^5.0.4",
|
||||||
"@react-sigma/layout-forceatlas2": "^5.0.6",
|
"@react-sigma/layout-forceatlas2": "^5.0.6",
|
||||||
"@tailwindcss/vite": "^4.1.4",
|
"@tailwindcss/vite": "^4.1.4",
|
||||||
|
"@tanstack/react-form": "^1.27.1",
|
||||||
"@tanstack/react-query": "^5.83.0",
|
"@tanstack/react-query": "^5.83.0",
|
||||||
"@tanstack/react-query-devtools": "^5.83.0",
|
"@tanstack/react-query-devtools": "^5.83.0",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
|
|
@ -39,6 +40,7 @@
|
||||||
"react-resizable-panels": "^3.0.4",
|
"react-resizable-panels": "^3.0.4",
|
||||||
"react-router": "^7.7.0",
|
"react-router": "^7.7.0",
|
||||||
"rehype-katex": "^7.0.1",
|
"rehype-katex": "^7.0.1",
|
||||||
|
"rehype-raw": "^7.0.0",
|
||||||
"remark-gfm": "^4.0.1",
|
"remark-gfm": "^4.0.1",
|
||||||
"remark-math": "^6.0.0",
|
"remark-math": "^6.0.0",
|
||||||
"shiki": "^3.9.1",
|
"shiki": "^3.9.1",
|
||||||
|
|
|
||||||
30
src/App.css
30
src/App.css
|
|
@ -2,30 +2,34 @@
|
||||||
@import url("https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,100..700;1,100..700&display=swap");
|
@import url("https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,100..700;1,100..700&display=swap");
|
||||||
|
|
||||||
code {
|
code {
|
||||||
font-family: "JetBrains Mono";
|
font-family: "JetBrains Mono";
|
||||||
}
|
}
|
||||||
|
|
||||||
pre > code {
|
pre>code {
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 > code {
|
h2>code {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn[data-state="active"] {
|
.btn[data-state="active"] {
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
button[data-state="active"] {
|
button[data-state="active"] {
|
||||||
--tw-shadow: none;
|
--tw-shadow: none;
|
||||||
--tw-shadow-colored: none;
|
--tw-shadow-colored: none;
|
||||||
box-shadow:
|
box-shadow:
|
||||||
var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sigma-container {
|
.sigma-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
match {
|
||||||
|
color: "red" !important;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { SidebarTrigger } from "./ui/sidebar"
|
import { SidebarTrigger } from "./ui/sidebar"
|
||||||
import { Separator } from "./ui/separator"
|
import { Separator } from "./ui/separator"
|
||||||
|
import Search from "@/containers/Search"
|
||||||
|
|
||||||
export default function AppHeader({ pageTitle }: { pageTitle: string }) {
|
export default function AppHeader({ pageTitle }: { pageTitle: string }) {
|
||||||
return (
|
return (
|
||||||
|
|
@ -7,7 +8,7 @@ export default function AppHeader({ pageTitle }: { pageTitle: string }) {
|
||||||
<div className="flex w-full items-center gap-1 px-4 lg:gap-2 lg:px-6">
|
<div className="flex w-full items-center gap-1 px-4 lg:gap-2 lg:px-6">
|
||||||
<SidebarTrigger className="-ml-1" />
|
<SidebarTrigger className="-ml-1" />
|
||||||
<Separator orientation="vertical" className="mx-2 data-[orientation=vertical]:h-4" />
|
<Separator orientation="vertical" className="mx-2 data-[orientation=vertical]:h-4" />
|
||||||
<h1 className="text-base font-medium">{pageTitle}</h1>
|
<Search />
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export default function BodyLink({ link, children }) {
|
||||||
const cachedEntry = queryClient.getQueryData([`entry_${path}`])
|
const cachedEntry = queryClient.getQueryData([`entry_${path}`])
|
||||||
if (cachedEntry) {
|
if (cachedEntry) {
|
||||||
setEntryExists(true)
|
setEntryExists(true)
|
||||||
console.info("INFO: Entry exists in cache.")
|
// console.info("INFO: Entry exists in cache.")
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
const remoteEntry = await queryClient.fetchQuery({
|
const remoteEntry = await queryClient.fetchQuery({
|
||||||
|
|
@ -21,9 +21,9 @@ export default function BodyLink({ link, children }) {
|
||||||
})
|
})
|
||||||
|
|
||||||
setEntryExists(true)
|
setEntryExists(true)
|
||||||
console.info("INFO: Entry exists on remote.")
|
// console.info("INFO: Entry exists on remote.")
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(`INFO: Could not fetch entry ${path} ${error}`)
|
// console.log(`INFO: Could not fetch entry ${path} ${error}`)
|
||||||
setEntryExists(false)
|
setEntryExists(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
37
src/components/EntriesSearchResult.tsx
Normal file
37
src/components/EntriesSearchResult.tsx
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import ReactMarkdown from "react-markdown"
|
||||||
|
import rehypeRaw from "rehype-raw"
|
||||||
|
import remarkGfm from "remark-gfm"
|
||||||
|
import remarkMath from "remark-math"
|
||||||
|
import { Link } from "react-router"
|
||||||
|
|
||||||
|
const stripMarkdownLinks = (text) => {
|
||||||
|
return text.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1")
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function EntriesSearchResult({ entry, match, searchParams }) {
|
||||||
|
return (
|
||||||
|
<Link to={`/entries/${entry}?highlight=${encodeURIComponent(searchParams)}`}>
|
||||||
|
{" "}
|
||||||
|
<div className="text-sm mt-1 mb-3 p-2 bg-muted hover:bg-sidebar">
|
||||||
|
<ReactMarkdown
|
||||||
|
remarkPlugins={[remarkGfm, remarkMath]}
|
||||||
|
rehypePlugins={[rehypeRaw]}
|
||||||
|
components={{
|
||||||
|
mark: ({ children }) => (
|
||||||
|
<span className="dark:bg-[#4c1d95] bg-[#ddd6fe]">{children}</span>
|
||||||
|
),
|
||||||
|
code: ({ children }) => (
|
||||||
|
<code className="rounded bg-mutedfont-mono text-[12px]">{children}</code>
|
||||||
|
),
|
||||||
|
pre: ({ children }) => (
|
||||||
|
<code className="rounded bg-muted font-mono text-[12px]">{children}</code>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{stripMarkdownLinks(match)}
|
||||||
|
</ReactMarkdown>
|
||||||
|
<div className="font-semibold">{`${entry.replace(/_/g, " ")}`}</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -4,26 +4,9 @@ import CodeBlock from "@/components/CodeBlock"
|
||||||
import remarkMath from "remark-math"
|
import remarkMath from "remark-math"
|
||||||
import rehypeKatex from "rehype-katex"
|
import rehypeKatex from "rehype-katex"
|
||||||
import "katex/dist/katex.min.css"
|
import "katex/dist/katex.min.css"
|
||||||
import { Skeleton } from "@/components/ui/skeleton"
|
|
||||||
import BodyLink from "./BodyLink"
|
import BodyLink from "./BodyLink"
|
||||||
|
import EntryLoadingSkeleton from "./EntryLoadingSkeleton"
|
||||||
const EntryLoadingSkeleton = () => {
|
import { useSearchParams } from "react-router"
|
||||||
return (
|
|
||||||
<div className="space-y-2 max-w-2xl p-4 lg:p-6">
|
|
||||||
{/*
|
|
||||||
|
|
||||||
<Skeleton className="h-[400px] md:h-[800px] max-w-2xl rounded-none" />
|
|
||||||
*/}
|
|
||||||
|
|
||||||
<Skeleton className="h-4 max-w-full" />
|
|
||||||
<Skeleton className="h-4 md:max-w-xl max-w-[300px]" />
|
|
||||||
<Skeleton className="h-4 md:max-w-[400px] max-w-[250px]" />
|
|
||||||
<Skeleton className="h-4 md:max-w-[300px] max-w-[200px]" />
|
|
||||||
<Skeleton className="h-4 md:max-w-[200px] max-w-[150px]" />
|
|
||||||
<Skeleton className="h-4 md:max-w-[100px] max-w-[100px]" />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const ImagePreprocessor = (src) => {
|
const ImagePreprocessor = (src) => {
|
||||||
const filename = src.src.split("/").pop()
|
const filename = src.src.split("/").pop()
|
||||||
|
|
@ -31,73 +14,108 @@ const ImagePreprocessor = (src) => {
|
||||||
return <img src={s3RootUrl + filename} />
|
return <img src={s3RootUrl + filename} />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
||||||
|
|
||||||
|
const highlighter = (children, highlight) => {
|
||||||
|
if (!highlight || typeof children !== "string") return children
|
||||||
|
const words = highlight.trim().split(/\s+/)
|
||||||
|
const pattern = words.length > 1 ? escapeRegex(highlight) : escapeRegex(words[0])
|
||||||
|
const regex = new RegExp(`\\b(${pattern})\\b`, "gi")
|
||||||
|
const parts = children.split(regex)
|
||||||
|
|
||||||
|
return parts.map((part, i) =>
|
||||||
|
regex.test(part) ? (
|
||||||
|
<mark key={i} className="dark:bg-[#4c1d95] dark:text-white bg-[#ddd6fe]">
|
||||||
|
{part}
|
||||||
|
</mark>
|
||||||
|
) : (
|
||||||
|
part
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default function EntryBody({ body, isLoading }) {
|
export default function EntryBody({ body, isLoading }) {
|
||||||
|
const [searchParams] = useSearchParams()
|
||||||
|
|
||||||
|
const highlight = searchParams.get("highlight")
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return <EntryLoadingSkeleton />
|
return <EntryLoadingSkeleton />
|
||||||
} else
|
}
|
||||||
return (
|
return (
|
||||||
<div className="max-w-2xl p-4 lg:p-6">
|
<div className="max-w-2xl p-4 lg:p-6">
|
||||||
<ReactMarkdown
|
<ReactMarkdown
|
||||||
remarkPlugins={[remarkGfm, remarkMath]}
|
remarkPlugins={[remarkGfm, remarkMath]}
|
||||||
rehypePlugins={[rehypeKatex]}
|
rehypePlugins={[rehypeKatex]}
|
||||||
components={{
|
components={{
|
||||||
h1: () => null,
|
h1: () => null,
|
||||||
h2: ({ children }) => (
|
h2: ({ children }) => (
|
||||||
<h2 className="scroll-m-20 font-semibold mt-8 mb-4 first:mt-0">{children}</h2>
|
<h2 className="scroll-m-20 font-semibold mt-8 mb-4 first:mt-0">
|
||||||
),
|
{highlighter(children, highlight)}
|
||||||
h3: ({ children }) => (
|
</h2>
|
||||||
<h3 className="scroll-m-20 font-semibold mt-8 mb-4 first:mt-0">{children}</h3>
|
),
|
||||||
),
|
h3: ({ children }) => (
|
||||||
h4: ({ children }) => (
|
<h3 className="scroll-m-20 font-semibold mt-8 mb-4 first:mt-0">
|
||||||
<h4 className="scroll-m-20 font-semibold mt-8 mb-4 first:mt-0">{children}</h4>
|
{highlighter(children, highlight)}
|
||||||
),
|
</h3>
|
||||||
p: ({ children }) => (
|
),
|
||||||
<p className="leading-[1.5] mb-4 not-first:mt-4">{children}</p>
|
h4: ({ children }) => (
|
||||||
),
|
<h4 className="scroll-m-20 font-semibold mt-8 mb-4 first:mt-0">
|
||||||
ul: ({ children }) => (
|
{highlighter(children, highlight)}
|
||||||
<ul className="list-disc ml-10 mb-4 space-y-1">{children}</ul>
|
</h4>
|
||||||
),
|
),
|
||||||
ol: ({ children }) => (
|
p: ({ children }) => (
|
||||||
<ol className="list-decimal ml-10 mb-4 space-y-1">{children}</ol>
|
<p className="leading-[1.5] mb-4 not-first:mt-4">
|
||||||
),
|
{highlighter(children, highlight)}
|
||||||
table: ({ children }) => (
|
</p>
|
||||||
<table className="w-full mb-4 text-sm">{children}</table>
|
),
|
||||||
),
|
ul: ({ children }) => <ul className="list-disc ml-10 mb-4 space-y-1">{children}</ul>,
|
||||||
tr: ({ children }) => (
|
ol: ({ children }) => (
|
||||||
<tr className="even:bg-muted m-0 border-t p-0">{children}</tr>
|
<ol className="list-decimal ml-10 mb-4 space-y-1">{children}</ol>
|
||||||
),
|
),
|
||||||
th: ({ children }) => (
|
li: ({ children }) => (
|
||||||
<th className="border px-4 py-2 text-left font-bold [&[align=center]]:text-center [&[align=right]]:text-right">
|
<li className="list-disc ml-10 mb-4 space-y-1">
|
||||||
{children}
|
{highlighter(children, highlight)}
|
||||||
</th>
|
</li>
|
||||||
),
|
),
|
||||||
td: ({ children }) => (
|
table: ({ children }) => <table className="w-full mb-4 text-sm">{children}</table>,
|
||||||
<td className="border px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right">
|
tr: ({ children }) => (
|
||||||
{children}
|
<tr className="even:bg-muted m-0 border-t p-0">
|
||||||
</td>
|
{highlighter(children, highlight)}
|
||||||
),
|
</tr>
|
||||||
blockquote: ({ children }) => (
|
),
|
||||||
<blockquote className="mt-4 border-l-2 pl-6 text-muted-foreground">
|
th: ({ children }) => (
|
||||||
{children}
|
<th className="border px-4 py-2 text-left font-bold [&[align=center]]:text-center [&[align=right]]:text-right">
|
||||||
</blockquote>
|
{highlighter(children, highlight)}
|
||||||
),
|
</th>
|
||||||
pre: ({ children }) => {
|
),
|
||||||
const child = children.props
|
td: ({ children }) => (
|
||||||
return <CodeBlock className={child.className}>{child.children}</CodeBlock>
|
<td className="border px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right">
|
||||||
},
|
{highlighter(children, highlight)}
|
||||||
code: ({ children }) => (
|
</td>
|
||||||
<code className="rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm">
|
),
|
||||||
{children}
|
blockquote: ({ children }) => (
|
||||||
</code>
|
<blockquote className="mt-4 border-l-2 pl-6 text-muted-foreground">
|
||||||
),
|
{highlighter(children, highlight)}
|
||||||
img: ({ src }) => <ImagePreprocessor src={src} />,
|
</blockquote>
|
||||||
a: ({ href, children }) => {
|
),
|
||||||
return <BodyLink link={href} children={children} />
|
pre: ({ children }) => {
|
||||||
},
|
const child = children.props
|
||||||
}}
|
return <CodeBlock className={child.className}>{child.children}</CodeBlock>
|
||||||
>
|
},
|
||||||
{body}
|
code: ({ children }) => (
|
||||||
</ReactMarkdown>
|
<code className="rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm">
|
||||||
</div>
|
{children}
|
||||||
)
|
</code>
|
||||||
|
),
|
||||||
|
img: ({ src }) => <ImagePreprocessor src={src} />,
|
||||||
|
a: ({ href, children }) => {
|
||||||
|
return <BodyLink link={href} children={children} />
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{body}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
18
src/components/EntryLoadingSkeleton.tsx
Normal file
18
src/components/EntryLoadingSkeleton.tsx
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Skeleton } from "@/components/ui/skeleton"
|
||||||
|
export default function EntryLoadingSkeleton() {
|
||||||
|
return (
|
||||||
|
<div className="space-y-2 max-w-2xl p-4 lg:p-6">
|
||||||
|
{/*
|
||||||
|
|
||||||
|
<Skeleton className="h-[400px] md:h-[800px] max-w-2xl rounded-none" />
|
||||||
|
*/}
|
||||||
|
|
||||||
|
<Skeleton className="h-4 max-w-full" />
|
||||||
|
<Skeleton className="h-4 md:max-w-xl max-w-[300px]" />
|
||||||
|
<Skeleton className="h-4 md:max-w-[400px] max-w-[250px]" />
|
||||||
|
<Skeleton className="h-4 md:max-w-[300px] max-w-[200px]" />
|
||||||
|
<Skeleton className="h-4 md:max-w-[200px] max-w-[150px]" />
|
||||||
|
<Skeleton className="h-4 md:max-w-[100px] max-w-[100px]" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
21
src/components/SearchHistory.tsx
Normal file
21
src/components/SearchHistory.tsx
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./ui/table"
|
||||||
|
import { useQueryClient } from "@tanstack/react-query"
|
||||||
|
export default function SearchHistory({ }) {
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
const history = queryClient.getQueriesData({ queryKey: ["search_results"] })
|
||||||
|
console.log(history)
|
||||||
|
return (
|
||||||
|
<Table className="mt-2">
|
||||||
|
{/* <TableHeader> */}
|
||||||
|
{/* <TableRow> */}
|
||||||
|
{/* <TableHead>Search term</TableHead> */}
|
||||||
|
{/* </TableRow> */}
|
||||||
|
{/* </TableHeader> */}
|
||||||
|
<TableBody>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Paid</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
)
|
||||||
|
}
|
||||||
42
src/components/SearchInput.tsx
Normal file
42
src/components/SearchInput.tsx
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { Field } from "./ui/field"
|
||||||
|
import { Input } from "./ui/input"
|
||||||
|
import { Kbd } from "./ui/kbd"
|
||||||
|
|
||||||
|
export default function SearchInput({ form, inputRef }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<form className="w-full">
|
||||||
|
<form.Field
|
||||||
|
name="search"
|
||||||
|
children={(field) => {
|
||||||
|
return (
|
||||||
|
<Field>
|
||||||
|
<div className="relative">
|
||||||
|
<Input
|
||||||
|
ref={inputRef}
|
||||||
|
className="rounded-none [&::-webkit-search-cancel-button]:hidden"
|
||||||
|
id={field.name}
|
||||||
|
name={field.name}
|
||||||
|
value={field.state.value}
|
||||||
|
onChange={(e) => field.handleChange(e.target.value)}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
e.preventDefault()
|
||||||
|
form.handleSubmit()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
type="search"
|
||||||
|
placeholder="Search"
|
||||||
|
/>
|
||||||
|
<div className="absolute right-5 top-1 pointer-events-none">
|
||||||
|
{field.state.value ? <Kbd>Enter</Kbd> : <Kbd>Ctrl + K</Kbd>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Field>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
109
src/components/SearchResults.tsx
Normal file
109
src/components/SearchResults.tsx
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
import {
|
||||||
|
Sheet,
|
||||||
|
SheetContent,
|
||||||
|
SheetDescription,
|
||||||
|
SheetFooter,
|
||||||
|
SheetHeader,
|
||||||
|
SheetTitle,
|
||||||
|
} from "./ui/sheet"
|
||||||
|
import { Badge } from "./ui/badge"
|
||||||
|
import EntriesSearchResult from "./EntriesSearchResult"
|
||||||
|
import { Link } from "react-router"
|
||||||
|
import { Skeleton } from "./ui/skeleton"
|
||||||
|
|
||||||
|
export default function SearchResults({
|
||||||
|
form,
|
||||||
|
entriesResults,
|
||||||
|
tagResults,
|
||||||
|
sheetOpen,
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
setSheetOpen,
|
||||||
|
searchParams,
|
||||||
|
setSearchParams,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Sheet
|
||||||
|
open={sheetOpen}
|
||||||
|
onOpenChange={(open) => {
|
||||||
|
setSheetOpen(open)
|
||||||
|
if (!open) {
|
||||||
|
form.reset()
|
||||||
|
setSearchParams(null)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SheetContent>
|
||||||
|
<SheetHeader className="border-b bg-sidebar">
|
||||||
|
<SheetTitle className="flex gap-3">
|
||||||
|
<div>Search Results</div>
|
||||||
|
</SheetTitle>
|
||||||
|
<SheetDescription className="flex flex-row gap-3">
|
||||||
|
<div className="text-foreground">Search term:</div>
|
||||||
|
<span className="font-medium text-foreground">{searchParams}</span>
|
||||||
|
</SheetDescription>
|
||||||
|
</SheetHeader>
|
||||||
|
|
||||||
|
{error ? (
|
||||||
|
<div className="p-4 text-sm dark:text-red-300 text-red-700">
|
||||||
|
<div className="p-2 border-2 dark:border-red-800 border-red-500 dark:bg-red-900 bg-red-300">
|
||||||
|
Error fetching search results.
|
||||||
|
</div>{" "}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div className="flex flex row justify-between bg-sidebar mx-2 pt-0">
|
||||||
|
<h3 className="font-medium text-sm p-1 ml-1">Tags</h3>
|
||||||
|
<Badge variant="secondary" className="rounded-none">
|
||||||
|
{tagResults?.length || 0}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
{loading ? (
|
||||||
|
<div className="p-4 flex flex-row gap-2">
|
||||||
|
<Skeleton className="h-4 w-[60px]" />
|
||||||
|
<Skeleton className="h-4 w-[60px]" />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="p-4 pt-0 flex gap-2">
|
||||||
|
{tagResults.map((tagResult) => (
|
||||||
|
<Link to={`/tags/${tagResult}`}>
|
||||||
|
<Badge className="hover:bg-muted" variant="outline">
|
||||||
|
{tagResult}
|
||||||
|
</Badge>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="flex flex row justify-between bg-sidebar mx-2 pt-0">
|
||||||
|
<h3 className="font-medium text-sm p-1 ml-1">Entries</h3>
|
||||||
|
<Badge variant="secondary" className="rounded-none">
|
||||||
|
{entriesResults?.count || 0}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
{loading ? (
|
||||||
|
<div className="p-4 flex flex-col gap-4">
|
||||||
|
<Skeleton className="rounded-none h-25 max-w-full" />
|
||||||
|
<Skeleton className="rounded-none h-25 max-w-full" />
|
||||||
|
<Skeleton className="rounded-none h-25 max-w-full" />
|
||||||
|
<Skeleton className="rounded-none h-25 max-w-full" />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="overflow-y-auto overflow-x-hidden p-4 pt-0">
|
||||||
|
{entriesResults?.data.map((result, i) => (
|
||||||
|
<EntriesSearchResult
|
||||||
|
key={i}
|
||||||
|
entry={result.entry}
|
||||||
|
match={result.excerpt}
|
||||||
|
searchParams={searchParams}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<SheetFooter></SheetFooter>
|
||||||
|
</SheetContent>
|
||||||
|
</Sheet>
|
||||||
|
)
|
||||||
|
}
|
||||||
0
src/components/TagsSearchResult.tsx
Normal file
0
src/components/TagsSearchResult.tsx
Normal file
246
src/components/ui/field.tsx
Normal file
246
src/components/ui/field.tsx
Normal file
|
|
@ -0,0 +1,246 @@
|
||||||
|
import { useMemo } from "react"
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { Label } from "@/components/ui/label"
|
||||||
|
import { Separator } from "@/components/ui/separator"
|
||||||
|
|
||||||
|
function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
|
||||||
|
return (
|
||||||
|
<fieldset
|
||||||
|
data-slot="field-set"
|
||||||
|
className={cn(
|
||||||
|
"flex flex-col gap-6",
|
||||||
|
"has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function FieldLegend({
|
||||||
|
className,
|
||||||
|
variant = "legend",
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"legend"> & { variant?: "legend" | "label" }) {
|
||||||
|
return (
|
||||||
|
<legend
|
||||||
|
data-slot="field-legend"
|
||||||
|
data-variant={variant}
|
||||||
|
className={cn(
|
||||||
|
"mb-3 font-medium",
|
||||||
|
"data-[variant=legend]:text-base",
|
||||||
|
"data-[variant=label]:text-sm",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="field-group"
|
||||||
|
className={cn(
|
||||||
|
"group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const fieldVariants = cva(
|
||||||
|
"group/field flex w-full gap-3 data-[invalid=true]:text-destructive",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
orientation: {
|
||||||
|
vertical: ["flex-col [&>*]:w-full [&>.sr-only]:w-auto"],
|
||||||
|
horizontal: [
|
||||||
|
"flex-row items-center",
|
||||||
|
"[&>[data-slot=field-label]]:flex-auto",
|
||||||
|
"has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
|
||||||
|
],
|
||||||
|
responsive: [
|
||||||
|
"flex-col [&>*]:w-full [&>.sr-only]:w-auto @md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto",
|
||||||
|
"@md/field-group:[&>[data-slot=field-label]]:flex-auto",
|
||||||
|
"@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
orientation: "vertical",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
function Field({
|
||||||
|
className,
|
||||||
|
orientation = "vertical",
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"div"> & VariantProps<typeof fieldVariants>) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
role="group"
|
||||||
|
data-slot="field"
|
||||||
|
data-orientation={orientation}
|
||||||
|
className={cn(fieldVariants({ orientation }), className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="field-content"
|
||||||
|
className={cn(
|
||||||
|
"group/field-content flex flex-1 flex-col gap-1.5 leading-snug",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function FieldLabel({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof Label>) {
|
||||||
|
return (
|
||||||
|
<Label
|
||||||
|
data-slot="field-label"
|
||||||
|
className={cn(
|
||||||
|
"group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50",
|
||||||
|
"has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4",
|
||||||
|
"has-data-[state=checked]:bg-primary/5 has-data-[state=checked]:border-primary dark:has-data-[state=checked]:bg-primary/10",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="field-label"
|
||||||
|
className={cn(
|
||||||
|
"flex w-fit items-center gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
|
||||||
|
return (
|
||||||
|
<p
|
||||||
|
data-slot="field-description"
|
||||||
|
className={cn(
|
||||||
|
"text-muted-foreground text-sm leading-normal font-normal group-has-[[data-orientation=horizontal]]/field:text-balance",
|
||||||
|
"last:mt-0 nth-last-2:-mt-1 [[data-variant=legend]+&]:-mt-1.5",
|
||||||
|
"[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function FieldSeparator({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"div"> & {
|
||||||
|
children?: React.ReactNode
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="field-separator"
|
||||||
|
data-content={!!children}
|
||||||
|
className={cn(
|
||||||
|
"relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<Separator className="absolute inset-0 top-1/2" />
|
||||||
|
{children && (
|
||||||
|
<span
|
||||||
|
className="bg-background text-muted-foreground relative mx-auto block w-fit px-2"
|
||||||
|
data-slot="field-separator-content"
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function FieldError({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
errors,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"div"> & {
|
||||||
|
errors?: Array<{ message?: string } | undefined>
|
||||||
|
}) {
|
||||||
|
const content = useMemo(() => {
|
||||||
|
if (children) {
|
||||||
|
return children
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!errors?.length) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const uniqueErrors = [
|
||||||
|
...new Map(errors.map((error) => [error?.message, error])).values(),
|
||||||
|
]
|
||||||
|
|
||||||
|
if (uniqueErrors?.length == 1) {
|
||||||
|
return uniqueErrors[0]?.message
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ul className="ml-4 flex list-disc flex-col gap-1">
|
||||||
|
{uniqueErrors.map(
|
||||||
|
(error, index) =>
|
||||||
|
error?.message && <li key={index}>{error.message}</li>
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
}, [children, errors])
|
||||||
|
|
||||||
|
if (!content) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
role="alert"
|
||||||
|
data-slot="field-error"
|
||||||
|
className={cn("text-destructive text-sm font-normal", className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
Field,
|
||||||
|
FieldLabel,
|
||||||
|
FieldDescription,
|
||||||
|
FieldError,
|
||||||
|
FieldGroup,
|
||||||
|
FieldLegend,
|
||||||
|
FieldSeparator,
|
||||||
|
FieldSet,
|
||||||
|
FieldContent,
|
||||||
|
FieldTitle,
|
||||||
|
}
|
||||||
28
src/components/ui/kbd.tsx
Normal file
28
src/components/ui/kbd.tsx
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Kbd({ className, ...props }: React.ComponentProps<"kbd">) {
|
||||||
|
return (
|
||||||
|
<kbd
|
||||||
|
data-slot="kbd"
|
||||||
|
className={cn(
|
||||||
|
"bg-muted text-muted-foreground pointer-events-none inline-flex h-5 w-fit min-w-5 items-center justify-center gap-1 rounded-sm px-1 font-sans text-xs font-medium select-none",
|
||||||
|
"[&_svg:not([class*='size-'])]:size-3",
|
||||||
|
"[[data-slot=tooltip-content]_&]:bg-background/20 [[data-slot=tooltip-content]_&]:text-background dark:[[data-slot=tooltip-content]_&]:bg-background/10",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function KbdGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<kbd
|
||||||
|
data-slot="kbd-group"
|
||||||
|
className={cn("inline-flex items-center gap-1", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Kbd, KbdGroup }
|
||||||
|
|
@ -5,133 +5,124 @@ import { XIcon } from "lucide-react"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
|
function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
|
||||||
return <SheetPrimitive.Root data-slot="sheet" {...props} />
|
return <SheetPrimitive.Root data-slot="sheet" {...props} />
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetTrigger({
|
function SheetTrigger({ ...props }: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
|
||||||
...props
|
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
|
||||||
}: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
|
|
||||||
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetClose({
|
function SheetClose({ ...props }: React.ComponentProps<typeof SheetPrimitive.Close>) {
|
||||||
...props
|
return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
|
||||||
}: React.ComponentProps<typeof SheetPrimitive.Close>) {
|
|
||||||
return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetPortal({
|
function SheetPortal({ ...props }: React.ComponentProps<typeof SheetPrimitive.Portal>) {
|
||||||
...props
|
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
|
||||||
}: React.ComponentProps<typeof SheetPrimitive.Portal>) {
|
|
||||||
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetOverlay({
|
function SheetOverlay({
|
||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
|
}: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
|
||||||
return (
|
return (
|
||||||
<SheetPrimitive.Overlay
|
<SheetPrimitive.Overlay
|
||||||
data-slot="sheet-overlay"
|
data-slot="sheet-overlay"
|
||||||
className={cn(
|
className={cn(
|
||||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetContent({
|
function SheetContent({
|
||||||
className,
|
className,
|
||||||
children,
|
children,
|
||||||
side = "right",
|
side = "right",
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof SheetPrimitive.Content> & {
|
}: React.ComponentProps<typeof SheetPrimitive.Content> & {
|
||||||
side?: "top" | "right" | "bottom" | "left"
|
side?: "top" | "right" | "bottom" | "left"
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<SheetPortal>
|
<SheetPortal>
|
||||||
<SheetOverlay />
|
<SheetOverlay />
|
||||||
<SheetPrimitive.Content
|
<SheetPrimitive.Content
|
||||||
data-slot="sheet-content"
|
data-slot="sheet-content"
|
||||||
className={cn(
|
className={cn(
|
||||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
|
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
|
||||||
side === "right" &&
|
side === "right" &&
|
||||||
"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
|
"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
|
||||||
side === "left" &&
|
side === "left" &&
|
||||||
"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
|
"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
|
||||||
side === "top" &&
|
side === "top" &&
|
||||||
"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
|
"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
|
||||||
side === "bottom" &&
|
side === "bottom" &&
|
||||||
"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
|
"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
|
<SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
|
||||||
<XIcon className="size-4" />
|
<XIcon className="size-4" />
|
||||||
<span className="sr-only">Close</span>
|
<span className="sr-only">Close</span>
|
||||||
</SheetPrimitive.Close>
|
</SheetPrimitive.Close>
|
||||||
</SheetPrimitive.Content>
|
</SheetPrimitive.Content>
|
||||||
</SheetPortal>
|
</SheetPortal>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
|
function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-slot="sheet-header"
|
data-slot="sheet-header"
|
||||||
className={cn("flex flex-col gap-1.5 p-4", className)}
|
className={cn("flex flex-col gap-1.5 p-4", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
|
function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-slot="sheet-footer"
|
data-slot="sheet-footer"
|
||||||
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
|
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetTitle({
|
function SheetTitle({ className, ...props }: React.ComponentProps<typeof SheetPrimitive.Title>) {
|
||||||
className,
|
return (
|
||||||
...props
|
<SheetPrimitive.Title
|
||||||
}: React.ComponentProps<typeof SheetPrimitive.Title>) {
|
data-slot="sheet-title"
|
||||||
return (
|
className={cn("text-foreground font-semibold", className)}
|
||||||
<SheetPrimitive.Title
|
{...props}
|
||||||
data-slot="sheet-title"
|
/>
|
||||||
className={cn("text-foreground font-semibold", className)}
|
)
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetDescription({
|
function SheetDescription({
|
||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof SheetPrimitive.Description>) {
|
}: React.ComponentProps<typeof SheetPrimitive.Description>) {
|
||||||
return (
|
return (
|
||||||
<SheetPrimitive.Description
|
<SheetPrimitive.Description
|
||||||
data-slot="sheet-description"
|
data-slot="sheet-description"
|
||||||
className={cn("text-muted-foreground text-sm", className)}
|
className={cn("text-muted-foreground text-sm", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Sheet,
|
Sheet,
|
||||||
SheetTrigger,
|
SheetTrigger,
|
||||||
SheetClose,
|
SheetClose,
|
||||||
SheetContent,
|
SheetContent,
|
||||||
SheetHeader,
|
SheetHeader,
|
||||||
SheetFooter,
|
SheetFooter,
|
||||||
SheetTitle,
|
SheetTitle,
|
||||||
SheetDescription,
|
SheetDescription,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
77
src/containers/Search.tsx
Normal file
77
src/containers/Search.tsx
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
import { useState, useEffect, useRef } from "react"
|
||||||
|
import { useLocation } from "react-router"
|
||||||
|
import { useForm } from "@tanstack/react-form"
|
||||||
|
import { useQuery, useQueryClient } from "@tanstack/react-query"
|
||||||
|
import api from "@/api/eolas-api"
|
||||||
|
import SearchInput from "@/components/SearchInput"
|
||||||
|
import SearchResults from "@/components/SearchResults"
|
||||||
|
|
||||||
|
export default function Search() {
|
||||||
|
const location = useLocation()
|
||||||
|
const inputRef = useRef(null)
|
||||||
|
const [sheetOpen, setSheetOpen] = useState(false)
|
||||||
|
const [tagResults, setTagResults] = useState([])
|
||||||
|
const [searchParams, setSearchParams] = useState(null)
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
const tags = queryClient.getQueryData(["tag_list"])?.["data"]
|
||||||
|
|
||||||
|
const form = useForm({
|
||||||
|
defaultValues: {
|
||||||
|
search: "",
|
||||||
|
},
|
||||||
|
onSubmit: ({ value }) => {
|
||||||
|
setSearchParams(value.search)
|
||||||
|
setSheetOpen(true)
|
||||||
|
setTagResults(
|
||||||
|
tags?.filter((tag) => tag.toLowerCase().includes(value.search.toLowerCase())),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: entriesResults,
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
} = useQuery({
|
||||||
|
queryKey: ["search_results", searchParams],
|
||||||
|
queryFn: () => api.get(`/search/${searchParams}`).then((res) => res.data),
|
||||||
|
enabled: !!searchParams,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Force Sheet close on renavigation (i.e search result selection)
|
||||||
|
useEffect(() => {
|
||||||
|
if (sheetOpen) {
|
||||||
|
setSheetOpen(false)
|
||||||
|
form.reset()
|
||||||
|
setSearchParams(null)
|
||||||
|
}
|
||||||
|
}, [location.pathname])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleKeyDown = (e) => {
|
||||||
|
if ((e.ctrlKey || e.metaKey) && e.key === "k") {
|
||||||
|
e.preventDefault()
|
||||||
|
inputRef.current?.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener("keydown", handleKeyDown)
|
||||||
|
return () => document.removeEventListener("keydown", handleKeyDown)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SearchInput form={form} inputRef={inputRef} />
|
||||||
|
<SearchResults
|
||||||
|
searchParams={searchParams}
|
||||||
|
form={form}
|
||||||
|
sheetOpen={sheetOpen}
|
||||||
|
entriesResults={entriesResults}
|
||||||
|
tagResults={tagResults}
|
||||||
|
loading={isLoading}
|
||||||
|
error={error}
|
||||||
|
setSheetOpen={setSheetOpen}
|
||||||
|
setSearchParams={setSearchParams}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import ThemeToggle from "@/components/ThemeToggle"
|
import ThemeToggle from "@/components/ThemeToggle"
|
||||||
export default function Settings() {
|
export default function Settings() {
|
||||||
return (
|
return (
|
||||||
<div className="p-4 lg:p-6">
|
<div className="p-4 lg:p-6">
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue