Relevant note new UI (#981)

* Relevant note new UI
* Make Relevant Notes section a sticky head
* Test distribution
* Adjust similarity threshold
* Adjust relevance sorting
* Make refresh reindex
* Address latest comments
This commit is contained in:
Zero Liu 2024-12-30 13:55:30 -08:00 committed by GitHub
parent 99a8764ba5
commit 82eaaaf113
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 1373 additions and 198 deletions

736
package-lock.json generated
View file

@ -19,8 +19,10 @@
"@langchain/groq": "^0.1.2",
"@langchain/openai": "^0.3.14",
"@orama/orama": "^3.0.0-rc-2",
"@radix-ui/react-collapsible": "^1.1.2",
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-tooltip": "^1.1.3",
"@radix-ui/react-popover": "^1.1.4",
"@radix-ui/react-tooltip": "^1.1.6",
"@tabler/icons-react": "^2.14.0",
"axios": "^1.3.4",
"chrono-node": "^2.7.7",
@ -48,6 +50,7 @@
},
"devDependencies": {
"@langchain/ollama": "^0.1.1",
"@tailwindcss/container-queries": "^0.1.1",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@types/crypto-js": "^4.1.1",
@ -4177,6 +4180,122 @@
}
}
},
"node_modules/@radix-ui/react-collapsible": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.2.tgz",
"integrity": "sha512-PliMB63vxz7vggcyq0IxNYk8vGDrLXVWw4+W4B8YnwI1s18x7YZYqlG9PLX7XxAJUi0g2DxP4XKJMFHh/iVh9A==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.1",
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-context": "1.1.1",
"@radix-ui/react-id": "1.1.0",
"@radix-ui/react-presence": "1.1.2",
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-use-controllable-state": "1.1.0",
"@radix-ui/react-use-layout-effect": "1.1.0"
},
"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-collapsible/node_modules/@radix-ui/primitive": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz",
"integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==",
"license": "MIT"
},
"node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
"integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
"license": "MIT",
"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-collapsible/node_modules/@radix-ui/react-presence": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz",
"integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-use-layout-effect": "1.1.0"
},
"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-collapsible/node_modules/@radix-ui/react-primitive": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz",
"integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-slot": "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-collapsible/node_modules/@radix-ui/react-slot": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz",
"integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.1"
},
"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-collection": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz",
@ -4406,6 +4525,285 @@
}
}
},
"node_modules/@radix-ui/react-popover": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.4.tgz",
"integrity": "sha512-aUACAkXx8LaFymDma+HQVji7WhvEhpFJ7+qPz17Nf4lLZqtreGOFRiNQWQmhzp7kEWg9cOyyQJpdIMUMPc/CPw==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.1",
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-context": "1.1.1",
"@radix-ui/react-dismissable-layer": "1.1.3",
"@radix-ui/react-focus-guards": "1.1.1",
"@radix-ui/react-focus-scope": "1.1.1",
"@radix-ui/react-id": "1.1.0",
"@radix-ui/react-popper": "1.2.1",
"@radix-ui/react-portal": "1.1.3",
"@radix-ui/react-presence": "1.1.2",
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-slot": "1.1.1",
"@radix-ui/react-use-controllable-state": "1.1.0",
"aria-hidden": "^1.1.1",
"react-remove-scroll": "^2.6.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-popover/node_modules/@radix-ui/primitive": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz",
"integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==",
"license": "MIT"
},
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-arrow": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.1.tgz",
"integrity": "sha512-NaVpZfmv8SKeZbn4ijN2V3jlHA9ngBG16VnIIm22nUR0Yk8KUALyBxT3KYEUnNuch9sTE8UTsS3whzBgKOL30w==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-primitive": "2.0.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-popover/node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
"integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
"license": "MIT",
"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-popover/node_modules/@radix-ui/react-dismissable-layer": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.3.tgz",
"integrity": "sha512-onrWn/72lQoEucDmJnr8uczSNTujT0vJnA/X5+3AkChVPowr8n1yvIKIabhWyMQeMvvmdpsvcyDqx3X1LEXCPg==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.1",
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-use-callback-ref": "1.1.0",
"@radix-ui/react-use-escape-keydown": "1.1.0"
},
"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-popover/node_modules/@radix-ui/react-focus-scope": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.1.tgz",
"integrity": "sha512-01omzJAYRxXdG2/he/+xy+c8a8gCydoQ1yOxnWNcRhrrBW5W+RQJ22EK1SaO8tb3WoUsuEw7mJjBozPzihDFjA==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-use-callback-ref": "1.1.0"
},
"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-popover/node_modules/@radix-ui/react-popper": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.1.tgz",
"integrity": "sha512-3kn5Me69L+jv82EKRuQCXdYyf1DqHwD2U/sxoNgBGCB7K9TRc3bQamQ+5EPM9EvyPdli0W41sROd+ZU1dTCztw==",
"license": "MIT",
"dependencies": {
"@floating-ui/react-dom": "^2.0.0",
"@radix-ui/react-arrow": "1.1.1",
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-context": "1.1.1",
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-use-callback-ref": "1.1.0",
"@radix-ui/react-use-layout-effect": "1.1.0",
"@radix-ui/react-use-rect": "1.1.0",
"@radix-ui/react-use-size": "1.1.0",
"@radix-ui/rect": "1.1.0"
},
"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-popover/node_modules/@radix-ui/react-portal": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.3.tgz",
"integrity": "sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-use-layout-effect": "1.1.0"
},
"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-popover/node_modules/@radix-ui/react-presence": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz",
"integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-use-layout-effect": "1.1.0"
},
"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-popover/node_modules/@radix-ui/react-primitive": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz",
"integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-slot": "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-popover/node_modules/@radix-ui/react-slot": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz",
"integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.1"
},
"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-popover/node_modules/react-remove-scroll": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.2.tgz",
"integrity": "sha512-KmONPx5fnlXYJQqC62Q+lwIeAk64ws/cUw6omIumRzMRPqgnYqhSSti99nbj0Ry13bv7dF+BKn7NB+OqkdZGTw==",
"license": "MIT",
"dependencies": {
"react-remove-scroll-bar": "^2.3.7",
"react-style-singleton": "^2.2.1",
"tslib": "^2.1.0",
"use-callback-ref": "^1.3.3",
"use-sidecar": "^1.1.2"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popper": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz",
@ -4582,22 +4980,23 @@
}
},
"node_modules/@radix-ui/react-tooltip": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.3.tgz",
"integrity": "sha512-Z4w1FIS0BqVFI2c1jZvb/uDVJijJjJ2ZMuPV81oVgTZ7g3BZxobplnMVvXtFWgtozdvYJ+MFWtwkM5S2HnAong==",
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.6.tgz",
"integrity": "sha512-TLB5D8QLExS1uDn7+wH/bjEmRurNMTzNrtq7IjaS4kjion9NtzsTGkvR5+i7yc9q01Pi2KMM2cN3f8UG4IvvXA==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.0",
"@radix-ui/react-compose-refs": "1.1.0",
"@radix-ui/primitive": "1.1.1",
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-context": "1.1.1",
"@radix-ui/react-dismissable-layer": "1.1.1",
"@radix-ui/react-dismissable-layer": "1.1.3",
"@radix-ui/react-id": "1.1.0",
"@radix-ui/react-popper": "1.2.0",
"@radix-ui/react-portal": "1.1.2",
"@radix-ui/react-presence": "1.1.1",
"@radix-ui/react-primitive": "2.0.0",
"@radix-ui/react-slot": "1.1.0",
"@radix-ui/react-popper": "1.2.1",
"@radix-ui/react-portal": "1.1.3",
"@radix-ui/react-presence": "1.1.2",
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-slot": "1.1.1",
"@radix-ui/react-use-controllable-state": "1.1.0",
"@radix-ui/react-visually-hidden": "1.1.0"
"@radix-ui/react-visually-hidden": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
@ -4614,6 +5013,198 @@
}
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/primitive": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz",
"integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==",
"license": "MIT"
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-arrow": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.1.tgz",
"integrity": "sha512-NaVpZfmv8SKeZbn4ijN2V3jlHA9ngBG16VnIIm22nUR0Yk8KUALyBxT3KYEUnNuch9sTE8UTsS3whzBgKOL30w==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-primitive": "2.0.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-tooltip/node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
"integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
"license": "MIT",
"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-tooltip/node_modules/@radix-ui/react-dismissable-layer": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.3.tgz",
"integrity": "sha512-onrWn/72lQoEucDmJnr8uczSNTujT0vJnA/X5+3AkChVPowr8n1yvIKIabhWyMQeMvvmdpsvcyDqx3X1LEXCPg==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.1",
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-use-callback-ref": "1.1.0",
"@radix-ui/react-use-escape-keydown": "1.1.0"
},
"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-tooltip/node_modules/@radix-ui/react-popper": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.1.tgz",
"integrity": "sha512-3kn5Me69L+jv82EKRuQCXdYyf1DqHwD2U/sxoNgBGCB7K9TRc3bQamQ+5EPM9EvyPdli0W41sROd+ZU1dTCztw==",
"license": "MIT",
"dependencies": {
"@floating-ui/react-dom": "^2.0.0",
"@radix-ui/react-arrow": "1.1.1",
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-context": "1.1.1",
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-use-callback-ref": "1.1.0",
"@radix-ui/react-use-layout-effect": "1.1.0",
"@radix-ui/react-use-rect": "1.1.0",
"@radix-ui/react-use-size": "1.1.0",
"@radix-ui/rect": "1.1.0"
},
"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-tooltip/node_modules/@radix-ui/react-portal": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.3.tgz",
"integrity": "sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-use-layout-effect": "1.1.0"
},
"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-tooltip/node_modules/@radix-ui/react-presence": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz",
"integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-use-layout-effect": "1.1.0"
},
"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-tooltip/node_modules/@radix-ui/react-primitive": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz",
"integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-slot": "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-tooltip/node_modules/@radix-ui/react-slot": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz",
"integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.1"
},
"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-use-callback-ref": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz",
@ -4711,11 +5302,12 @@
}
},
"node_modules/@radix-ui/react-visually-hidden": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.0.tgz",
"integrity": "sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.1.tgz",
"integrity": "sha512-vVfA2IZ9q/J+gEamvj761Oq1FpWgCDaNOOIfbPVp2MVPLEomUr5+Vf7kJGwQ24YxZSlQVar7Bes8kyTo5Dshpg==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-primitive": "2.0.0"
"@radix-ui/react-primitive": "2.0.1"
},
"peerDependencies": {
"@types/react": "*",
@ -4732,6 +5324,62 @@
}
}
},
"node_modules/@radix-ui/react-visually-hidden/node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
"integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
"license": "MIT",
"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-visually-hidden/node_modules/@radix-ui/react-primitive": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz",
"integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-slot": "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-visually-hidden/node_modules/@radix-ui/react-slot": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz",
"integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.1"
},
"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/rect": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz",
@ -5799,6 +6447,16 @@
"react": "^16.5.1 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/@tailwindcss/container-queries": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@tailwindcss/container-queries/-/container-queries-0.1.1.tgz",
"integrity": "sha512-p18dswChx6WnTSaJCSGx6lTmrGzNNvm2FtXmiO6AuA1V4U5REyoqwmT6kgAsIMdjo07QdAfYXHJ4hnMtfHzWgA==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"tailwindcss": ">=3.2.0"
}
},
"node_modules/@testing-library/dom": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.2.0.tgz",
@ -10274,14 +10932,6 @@
"node": ">= 0.4"
}
},
"node_modules/invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/is-alphabetical": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz",
@ -15335,19 +15985,20 @@
}
},
"node_modules/react-remove-scroll-bar": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz",
"integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==",
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz",
"integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==",
"license": "MIT",
"dependencies": {
"react-style-singleton": "^2.2.1",
"react-style-singleton": "^2.2.2",
"tslib": "^2.0.0"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
"@types/react": "*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
@ -15356,20 +16007,20 @@
}
},
"node_modules/react-style-singleton": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz",
"integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==",
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
"integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==",
"license": "MIT",
"dependencies": {
"get-nonce": "^1.0.0",
"invariant": "^2.2.4",
"tslib": "^2.0.0"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
"@types/react": "*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
@ -17194,9 +17845,10 @@
}
},
"node_modules/use-callback-ref": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz",
"integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==",
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz",
"integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==",
"license": "MIT",
"dependencies": {
"tslib": "^2.0.0"
},
@ -17204,8 +17856,8 @@
"node": ">=10"
},
"peerDependencies": {
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
"@types/react": "*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {

View file

@ -34,6 +34,7 @@
"license": "AGPL-3.0",
"devDependencies": {
"@langchain/ollama": "^0.1.1",
"@tailwindcss/container-queries": "^0.1.1",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@types/crypto-js": "^4.1.1",
@ -77,8 +78,10 @@
"@langchain/groq": "^0.1.2",
"@langchain/openai": "^0.3.14",
"@orama/orama": "^3.0.0-rc-2",
"@radix-ui/react-collapsible": "^1.1.2",
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-tooltip": "^1.1.3",
"@radix-ui/react-popover": "^1.1.4",
"@radix-ui/react-tooltip": "^1.1.6",
"@tabler/icons-react": "^2.14.0",
"axios": "^1.3.4",
"chrono-node": "^2.7.7",

View file

@ -620,9 +620,10 @@ ${chatContent}`;
onRegenerate={handleRegenerate}
onEdit={handleEdit}
onDelete={handleDelete}
onSelectSuggestedPrompt={(prompt) => {
setInputMessage(prompt);
onInsertToChat={(prompt) => {
setInputMessage((prev) => `${prev} ${prompt} `);
}}
onReplaceChat={setInputMessage}
/>
<div className="bottom-container">
<ChatInput

View file

@ -1,5 +1,7 @@
import ChatSingleMessage from "@/components/chat-components/ChatSingleMessage";
import { RelevantNotes } from "@/components/chat-components/RelevantNotes";
import { SuggestedPrompts } from "@/components/chat-components/SuggestedPrompts";
import { useSettingsValue } from "@/settings/model";
import { ChatMessage } from "@/sharedState";
import { App } from "obsidian";
import React, { useEffect, useState } from "react";
@ -14,7 +16,8 @@ interface ChatMessagesProps {
onRegenerate: (messageIndex: number) => void;
onEdit: (messageIndex: number, newMessage: string) => void;
onDelete: (messageIndex: number) => void;
onSelectSuggestedPrompt: (prompt: string) => void;
onInsertToChat: (prompt: string) => void;
onReplaceChat: (prompt: string) => void;
}
const ChatMessages: React.FC<ChatMessagesProps> = ({
@ -27,12 +30,14 @@ const ChatMessages: React.FC<ChatMessagesProps> = ({
onRegenerate,
onEdit,
onDelete,
onSelectSuggestedPrompt,
onInsertToChat,
onReplaceChat,
}) => {
const [loadingDots, setLoadingDots] = useState("");
const settings = useSettingsValue();
const scrollToBottom = () => {
const chatMessagesContainer = document.querySelector(".chat-messages");
const chatMessagesContainer = document.querySelector("[data-testid='chat-messages']");
if (chatMessagesContainer) {
chatMessagesContainer.scrollTop = chatMessagesContainer.scrollHeight;
}
@ -58,8 +63,15 @@ const ChatMessages: React.FC<ChatMessagesProps> = ({
if (!chatHistory.filter((message) => message.isVisible).length && !currentAiMessage) {
return (
<div className="chat-messages">
<SuggestedPrompts onClick={onSelectSuggestedPrompt} />
<div className="flex flex-col gap-2 overflow-y-auto w-full h-full">
{settings.showRelevantNotes && (
<RelevantNotes
onInsertToChat={onInsertToChat}
defaultOpen={true}
key="relevant-notes-before-chat"
/>
)}
{settings.showSuggestedPrompts && <SuggestedPrompts onClick={onReplaceChat} />}
</div>
);
}
@ -69,38 +81,51 @@ const ChatMessages: React.FC<ChatMessagesProps> = ({
};
return (
<div className="chat-messages">
{chatHistory.map(
(message, index) =>
message.isVisible && (
<ChatSingleMessage
key={index}
message={message}
app={app}
isStreaming={false}
onInsertAtCursor={() => {
onInsertAtCursor(message.message);
}}
onRegenerate={() => onRegenerate(index)}
onEdit={(newMessage) => onEdit(index, newMessage)}
onDelete={() => onDelete(index)}
/>
)
)}
{(currentAiMessage || loading) && (
<ChatSingleMessage
key={`ai_message_${currentAiMessage}`}
message={{
sender: "AI",
message: currentAiMessage || getLoadingMessage(),
isVisible: true,
timestamp: null,
}}
app={app}
isStreaming={true}
onDelete={() => {}}
<div className="flex flex-col flex-1 h-full overflow-hidden">
{settings.showRelevantNotes && (
<RelevantNotes
className="mb-4"
onInsertToChat={onInsertToChat}
defaultOpen={false}
key="relevant-notes-in-chat"
/>
)}
<div
data-testid="chat-messages"
className="flex flex-col items-start justify-start flex-1 overflow-y-auto w-full break-words text-[calc(var(--font-text-size)_-_2px)] box-border scroll-smooth mt-auto select-text"
>
{chatHistory.map(
(message, index) =>
message.isVisible && (
<ChatSingleMessage
key={index}
message={message}
app={app}
isStreaming={false}
onInsertAtCursor={() => {
onInsertAtCursor(message.message);
}}
onRegenerate={() => onRegenerate(index)}
onEdit={(newMessage) => onEdit(index, newMessage)}
onDelete={() => onDelete(index)}
/>
)
)}
{(currentAiMessage || loading) && (
<ChatSingleMessage
key={`ai_message_${currentAiMessage}`}
message={{
sender: "AI",
message: currentAiMessage || getLoadingMessage(),
isVisible: true,
timestamp: null,
}}
app={app}
isStreaming={true}
onDelete={() => {}}
/>
)}
</div>
</div>
);
};

View file

@ -0,0 +1,310 @@
import { Badge } from "@/components/ui/badge";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import {
findRelevantNotes,
getSimilarityCategory,
RelevantNoteEntry,
} from "@/search/findRelevantNotes";
import VectorStoreManager from "@/search/vectorStoreManager";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import {
ArrowRight,
ChevronDown,
ChevronUp,
Info,
PlusCircle,
RefreshCcw,
TriangleAlert,
} from "lucide-react";
import React, { forwardRef, useEffect, useState } from "react";
import { Notice, TFile } from "obsidian";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { cn } from "@/lib/utils";
function useRelevantNotes(refresher: number) {
const [relevantNotes, setRelevantNotes] = useState<RelevantNoteEntry[]>([]);
const activeFile = app.workspace.getActiveFile();
useEffect(() => {
async function fetchNotes() {
if (!activeFile?.path) return;
const db = await VectorStoreManager.getInstance().getDb();
const notes = await findRelevantNotes({ db, filePath: activeFile.path });
setRelevantNotes(notes);
}
fetchNotes();
}, [activeFile?.path, refresher]);
return relevantNotes;
}
function useHasIndex(notePath: string, refresher: number) {
const [hasIndex, setHasIndex] = useState(true);
useEffect(() => {
if (!notePath) return;
async function fetchHasIndex() {
const hasIndex = await VectorStoreManager.getInstance().hasIndex(notePath);
setHasIndex(hasIndex);
}
fetchHasIndex();
}, [notePath, refresher]);
return hasIndex;
}
const ExplainerBadge = forwardRef<HTMLDivElement, { children: React.ReactNode }>(
({ children, ...props }, ref) => {
return (
<div ref={ref} {...props} className="flex gap-2">
<Badge
variant="secondary"
className="text-muted font-normal whitespace-nowrap cursor-default"
>
{children}
</Badge>
</div>
);
}
);
function inSameFolder(path1: string, path2: string) {
const folder1 = path1.split("/").slice(0, -1).join("/");
const folder2 = path2.split("/").slice(0, -1).join("/");
return folder1 === folder2;
}
function SimilarityBadge({ score }: { score: number }) {
const category = getSimilarityCategory(score);
let text = "🔴 Low Similarity";
if (category === 2) text = "🟠 Medium Similarity";
if (category === 3) text = "🟢 High Similarity";
return (
<Tooltip>
<TooltipTrigger asChild>
<ExplainerBadge>{text}</ExplainerBadge>
</TooltipTrigger>
<TooltipContent side="right">
Similarity Score: {Math.round(score * 100).toFixed(0)}%
</TooltipContent>
</Tooltip>
);
}
function Metadata({ note }: { note: RelevantNoteEntry }) {
return (
<div className="flex gap-1">
{note.metadata.similarityScore != null && (
<SimilarityBadge score={note.metadata.similarityScore} />
)}
{note.metadata.hasOutgoingLinks && <ExplainerBadge>Outgoing Links</ExplainerBadge>}
{note.metadata.hasBacklinks && <ExplainerBadge>Backlinks</ExplainerBadge>}
</div>
);
}
function RelevantNote({
showPath = false,
note,
onAddToChat,
onNavigateToNote,
}: {
showPath: boolean;
note: RelevantNoteEntry;
onAddToChat: () => void;
onNavigateToNote: () => void;
}) {
return (
<div className="flex gap-2 p-2 justify-between rounded-md border border-border border-solid">
<div className="flex flex-col gap-2 flex-1 overflow-hidden">
<a
onClick={onNavigateToNote}
className="text-sm text-normal font-bold text-ellipsis overflow-hidden whitespace-nowrap w-full"
>
{note.document.title}
</a>
{showPath && (
<span className="text-xs text-faint text-ellipsis overflow-hidden whitespace-nowrap w-full">
{note.document.path}
</span>
)}
<div className="flex gap-1">
<Metadata note={note} />
</div>
</div>
<div className="flex gap-2">
<Tooltip>
<TooltipTrigger asChild>
<button
className="size-6 p-0 !bg-transparent border-none !shadow-none hover:!bg-interactive-hover"
onClick={onAddToChat}
>
<PlusCircle className="size-4" />
</button>
</TooltipTrigger>
<TooltipContent>Add to Chat</TooltipContent>
</Tooltip>
</div>
</div>
);
}
function RelevantNotePopover({
note,
onAddToChat,
onNavigateToNote,
children,
}: {
note: RelevantNoteEntry;
onAddToChat: () => void;
onNavigateToNote: () => void;
children: React.ReactNode;
}) {
return (
<Popover key={note.document.path}>
<PopoverTrigger asChild>{children}</PopoverTrigger>
<PopoverContent className="flex flex-col gap-2 overflow-hidden w-fit min-w-72 max-w-96">
<span className="text-sm text-normal">{note.document.title}</span>
<span className="text-xs text-muted">{note.document.path}</span>
<Metadata note={note} />
<div className="flex gap-2">
<button
onClick={onAddToChat}
className="!bg-transparent inline-flex items-center gap-2 border border-border border-solid !shadow-none hover:!bg-interactive-hover"
>
Add to Chat <PlusCircle className="size-4" />
</button>
<button
onClick={onNavigateToNote}
className="!bg-transparent inline-flex items-center gap-2 border border-border border-solid !shadow-none hover:!bg-interactive-hover"
>
Navigate to Note <ArrowRight className="size-4" />
</button>
</div>
</PopoverContent>
</Popover>
);
}
export function RelevantNotes({
className,
onInsertToChat,
defaultOpen = false,
}: {
className?: string;
onInsertToChat: (prompt: string) => void;
defaultOpen?: boolean;
}) {
const [refresher, setRefresher] = useState(0);
const [isOpen, setIsOpen] = useState(defaultOpen);
const relevantNotes = useRelevantNotes(refresher);
const activeFile = app.workspace.getActiveFile();
const hasIndex = useHasIndex(activeFile?.path ?? "", refresher);
const navigateToNote = (notePath: string) => {
const file = app.vault.getAbstractFileByPath(notePath);
if (file instanceof TFile) {
app.workspace.getLeaf(false).openFile(file);
}
};
const addToChat = (prompt: string) => {
onInsertToChat(`[[${prompt}]]`);
};
const refreshIndex = async () => {
if (activeFile) {
await VectorStoreManager.getInstance().reindexFile(activeFile);
new Notice(`Reindexed ${activeFile.name}`);
setRefresher(refresher + 1);
}
};
return (
<div
className={cn(
"@container w-full border border-transparent border-b-border border-solid pb-2",
className
)}
>
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
<div className="flex justify-between items-center px-2 pb-2">
<div className="flex gap-2 items-center flex-1">
<span className="font-semibold text-normal">Relevant Notes</span>
<Tooltip>
<TooltipTrigger asChild>
<Info className="size-4 text-muted" />
</TooltipTrigger>
<TooltipContent side="bottom" className="w-64">
Relevance is a combination of semantic similarity and links.
</TooltipContent>
</Tooltip>
{!hasIndex && (
<Tooltip>
<TooltipTrigger asChild>
<TriangleAlert className="size-4 text-warning" />
</TooltipTrigger>
<TooltipContent side="bottom">Note has not been indexed</TooltipContent>
</Tooltip>
)}
</div>
<div className="flex gap-2 items-center">
<Tooltip>
<TooltipTrigger asChild>
<button
className="size-6 p-0 !bg-transparent border-none !shadow-none hover:!bg-interactive-hover"
onClick={refreshIndex}
>
<RefreshCcw className="size-4" />
</button>
</TooltipTrigger>
<TooltipContent side="bottom">Reindex Current Note</TooltipContent>
</Tooltip>
{relevantNotes.length > 0 && (
<CollapsibleTrigger asChild>
<button className="size-6 p-0 !bg-transparent border-none !shadow-none hover:!bg-interactive-hover">
{isOpen ? <ChevronUp className="size-4" /> : <ChevronDown className="size-4" />}
</button>
</CollapsibleTrigger>
)}
</div>
</div>
{relevantNotes.length === 0 && (
<div className="flex flex-wrap gap-x-2 gap-y-1 max-h-12 overflow-y-hidden px-2">
<span className="text-xs text-muted">No relevant notes found</span>
</div>
)}
{!isOpen && relevantNotes.length > 0 && (
<div className="flex flex-wrap gap-x-2 gap-y-1 max-h-6 overflow-y-hidden px-2">
{relevantNotes.map((note) => (
<RelevantNotePopover
key={note.document.path}
note={note}
onAddToChat={() => addToChat(note.document.title)}
onNavigateToNote={() => navigateToNote(note.document.path)}
>
<Badge
variant="outline"
key={note.document.path}
className="text-xs max-w-40 text-muted hover:cursor-pointer hover:bg-interactive-hover"
>
<span className="text-ellipsis overflow-hidden whitespace-nowrap">
{note.document.title}
</span>
</Badge>
</RelevantNotePopover>
))}
</div>
)}
<CollapsibleContent>
<div className="p-2 max-h-96 overflow-y-auto flex flex-col gap-2 @2xl:grid @2xl:grid-cols-2 @4xl:grid-cols-3">
{relevantNotes.map((note) => (
<RelevantNote
showPath={!inSameFolder(activeFile?.path ?? "", note.document.path)}
note={note}
key={note.document.path}
onAddToChat={() => addToChat(note.document.title)}
onNavigateToNote={() => navigateToNote(note.document.path)}
/>
))}
</div>
</CollapsibleContent>
</Collapsible>
</div>
);
}

View file

@ -1,7 +1,10 @@
import { useChainType } from "@/aiParams";
import { ChainType } from "@/chainFactory";
import { Card, CardTitle, CardContent, CardHeader } from "@/components/ui/card";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { VAULT_VECTOR_STORE_STRATEGY } from "@/constants";
import { useSettingsValue } from "@/settings/model";
import { PlusCircle, TriangleAlert } from "lucide-react";
import React, { useMemo } from "react";
interface NotePrompt {
@ -89,97 +92,58 @@ export const SuggestedPrompts: React.FC<SuggestedPromptsProps> = ({ onClick }) =
const prompts = useMemo(() => getRandomPrompt(chainType), [chainType]);
const settings = useSettingsValue();
const indexVaultToVectorStore = settings.indexVaultToVectorStore as VAULT_VECTOR_STORE_STRATEGY;
const showSuggestedPrompts = settings.showSuggestedPrompts;
if (!showSuggestedPrompts) {
return null;
}
return (
<div
style={{
width: "100%",
height: "100%",
display: "flex",
flexDirection: "column",
}}
>
<div
style={{
alignItems: "center",
width: "100%",
height: "100%",
flex: 1,
display: "flex",
justifyContent: "center",
flexDirection: "column",
}}
>
<div style={{ maxWidth: "500px", width: "90%" }}>
<p>
<b>Suggested Prompts</b>
</p>
<div
style={{
display: "flex",
flexDirection: "column",
gap: "1rem",
}}
>
<div className="flex flex-col gap-4">
<Card className="w-full bg-transparent">
<CardHeader className="px-2">
<CardTitle>Suggested Prompts</CardTitle>
</CardHeader>
<CardContent className="p-2 px-2 pt-0">
<div className="flex flex-col gap-2">
{prompts.map((prompt, i) => (
<button
<div
key={i}
onClick={() => onClick(prompt.text)}
style={{
cursor: "pointer",
display: "flex",
flexDirection: "column",
height: "fit-content",
gap: "0.5rem",
alignItems: "start",
padding: "0.5rem 1rem",
width: "100%",
whiteSpace: "normal",
textAlign: "left",
}}
className="flex gap-2 p-2 justify-between text-sm rounded-md border border-border border-solid"
>
<div style={{ color: "var(--text-muted)" }}>{prompt.title}</div>
<div>{prompt.text}</div>
</button>
<div className="flex flex-col gap-1">
<div className="text-muted">{prompt.title}</div>
<div>{prompt.text}</div>
</div>
<Tooltip>
<TooltipTrigger asChild>
<button
className="size-6 p-0 !bg-transparent border-none !shadow-none hover:!bg-interactive-hover"
onClick={() => onClick(prompt.text)}
>
<PlusCircle className="size-4" />
</button>
</TooltipTrigger>
<TooltipContent>Add to Chat</TooltipContent>
</Tooltip>
</div>
))}
</div>
{chainType === ChainType.VAULT_QA_CHAIN && (
<p
style={{
border: "1px solid var(--background-modifier-border)",
padding: "0.5rem",
borderRadius: "var(--radius-s)",
}}
>
Please note that this is a retrieval-based QA. Questions should contain keywords and
concepts that exist literally in your vault
</p>
)}
{chainType === ChainType.VAULT_QA_CHAIN &&
indexVaultToVectorStore === VAULT_VECTOR_STORE_STRATEGY.NEVER && (
<p
style={{
border: "1px solid var(--background-modifier-border)",
padding: "0.5rem",
borderRadius: "var(--radius-s)",
}}
>
Your auto-index strategy is set to <b>NEVER</b>. Before proceeding, click the{" "}
<span style={{ color: "var(--color-blue" }}>Refresh Index</span> button below or run
the{" "}
<span style={{ color: "var(--color-blue" }}>
Copilot command: Index (refresh) vault for QA
</span>{" "}
to update the index.
</p>
)}
</CardContent>
</Card>
{chainType === ChainType.VAULT_QA_CHAIN && (
<div className="text-sm border border-border border-solid p-2 rounded-md">
Please note that this is a retrieval-based QA. Questions should contain keywords and
concepts that exist literally in your vault
</div>
</div>
)}
{chainType === ChainType.VAULT_QA_CHAIN &&
indexVaultToVectorStore === VAULT_VECTOR_STORE_STRATEGY.NEVER && (
<div className="text-sm border border-border border-solid p-2 rounded-md">
<div>
<TriangleAlert className="size-4" /> Your auto-index strategy is set to <b>NEVER</b>.
Before proceeding, click the <span className="text-accent">Refresh Index</span> button
below or run the{" "}
<span className="text-accent">Copilot command: Index (refresh) vault for QA</span> to
update the index.
</div>
</div>
)}
</div>
);
};

View file

@ -0,0 +1,33 @@
import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const badgeVariants = cva(
"inline-flex items-center rounded-md px-2.5 py-0.5 text-normal text-ui-smaller transition-colors focus:outline-none",
{
variants: {
variant: {
default: "bg-primary-alt shadow",
secondary: "bg-secondary-alt",
destructive: "bg-modifier-error shadow",
outline: "border border-border border-solid",
},
},
defaultVariants: {
variant: "default",
},
}
);
export interface BadgeProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof badgeVariants> {}
const Badge = React.forwardRef<HTMLDivElement, BadgeProps>(
({ className, variant, ...props }, ref) => (
<div ref={ref} className={cn(badgeVariants({ variant }), className)} {...props} />
)
);
export { Badge, badgeVariants };

View file

@ -0,0 +1,55 @@
import * as React from "react";
import { cn } from "@/lib/utils";
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("rounded-xl border border-border bg-primary text-normal shadow", className)}
{...props}
/>
)
);
Card.displayName = "Card";
const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
)
);
CardHeader.displayName = "CardHeader";
const CardTitle = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("font-semibold leading-none tracking-tight", className)}
{...props}
/>
)
);
CardTitle.displayName = "CardTitle";
const CardDescription = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={cn("text-sm text-muted", className)} {...props} />
)
);
CardDescription.displayName = "CardDescription";
const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
)
);
CardContent.displayName = "CardContent";
const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} />
)
);
CardFooter.displayName = "CardFooter";
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };

View file

@ -0,0 +1,9 @@
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
const Collapsible = CollapsiblePrimitive.Root;
const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent;
export { Collapsible, CollapsibleTrigger, CollapsibleContent };

View file

@ -0,0 +1,31 @@
import * as React from "react";
import * as PopoverPrimitive from "@radix-ui/react-popover";
import { cn } from "@/lib/utils";
const Popover = PopoverPrimitive.Root;
const PopoverTrigger = PopoverPrimitive.Trigger;
const PopoverAnchor = PopoverPrimitive.Anchor;
const PopoverContent = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
<PopoverPrimitive.Portal container={activeDocument.body}>
<PopoverPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
"z-50 w-72 border-border border-solid rounded-md border bg-secondary p-4 text-normal shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
/>
</PopoverPrimitive.Portal>
));
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };

View file

@ -0,0 +1,30 @@
import * as React from "react";
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
import { cn } from "@/lib/utils";
const TooltipProvider = TooltipPrimitive.Provider;
const Tooltip = TooltipPrimitive.Root;
const TooltipTrigger = TooltipPrimitive.Trigger;
const TooltipContent = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<TooltipPrimitive.Portal container={activeDocument.body}>
<TooltipPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 overflow-hidden rounded-md bg-secondary border-border border border-solid px-3 py-1.5 text-xs text-normal animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
/>
</TooltipPrimitive.Portal>
));
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };

View file

@ -278,6 +278,7 @@ export const DEFAULT_SETTINGS: CopilotSettings = {
embeddingRequestsPerSecond: 10,
disableIndexOnMobile: true,
showSuggestedPrompts: true,
showRelevantNotes: true,
numPartitions: 1,
enabledCommands: {
[COMMAND_IDS.FIX_GRAMMAR]: {

View file

@ -1,6 +1,22 @@
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
import { extendTailwindMerge } from "tailwind-merge";
const customTwMerge = extendTailwindMerge({
extend: {
classGroups: {
"text-color": ["text-muted", "text-success", "text-warning", "text-error", "text-accent"],
"font-size": [
"text-smallest",
"text-smaller",
"text-small",
"text-ui-smaller",
"text-ui-small",
"text-ui-medium",
"text-ui-larger",
],
},
},
});
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
return customTwMerge(clsx(inputs));
}

View file

@ -550,4 +550,12 @@ export class DBOperations {
throw new CustomError("Failed to check if database is empty.");
}
}
public async hasIndex(notePath: string): Promise<boolean> {
if (!this.oramaDb) {
return false;
}
const docs = await DBOperations.getDocsByPath(this.oramaDb, notePath);
return docs !== undefined && docs.length > 0;
}
}

View file

@ -4,7 +4,7 @@ import { getSettings } from "@/settings/model";
import { InternalTypedDocument, Orama, Result } from "@orama/orama";
import { TFile } from "obsidian";
const MIN_SIMILARITY_SCORE = 0.3;
const MIN_SIMILARITY_SCORE = 0.4;
const MAX_K = 20;
const ORIGINAL_WEIGHT = 0.7;
const LINKS_WEIGHT = 0.3;
@ -186,7 +186,18 @@ export async function findRelevantNotes({
const similarityScoreMap = await calculateSimilarityScore({ db, filePath });
const noteLinks = getNoteLinks(file);
const mergedScoreMap = mergeScoreMaps(similarityScoreMap, noteLinks);
const sortedHits = Array.from(mergedScoreMap.entries()).sort((a, b) => b[1] - a[1]);
const sortedHits = Array.from(mergedScoreMap.entries()).sort((a, b) => {
const aPath = a[0];
const bPath = b[0];
const aCategory = getSimilarityCategory(similarityScoreMap.get(aPath) ?? 0);
const bCategory = getSimilarityCategory(similarityScoreMap.get(bPath) ?? 0);
if (aCategory !== bCategory) {
return bCategory - aCategory;
}
return b[1] - a[1];
});
return sortedHits
.map(([path, score]) => {
const file = app.vault.getAbstractFileByPath(path);
@ -208,3 +219,14 @@ export async function findRelevantNotes({
})
.filter((entry) => entry !== null);
}
/**
* Gets the similarity category for the given score.
* @param score - The score to get the similarity category for.
* @returns The similarity category. 1 is low, 2 is medium, 3 is high.
*/
export function getSimilarityCategory(score: number): number {
if (score > 0.7) return 3;
if (score > 0.55) return 2;
return 1;
}

View file

@ -2,7 +2,7 @@ import { CustomError } from "@/error";
import EmbeddingsManager from "@/LLMProviders/embeddingManager";
import { CopilotSettings, getSettings, subscribeToSettingsChange } from "@/settings/model";
import { Orama } from "@orama/orama";
import { Notice, Platform } from "obsidian";
import { Notice, Platform, TFile } from "obsidian";
import { DBOperations } from "./dbOperations";
import { IndexEventHandler } from "./indexEventHandler";
import { IndexOperations } from "./indexOperations";
@ -94,6 +94,10 @@ export default class VectorStoreManager {
}
}
private async waitForInitialization(): Promise<void> {
await this.initializationPromise;
}
public async indexVaultToVectorStore(overwrite?: boolean): Promise<number> {
await this.waitForInitialization();
if (Platform.isMobile && getSettings().disableIndexOnMobile) {
@ -123,8 +127,9 @@ export default class VectorStoreManager {
return this.dbOps.isIndexEmpty();
}
public async waitForInitialization(): Promise<void> {
await this.initializationPromise;
public async hasIndex(notePath: string): Promise<boolean> {
await this.waitForInitialization();
return this.dbOps.hasIndex(notePath);
}
public onunload(): void {
@ -145,4 +150,9 @@ export default class VectorStoreManager {
}
return db;
}
public async reindexFile(file: TFile): Promise<void> {
await this.waitForInitialization();
await this.indexOps.reindexFile(file);
}
}

View file

@ -105,6 +105,13 @@ const GeneralSettings: React.FC = () => {
value={settings.showSuggestedPrompts}
onChange={(value) => updateSetting("showSuggestedPrompts", value)}
/>
<ToggleComponent
name="Relevant Notes"
description="Show relevant notes in the chat view"
value={settings.showRelevantNotes}
onChange={(value) => updateSetting("showRelevantNotes", value)}
/>
<div className="chat-icon-selection-tooltip">
<h2>Open Plugin In</h2>
<div className="select-wrapper">

View file

@ -58,6 +58,7 @@ export interface CopilotSettings {
defaultOpenArea: DEFAULT_OPEN_AREA;
disableIndexOnMobile: boolean;
showSuggestedPrompts: boolean;
showRelevantNotes: boolean;
numPartitions: number;
}

View file

@ -11,6 +11,11 @@ If your plugin does not need CSS, delete this file.
*/
/* Popover needs to render on top of the workspace-leaf-resize-handle */
[data-radix-popper-content-wrapper] {
z-index: 9999 !important;
}
.button-container {
display: flex;
justify-content: space-between;
@ -457,24 +462,6 @@ If your plugin does not need CSS, delete this file.
height: 12px;
}
.chat-messages {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
flex: 1;
overflow-y: auto;
padding: 8px;
width: 100%;
word-wrap: break-word;
overflow-wrap: break-word;
font-size: calc(var(--font-text-size) - 2px);
box-sizing: border-box;
scroll-behavior: smooth;
margin-top: auto;
user-select: text;
}
.chat-message-container {
display: flex;
flex-direction: column;

View file

@ -3,18 +3,22 @@
import tailwindcssAnimate from "tailwindcss-animate";
import { colorOpacityPlugin } from "./src/lib/plugins/colorOpacityPlugin";
import colors from "tailwindcss/colors";
import containerQueries from "@tailwindcss/container-queries";
/** @type {import("tailwindcss").Config} */
module.exports = {
content: ["./src/**/*.{js,ts,jsx,tsx}", "./src/styles/tailwind.css"],
darkMode: ["class"],
plugins: [tailwindcssAnimate, colorOpacityPlugin],
plugins: [tailwindcssAnimate, colorOpacityPlugin, containerQueries],
corePlugins: {
preflight: false,
},
// https://github.com/tailwindlabs/tailwindcss/blob/main/stubs/config.full.js
theme: {
textColor: {
inherit: colors.inherit,
current: colors.current,
transparent: colors.transparent,
normal: "var(--text-normal)",
muted: "var(--text-muted)",
faint: "var(--text-faint)",
@ -27,10 +31,12 @@ module.exports = {
"accent-hover": "var(--text-accent-hover)",
selection: "var(--text-selection)",
"highlight-bg": "var(--text-highlight-bg)",
bold: "var(--bold-color)",
italic: "var(--italic-color)",
},
backgroundColor: {
inherit: colors.inherit,
current: colors.current,
transparent: colors.transparent,
background: "var(--background-primary)",
primary: "var(--background-primary)",
"primary-alt": "var(--background-primary-alt)",
secondary: "var(--background-secondary)",
@ -50,8 +56,18 @@ module.exports = {
DEFAULT: "var(--dropdown-background)",
},
},
interactive: {
normal: "var(--interactive-normal)",
hover: "var(--interactive-hover)",
accent: "var(--interactive-accent)",
"accent-hsl": "hsl(var(--interactive-accent-hsl),<alpha-value>)",
"accent-hover": "var(--interactive-accent-hover)",
},
},
borderColor: {
inherit: colors.inherit,
current: colors.current,
transparent: colors.transparent,
border: "var(--background-modifier-border)",
"border-hover": "var(--background-modifier-border-hover)",
"border-focus": "var(--background-modifier-border-focus)",
@ -97,13 +113,7 @@ module.exports = {
0: "rgba(var(--mono-rgb-0),<alpha-value>)",
100: "rgba(var(--mono-rgb-100),<alpha-value>)",
},
interactive: {
normal: "var(--interactive-normal)",
hover: "var(--interactive-hover)",
accent: "var(--interactive-accent)",
"accent-hsl": "hsl(var(--interactive-accent-hsl),<alpha-value>)",
"accent-hover": "var(--interactive-accent-hover)",
},
caret: "var(--caret-color)",
icon: {
DEFAULT: "var(--icon-color)",