mirror of
https://github.com/logancyang/obsidian-copilot.git
synced 2026-07-22 07:50:24 +00:00
Chat UI revamp (#1074)
This commit is contained in:
parent
45ca3b239c
commit
40c5f50c8b
16 changed files with 1136 additions and 722 deletions
621
package-lock.json
generated
621
package-lock.json
generated
|
|
@ -22,7 +22,7 @@
|
|||
"@radix-ui/react-checkbox": "^1.1.3",
|
||||
"@radix-ui/react-collapsible": "^1.1.2",
|
||||
"@radix-ui/react-dialog": "^1.1.3",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.2",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.4",
|
||||
"@radix-ui/react-label": "^2.1.0",
|
||||
"@radix-ui/react-popover": "^1.1.4",
|
||||
"@radix-ui/react-scroll-area": "^1.2.2",
|
||||
|
|
@ -4698,30 +4698,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-checkbox/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-checkbox/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",
|
||||
|
|
@ -4814,30 +4790,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"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",
|
||||
|
|
@ -5079,30 +5031,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-dialog/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-dialog/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",
|
||||
|
|
@ -5185,16 +5113,17 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-dropdown-menu": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.2.tgz",
|
||||
"integrity": "sha512-GVZMR+eqK8/Kes0a36Qrv+i20bAPXSn8rCBTHx30w+3ECnR5o3xixAlqcVaYvLeyKUsm0aqyhWfmUcqufM8nYA==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.4.tgz",
|
||||
"integrity": "sha512-iXU1Ab5ecM+yEepGAWK8ZhMyKX4ubFdCNtol4sT9D0OVErG9PNElfx3TQhjw7n7BC5nFVz68/5//clWy+8TXzA==",
|
||||
"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-id": "1.1.0",
|
||||
"@radix-ui/react-menu": "2.1.2",
|
||||
"@radix-ui/react-primitive": "2.0.0",
|
||||
"@radix-ui/react-menu": "2.1.4",
|
||||
"@radix-ui/react-primitive": "2.0.1",
|
||||
"@radix-ui/react-use-controllable-state": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
@ -5212,6 +5141,68 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-dropdown-menu/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-dropdown-menu/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-dropdown-menu/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-dropdown-menu/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-focus-guards": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz",
|
||||
|
|
@ -5291,28 +5282,29 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-menu": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.2.tgz",
|
||||
"integrity": "sha512-lZ0R4qR2Al6fZ4yCCZzu/ReTFrylHFxIqy7OezIpWF4bL0o9biKo0pFIvkaew3TyZ9Fy5gYVrR5zCGZBVbO1zg==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.4.tgz",
|
||||
"integrity": "sha512-BnOgVoL6YYdHAG6DtXONaR29Eq4nvbi8rutrV/xlr3RQCMMb3yqP85Qiw/3NReozrSW+4dfLkK+rc1hb4wPU/A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.0",
|
||||
"@radix-ui/react-collection": "1.1.0",
|
||||
"@radix-ui/react-compose-refs": "1.1.0",
|
||||
"@radix-ui/primitive": "1.1.1",
|
||||
"@radix-ui/react-collection": "1.1.1",
|
||||
"@radix-ui/react-compose-refs": "1.1.1",
|
||||
"@radix-ui/react-context": "1.1.1",
|
||||
"@radix-ui/react-direction": "1.1.0",
|
||||
"@radix-ui/react-dismissable-layer": "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.0",
|
||||
"@radix-ui/react-focus-scope": "1.1.1",
|
||||
"@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-roving-focus": "1.1.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-roving-focus": "1.1.1",
|
||||
"@radix-ui/react-slot": "1.1.1",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.0",
|
||||
"aria-hidden": "^1.1.1",
|
||||
"react-remove-scroll": "2.6.0"
|
||||
"react-remove-scroll": "^2.6.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
|
|
@ -5329,6 +5321,250 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-menu/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-menu/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-menu/node_modules/@radix-ui/react-collection": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.1.tgz",
|
||||
"integrity": "sha512-LwT3pSho9Dljg+wY2KN2mrrh6y3qELfftINERIzBUO9e0N+t0oMTyn3k9iv+ZqgrwGkRnLpNJrsMv9BZlt2yuA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@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-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-menu/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-menu/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-menu/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-menu/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-menu/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-menu/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-menu/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-menu/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-popover": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.4.tgz",
|
||||
|
|
@ -5518,30 +5754,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"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",
|
||||
|
|
@ -5677,11 +5889,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-presence": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.1.tgz",
|
||||
"integrity": "sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==",
|
||||
"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.0",
|
||||
"@radix-ui/react-compose-refs": "1.1.1",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
@ -5699,6 +5912,21 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-presence/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-primitive": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz",
|
||||
|
|
@ -5722,17 +5950,18 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-roving-focus": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz",
|
||||
"integrity": "sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==",
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.1.tgz",
|
||||
"integrity": "sha512-QE1RoxPGJ/Nm8Qmk0PxP8ojmoaS67i0s7hVssS7KuI2FQoc/uzVlZsqKfQvxPE6D8hICCPHJ4D88zNhT3OOmkw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.0",
|
||||
"@radix-ui/react-collection": "1.1.0",
|
||||
"@radix-ui/react-compose-refs": "1.1.0",
|
||||
"@radix-ui/react-context": "1.1.0",
|
||||
"@radix-ui/primitive": "1.1.1",
|
||||
"@radix-ui/react-collection": "1.1.1",
|
||||
"@radix-ui/react-compose-refs": "1.1.1",
|
||||
"@radix-ui/react-context": "1.1.1",
|
||||
"@radix-ui/react-direction": "1.1.0",
|
||||
"@radix-ui/react-id": "1.1.0",
|
||||
"@radix-ui/react-primitive": "2.0.0",
|
||||
"@radix-ui/react-primitive": "2.0.1",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.0",
|
||||
"@radix-ui/react-use-controllable-state": "1.1.0"
|
||||
},
|
||||
|
|
@ -5751,10 +5980,84 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-context": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz",
|
||||
"integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==",
|
||||
"node_modules/@radix-ui/react-roving-focus/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-roving-focus/node_modules/@radix-ui/react-collection": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.1.tgz",
|
||||
"integrity": "sha512-LwT3pSho9Dljg+wY2KN2mrrh6y3qELfftINERIzBUO9e0N+t0oMTyn3k9iv+ZqgrwGkRnLpNJrsMv9BZlt2yuA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@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-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-roving-focus/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-roving-focus/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-roving-focus/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"
|
||||
|
|
@ -5817,30 +6120,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-scroll-area/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-scroll-area/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",
|
||||
|
|
@ -6189,30 +6468,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"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",
|
||||
|
|
|
|||
|
|
@ -83,14 +83,14 @@
|
|||
"@radix-ui/react-checkbox": "^1.1.3",
|
||||
"@radix-ui/react-collapsible": "^1.1.2",
|
||||
"@radix-ui/react-dialog": "^1.1.3",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.2",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.4",
|
||||
"@radix-ui/react-label": "^2.1.0",
|
||||
"@radix-ui/react-popover": "^1.1.4",
|
||||
"@radix-ui/react-scroll-area": "^1.2.2",
|
||||
"@radix-ui/react-select": "^2.1.2",
|
||||
"@radix-ui/react-slider": "^1.2.1",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"@radix-ui/react-switch": "^1.1.1",
|
||||
"@radix-ui/react-popover": "^1.1.4",
|
||||
"@radix-ui/react-tooltip": "^1.1.6",
|
||||
"@tabler/icons-react": "^2.14.0",
|
||||
"axios": "^1.3.4",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useChainType, useModelKey } from "@/aiParams";
|
||||
import { ChainType } from "@/chainFactory";
|
||||
import { updateChatMemory } from "@/chatUtils";
|
||||
import { ChatControls } from "@/components/chat-components/ChatControls";
|
||||
import ChatInput from "@/components/chat-components/ChatInput";
|
||||
import ChatMessages from "@/components/chat-components/ChatMessages";
|
||||
import { ABORT_REASON, COMMAND_IDS, EVENT_NAMES, LOADING_MESSAGES, USER_SENDER } from "@/constants";
|
||||
|
|
@ -312,25 +313,6 @@ ${chatContent}`;
|
|||
[app, chatHistory, currentModelKey, settings.defaultConversationTag, settings.defaultSaveFolder]
|
||||
);
|
||||
|
||||
const refreshVaultContext = useCallback(async () => {
|
||||
if (!app) {
|
||||
console.error("App instance is not available.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await plugin.vectorStoreManager.indexVaultToVectorStore();
|
||||
new Notice("Vault index refreshed.");
|
||||
} catch (error) {
|
||||
console.error("Error refreshing vault index:", error);
|
||||
new Notice("Failed to refresh vault index. Check console for details.");
|
||||
}
|
||||
}, [app, plugin.vectorStoreManager]);
|
||||
|
||||
const clearCurrentAiMessage = useCallback(() => {
|
||||
setCurrentAiMessage("");
|
||||
}, []);
|
||||
|
||||
const handleStopGenerating = useCallback(
|
||||
(reason?: ABORT_REASON) => {
|
||||
if (abortController) {
|
||||
|
|
@ -533,6 +515,25 @@ ${chatContent}`;
|
|||
setInputMessage((prev) => `${prev} ${prompt} `);
|
||||
}, []);
|
||||
|
||||
const handleNewChat = useCallback(async () => {
|
||||
handleStopGenerating(ABORT_REASON.NEW_CHAT);
|
||||
if (settings.autosaveChat && chatHistory.length > 0) {
|
||||
await handleSaveAsNote(true);
|
||||
}
|
||||
clearMessages();
|
||||
chainManager.memoryManager.clearChatMemory();
|
||||
setCurrentAiMessage("");
|
||||
setContextNotes([]);
|
||||
setIncludeActiveNote(false);
|
||||
}, [
|
||||
handleStopGenerating,
|
||||
settings.autosaveChat,
|
||||
chatHistory.length,
|
||||
clearMessages,
|
||||
chainManager.memoryManager,
|
||||
handleSaveAsNote,
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className="chat-container">
|
||||
<ChatMessages
|
||||
|
|
@ -548,6 +549,7 @@ ${chatContent}`;
|
|||
onReplaceChat={setInputMessage}
|
||||
/>
|
||||
<div className="bottom-container">
|
||||
<ChatControls onNewChat={handleNewChat} onSaveAsNote={() => handleSaveAsNote(true)} />
|
||||
<ChatInput
|
||||
ref={inputRef}
|
||||
inputMessage={inputMessage}
|
||||
|
|
@ -557,19 +559,6 @@ ${chatContent}`;
|
|||
onStopGenerating={() => handleStopGenerating(ABORT_REASON.USER_STOPPED)}
|
||||
app={app}
|
||||
navigateHistory={navigateHistory}
|
||||
onNewChat={async (openNote: boolean) => {
|
||||
handleStopGenerating(ABORT_REASON.NEW_CHAT);
|
||||
if (settings.autosaveChat && chatHistory.length > 0) {
|
||||
await handleSaveAsNote(openNote);
|
||||
}
|
||||
clearMessages();
|
||||
chainManager.memoryManager.clearChatMemory();
|
||||
clearCurrentAiMessage();
|
||||
setContextNotes([]);
|
||||
setIncludeActiveNote(false);
|
||||
}}
|
||||
onSaveAsNote={() => handleSaveAsNote(true)}
|
||||
onRefreshVaultContext={refreshVaultContext}
|
||||
contextNotes={contextNotes}
|
||||
setContextNotes={setContextNotes}
|
||||
includeActiveNote={includeActiveNote}
|
||||
|
|
@ -578,7 +567,6 @@ ${chatContent}`;
|
|||
selectedImages={selectedImages}
|
||||
onAddImage={(files: File[]) => setSelectedImages((prev) => [...prev, ...files])}
|
||||
setSelectedImages={setSelectedImages}
|
||||
chatHistory={chatHistory}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { Platform } from "obsidian";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { USER_SENDER } from "@/constants";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { ChatMessage } from "@/sharedState";
|
||||
import {
|
||||
Check,
|
||||
|
|
@ -35,39 +39,79 @@ export const ChatButtons: React.FC<ChatButtonsProps> = ({
|
|||
hasSources,
|
||||
}) => {
|
||||
return (
|
||||
<div className="chat-message-buttons">
|
||||
<button onClick={onCopy} className="clickable-icon" title="Copy">
|
||||
{isCopied ? <Check /> : <Copy />}
|
||||
</button>
|
||||
<div
|
||||
className={cn("flex", {
|
||||
"group-hover:opacity-100 opacity-0": !Platform.isMobile,
|
||||
})}
|
||||
>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost2" size="fit" onClick={onCopy} title="Copy">
|
||||
{isCopied ? <Check className="size-4" /> : <Copy className="size-4" />}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Copy</TooltipContent>
|
||||
</Tooltip>
|
||||
{message.sender === USER_SENDER ? (
|
||||
<>
|
||||
<button onClick={onEdit} className="clickable-icon" title="Edit">
|
||||
<PenSquare />
|
||||
</button>
|
||||
<button onClick={onDelete} className="clickable-icon" title="Delete">
|
||||
<Trash2 />
|
||||
</button>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button onClick={onEdit} variant="ghost2" size="fit" title="Edit">
|
||||
<PenSquare className="size-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Edit</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button onClick={onDelete} variant="ghost2" size="fit" title="Delete">
|
||||
<Trash2 className="size-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Delete</TooltipContent>
|
||||
</Tooltip>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{hasSources && (
|
||||
<button onClick={onShowSources} className="clickable-icon" title="Show Sources">
|
||||
<LibraryBig />
|
||||
</button>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button onClick={onShowSources} variant="ghost2" size="fit" title="Show Sources">
|
||||
<LibraryBig className="size-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Show Sources</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
<button
|
||||
onClick={onInsertIntoEditor}
|
||||
className="clickable-icon"
|
||||
title="Insert to note at cursor"
|
||||
>
|
||||
<TextCursorInput />
|
||||
</button>
|
||||
<button onClick={onRegenerate} className="clickable-icon" title="Regenerate">
|
||||
<RotateCw />
|
||||
</button>
|
||||
<button onClick={onDelete} className="clickable-icon" title="Delete">
|
||||
<Trash2 />
|
||||
</button>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
onClick={onInsertIntoEditor}
|
||||
variant="ghost2"
|
||||
size="fit"
|
||||
title="Insert to note at cursor"
|
||||
>
|
||||
<TextCursorInput className="size-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Insert to note at cursor</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button onClick={onRegenerate} variant="ghost2" size="fit" title="Regenerate">
|
||||
<RotateCw className="size-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Regenerate</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button onClick={onDelete} variant="ghost2" size="fit" title="Delete">
|
||||
<Trash2 className="size-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Delete</TooltipContent>
|
||||
</Tooltip>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { Plus } from "lucide-react";
|
||||
import { Plus, X } from "lucide-react";
|
||||
import { TFile } from "obsidian";
|
||||
import React from "react";
|
||||
import { TooltipActionButton } from "./TooltipActionButton";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
|
||||
interface ChatContextMenuProps {
|
||||
activeNote: TFile | null;
|
||||
|
|
@ -12,6 +13,53 @@ interface ChatContextMenuProps {
|
|||
onRemoveUrl: (url: string) => void;
|
||||
}
|
||||
|
||||
function ContextNote({
|
||||
note,
|
||||
isActive = false,
|
||||
onRemoveContext,
|
||||
}: {
|
||||
note: TFile;
|
||||
isActive: boolean;
|
||||
onRemoveContext: (path: string) => void;
|
||||
}) {
|
||||
return (
|
||||
<Badge className="text-xs pl-2 pr-0.5 py-0 items-center">
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="max-w-40 truncate">{note.basename}</span>
|
||||
{isActive && <span className="text-xs text-faint">Current</span>}
|
||||
{note.extension === "pdf" && <span className="text-xs text-faint">pdf</span>}
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost2"
|
||||
size="fit"
|
||||
onClick={() => onRemoveContext(note.path)}
|
||||
aria-label="Remove from context"
|
||||
>
|
||||
<X className="size-4" />
|
||||
</Button>
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
function ContextUrl({ url, onRemoveUrl }: { url: string; onRemoveUrl: (url: string) => void }) {
|
||||
return (
|
||||
<Badge className="text-xs pl-2 pr-0.5 py-0 items-center">
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="max-w-40 truncate">{url}</span>
|
||||
<span className="text-xs text-faint">Link</span>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost2"
|
||||
size="fit"
|
||||
onClick={() => onRemoveUrl(url)}
|
||||
aria-label="Remove from context"
|
||||
>
|
||||
<X className="size-4" />
|
||||
</Button>
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
export const ChatContextMenu: React.FC<ChatContextMenuProps> = ({
|
||||
activeNote,
|
||||
contextNotes,
|
||||
|
|
@ -36,49 +84,40 @@ export const ChatContextMenu: React.FC<ChatContextMenuProps> = ({
|
|||
|
||||
const uniqueUrls = React.useMemo(() => Array.from(new Set(contextUrls)), [contextUrls]);
|
||||
|
||||
const renderNote = (note: TFile, isActive = false) => (
|
||||
<div key={note.path} className={`context-note ${isActive ? "active" : "with-hover"}`}>
|
||||
<span className="note-name">{note.basename}</span>
|
||||
{isActive && <span className="note-badge">current</span>}
|
||||
{note.extension === "pdf" && <span className="note-badge pdf">pdf</span>}
|
||||
<button
|
||||
className="remove-note"
|
||||
onClick={() => onRemoveContext(note.path)}
|
||||
aria-label="Remove from context"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
const hasContext = uniqueNotes.length > 0 || uniqueUrls.length > 0 || !!activeNote;
|
||||
|
||||
return (
|
||||
<div className="chat-context-menu">
|
||||
<TooltipActionButton onClick={onAddContext} Icon={<Plus className="icon-scaler" />}>
|
||||
Add Note to Context
|
||||
</TooltipActionButton>
|
||||
<div className="context-notes">
|
||||
{activeNote && renderNote(activeNote, true)}
|
||||
{uniqueNotes.map((note) => renderNote(note))}
|
||||
<div className="flex items-center w-full gap-1">
|
||||
<div className="flex items-start h-full">
|
||||
<Button
|
||||
onClick={onAddContext}
|
||||
variant="ghost2"
|
||||
size="fit"
|
||||
className="border border-border border-solid rounded-sm ml-1"
|
||||
>
|
||||
<Plus className="size-4" />
|
||||
{!hasContext && <span className="text-xs leading-4 pr-1">Add context</span>}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex gap-1 flex-wrap flex-1">
|
||||
{activeNote && (
|
||||
<ContextNote
|
||||
key={activeNote.path}
|
||||
note={activeNote}
|
||||
isActive={true}
|
||||
onRemoveContext={onRemoveContext}
|
||||
/>
|
||||
)}
|
||||
{uniqueNotes.map((note) => (
|
||||
<ContextNote
|
||||
key={note.path}
|
||||
note={note}
|
||||
isActive={false}
|
||||
onRemoveContext={onRemoveContext}
|
||||
/>
|
||||
))}
|
||||
{uniqueUrls.map((url) => (
|
||||
<div key={url} className="context-note url">
|
||||
<span className="note-name" title={url}>
|
||||
{(() => {
|
||||
const hostname = new URL(url).hostname;
|
||||
const cleanUrl = url.replace(/\/+$/, "");
|
||||
const path = cleanUrl.slice(cleanUrl.indexOf(hostname) + hostname.length);
|
||||
if (path.length <= 1) return hostname;
|
||||
return `${hostname}/...${cleanUrl.slice(-2)}`;
|
||||
})()}
|
||||
</span>
|
||||
<span className="note-badge">url</span>
|
||||
<button
|
||||
className="remove-note"
|
||||
onClick={() => onRemoveUrl(url)}
|
||||
aria-label="Remove URL from context"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
<ContextUrl key={url} url={url} onRemoveUrl={onRemoveUrl} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,172 +1,128 @@
|
|||
import { useChainType } from "@/aiParams";
|
||||
import { App } from "obsidian";
|
||||
import React from "react";
|
||||
|
||||
import {
|
||||
Download,
|
||||
MoreHorizontal,
|
||||
Sparkles,
|
||||
FileText,
|
||||
RefreshCw,
|
||||
MessageCirclePlus,
|
||||
ChevronDown,
|
||||
} from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { DropdownMenu, DropdownMenuTrigger } from "@radix-ui/react-dropdown-menu";
|
||||
import { DropdownMenuContent, DropdownMenuItem } from "@/components/ui/dropdown-menu";
|
||||
import { SettingSwitch } from "@/components/ui/setting-switch";
|
||||
import { updateSetting, useSettingsValue } from "@/settings/model";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { useChainType } from "@/aiParams";
|
||||
import { ChainType } from "@/chainFactory";
|
||||
import { TooltipActionButton } from "@/components/chat-components/TooltipActionButton";
|
||||
import { AddContextNoteModal } from "@/components/modals/AddContextNoteModal";
|
||||
import { stringToChainType } from "@/utils";
|
||||
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
||||
import { ChevronDown, Download, MessageCirclePlus, Puzzle } from "lucide-react";
|
||||
import { Notice } from "obsidian";
|
||||
import VectorStoreManager from "@/search/vectorStoreManager";
|
||||
|
||||
import { NewChatConfirmModal } from "@/components/modals/NewChatConfirmModal";
|
||||
import { useSettingsValue } from "@/settings/model";
|
||||
import { ChatMessage } from "@/sharedState";
|
||||
import { TFile } from "obsidian";
|
||||
import { ChatContextMenu } from "./ChatContextMenu";
|
||||
|
||||
interface ChatControlsProps {
|
||||
onNewChat: (openNote: boolean) => void;
|
||||
onSaveAsNote: () => void;
|
||||
onRefreshVaultContext: () => void;
|
||||
app: App;
|
||||
contextNotes: TFile[];
|
||||
setContextNotes: React.Dispatch<React.SetStateAction<TFile[]>>;
|
||||
includeActiveNote: boolean;
|
||||
setIncludeActiveNote: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
activeNote: TFile | null;
|
||||
contextUrls: string[];
|
||||
onRemoveUrl: (url: string) => void;
|
||||
chatHistory: ChatMessage[];
|
||||
export async function refreshVaultIndex() {
|
||||
try {
|
||||
await VectorStoreManager.getInstance().indexVaultToVectorStore();
|
||||
new Notice("Vault index refreshed.");
|
||||
} catch (error) {
|
||||
console.error("Error refreshing vault index:", error);
|
||||
new Notice("Failed to refresh vault index. Check console for details.");
|
||||
}
|
||||
}
|
||||
|
||||
const ChatControls: React.FC<ChatControlsProps> = ({
|
||||
onNewChat,
|
||||
onSaveAsNote,
|
||||
onRefreshVaultContext,
|
||||
app,
|
||||
contextNotes,
|
||||
setContextNotes,
|
||||
includeActiveNote,
|
||||
setIncludeActiveNote,
|
||||
activeNote,
|
||||
contextUrls,
|
||||
onRemoveUrl,
|
||||
chatHistory,
|
||||
}) => {
|
||||
const [selectedChain, setSelectedChain] = useChainType();
|
||||
interface ChatControlsProps {
|
||||
onNewChat: () => void;
|
||||
onSaveAsNote: () => void;
|
||||
}
|
||||
|
||||
export function ChatControls({ onNewChat, onSaveAsNote }: ChatControlsProps) {
|
||||
const settings = useSettingsValue();
|
||||
|
||||
const handleChainChange = async ({ value }: { value: string }) => {
|
||||
const newChain = stringToChainType(value);
|
||||
setSelectedChain(newChain);
|
||||
};
|
||||
|
||||
const handleAddContext = () => {
|
||||
const excludeNotes = [
|
||||
...contextNotes.map((note) => note.path),
|
||||
...(includeActiveNote && activeNote ? [activeNote.path] : []),
|
||||
].filter(Boolean) as string[];
|
||||
|
||||
new AddContextNoteModal({
|
||||
app,
|
||||
onNoteSelect: (note) => {
|
||||
if (activeNote && note.path === activeNote.path) {
|
||||
setIncludeActiveNote(true);
|
||||
// Remove the note from contextNotes if it exists there
|
||||
setContextNotes((prev) => prev.filter((n) => n.path !== note.path));
|
||||
} else {
|
||||
// Add wasAddedManually flag to distinguish from reference-added notes
|
||||
setContextNotes((prev) => [...prev, Object.assign(note, { wasAddedManually: true })]);
|
||||
}
|
||||
},
|
||||
excludeNotes,
|
||||
}).open();
|
||||
};
|
||||
|
||||
const handleRemoveContext = (path: string) => {
|
||||
// First check if this note was added manually
|
||||
const noteToRemove = contextNotes.find((note) => note.path === path);
|
||||
const wasAddedManually = noteToRemove && (noteToRemove as any).wasAddedManually;
|
||||
|
||||
if (wasAddedManually) {
|
||||
// If it was added manually, just remove it from contextNotes
|
||||
setContextNotes((prev) => prev.filter((note) => note.path !== path));
|
||||
} else {
|
||||
// If it wasn't added manually, it could be either:
|
||||
// 1. The active note (controlled by includeActiveNote)
|
||||
// 2. A note added via [[reference]]
|
||||
// In either case, we should:
|
||||
setIncludeActiveNote(false); // Turn off includeActiveNote if this was the active note
|
||||
setContextNotes((prev) => prev.filter((note) => note.path !== path)); // Remove from contextNotes if it was there
|
||||
}
|
||||
};
|
||||
|
||||
const [selectedChain, setSelectedChain] = useChainType();
|
||||
return (
|
||||
<div className="chat-controls-wrapper">
|
||||
<div className="chat-icons-container">
|
||||
{selectedChain === ChainType.COPILOT_PLUS_CHAIN && (
|
||||
<ChatContextMenu
|
||||
activeNote={includeActiveNote ? activeNote : null}
|
||||
contextNotes={contextNotes}
|
||||
onAddContext={handleAddContext}
|
||||
onRemoveContext={handleRemoveContext}
|
||||
contextUrls={contextUrls}
|
||||
onRemoveUrl={onRemoveUrl}
|
||||
/>
|
||||
)}
|
||||
<div className="chat-icons-right">
|
||||
<TooltipActionButton
|
||||
onClick={() => {
|
||||
if (!settings.autosaveChat && chatHistory.length > 0) {
|
||||
new NewChatConfirmModal(app, () => {
|
||||
onNewChat(false);
|
||||
}).open();
|
||||
} else {
|
||||
onNewChat(false);
|
||||
}
|
||||
}}
|
||||
Icon={<MessageCirclePlus className="icon-scaler" />}
|
||||
>
|
||||
<div>New Chat</div>
|
||||
{!settings.autosaveChat && <div>(Unsaved history will be lost)</div>}
|
||||
</TooltipActionButton>
|
||||
<TooltipActionButton onClick={onSaveAsNote} Icon={<Download className="icon-scaler" />}>
|
||||
Save as Note
|
||||
</TooltipActionButton>
|
||||
{(selectedChain === ChainType.VAULT_QA_CHAIN ||
|
||||
selectedChain === ChainType.COPILOT_PLUS_CHAIN) && (
|
||||
<TooltipActionButton
|
||||
onClick={onRefreshVaultContext}
|
||||
Icon={<Puzzle className="icon-scaler" />}
|
||||
>
|
||||
Refresh Index for Vault
|
||||
</TooltipActionButton>
|
||||
)}
|
||||
<div className="chat-icon-selection-tooltip">
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger className="chain-select-button">
|
||||
{selectedChain === ChainType.LLM_CHAIN && "chat"}
|
||||
{selectedChain === ChainType.VAULT_QA_CHAIN && "vault QA (basic)"}
|
||||
{selectedChain === ChainType.COPILOT_PLUS_CHAIN && "copilot plus (beta)"}
|
||||
<ChevronDown size={10} />
|
||||
</DropdownMenu.Trigger>
|
||||
<div className="w-full py-1 flex justify-between items-center px-1">
|
||||
<div className="flex-1">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost2" size="fit" className="ml-1">
|
||||
{selectedChain === ChainType.LLM_CHAIN && "chat"}
|
||||
{selectedChain === ChainType.VAULT_QA_CHAIN && "vault QA (basic)"}
|
||||
{selectedChain === ChainType.COPILOT_PLUS_CHAIN && "copilot plus (beta)"}
|
||||
<ChevronDown className="size-5 mt-0.5" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start">
|
||||
<DropdownMenuItem onSelect={() => setSelectedChain(ChainType.LLM_CHAIN)}>
|
||||
chat
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onSelect={() => setSelectedChain(ChainType.VAULT_QA_CHAIN)}>
|
||||
vault QA (basic)
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onSelect={() => setSelectedChain(ChainType.COPILOT_PLUS_CHAIN)}>
|
||||
copilot plus (beta)
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<div>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost2" size="icon" title="New Chat" onClick={onNewChat}>
|
||||
<MessageCirclePlus className="size-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>New Chat</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost2" size="icon" title="Save Chat as Note" onClick={onSaveAsNote}>
|
||||
<Download className="size-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Save Chat as Note</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<DropdownMenu.Portal container={activeDocument.body}>
|
||||
<DropdownMenu.Content className="chain-select-content" align="end" sideOffset={5}>
|
||||
<DropdownMenu.Item
|
||||
onSelect={() => handleChainChange({ value: ChainType.LLM_CHAIN })}
|
||||
>
|
||||
chat
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
onSelect={() => handleChainChange({ value: ChainType.VAULT_QA_CHAIN })}
|
||||
>
|
||||
vault QA (basic)
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
onSelect={() => handleChainChange({ value: ChainType.COPILOT_PLUS_CHAIN })}
|
||||
>
|
||||
copilot plus (beta)
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Portal>
|
||||
</DropdownMenu.Root>
|
||||
</div>
|
||||
</div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost2" size="icon" title="Advanced Settings">
|
||||
<MoreHorizontal className="size-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-64">
|
||||
<DropdownMenuItem
|
||||
className="flex justify-between"
|
||||
onSelect={(e) => {
|
||||
e.preventDefault();
|
||||
updateSetting("showSuggestedPrompts", !settings.showSuggestedPrompts);
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Sparkles className="size-4" />
|
||||
Suggested Prompt
|
||||
</div>
|
||||
<SettingSwitch checked={settings.showSuggestedPrompts} />
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className="flex justify-between"
|
||||
onSelect={(e) => {
|
||||
e.preventDefault();
|
||||
updateSetting("showRelevantNotes", !settings.showRelevantNotes);
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<FileText className="size-4" />
|
||||
Relevant Note
|
||||
</div>
|
||||
<SettingSwitch checked={settings.showRelevantNotes} />
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className="flex items-center gap-2"
|
||||
onSelect={() => refreshVaultIndex()}
|
||||
>
|
||||
<RefreshCw className="size-4" />
|
||||
Refresh Vault Index
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChatControls;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,15 +8,27 @@ import { CustomPromptProcessor } from "@/customPromptProcessor";
|
|||
import { COPILOT_TOOL_NAMES } from "@/LLMProviders/intentAnalyzer";
|
||||
import { Mention } from "@/mentions/Mention";
|
||||
import { getModelKeyFromModel, useSettingsValue } from "@/settings/model";
|
||||
import { ChatMessage } from "@/sharedState";
|
||||
import { getToolDescription } from "@/tools/toolManager";
|
||||
import { extractNoteTitles } from "@/utils";
|
||||
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
||||
import { ArrowBigUp, ChevronUp, Command, CornerDownLeft, Image, StopCircle } from "lucide-react";
|
||||
import {
|
||||
ArrowBigUp,
|
||||
ChevronDown,
|
||||
Command,
|
||||
CornerDownLeft,
|
||||
Image,
|
||||
StopCircle,
|
||||
X,
|
||||
} from "lucide-react";
|
||||
import { App, Platform, TFile } from "obsidian";
|
||||
import React, { forwardRef, useEffect, useImperativeHandle, useRef, useState } from "react";
|
||||
import ChatControls from "./ChatControls";
|
||||
import { TooltipActionButton } from "./TooltipActionButton";
|
||||
import ContextControl from "./ContextControl";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
interface ChatInputProps {
|
||||
inputMessage: string;
|
||||
|
|
@ -30,9 +42,6 @@ interface ChatInputProps {
|
|||
onStopGenerating: () => void;
|
||||
app: App;
|
||||
navigateHistory: (direction: "up" | "down") => string;
|
||||
onNewChat: (openNote: boolean) => void;
|
||||
onSaveAsNote: () => void;
|
||||
onRefreshVaultContext: () => void;
|
||||
contextNotes: TFile[];
|
||||
setContextNotes: React.Dispatch<React.SetStateAction<TFile[]>>;
|
||||
includeActiveNote: boolean;
|
||||
|
|
@ -41,7 +50,6 @@ interface ChatInputProps {
|
|||
selectedImages: File[];
|
||||
onAddImage: (files: File[]) => void;
|
||||
setSelectedImages: React.Dispatch<React.SetStateAction<File[]>>;
|
||||
chatHistory: ChatMessage[];
|
||||
}
|
||||
|
||||
const ChatInput = forwardRef<{ focus: () => void }, ChatInputProps>(
|
||||
|
|
@ -54,9 +62,6 @@ const ChatInput = forwardRef<{ focus: () => void }, ChatInputProps>(
|
|||
onStopGenerating,
|
||||
app,
|
||||
navigateHistory,
|
||||
onNewChat,
|
||||
onSaveAsNote,
|
||||
onRefreshVaultContext,
|
||||
contextNotes,
|
||||
setContextNotes,
|
||||
includeActiveNote,
|
||||
|
|
@ -65,7 +70,6 @@ const ChatInput = forwardRef<{ focus: () => void }, ChatInputProps>(
|
|||
selectedImages,
|
||||
onAddImage,
|
||||
setSelectedImages,
|
||||
chatHistory,
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
|
|
@ -352,11 +356,11 @@ const ChatInput = forwardRef<{ focus: () => void }, ChatInputProps>(
|
|||
}, [app.workspace]);
|
||||
|
||||
return (
|
||||
<div className="chat-input-container" ref={containerRef}>
|
||||
<ChatControls
|
||||
onNewChat={onNewChat}
|
||||
onSaveAsNote={onSaveAsNote}
|
||||
onRefreshVaultContext={onRefreshVaultContext}
|
||||
<div
|
||||
className="flex flex-col gap-0.5 w-full border border-border border-solid rounded-md pt-2 pb-1 px-1"
|
||||
ref={containerRef}
|
||||
>
|
||||
<ContextControl
|
||||
app={app}
|
||||
contextNotes={contextNotes}
|
||||
setContextNotes={setContextNotes}
|
||||
|
|
@ -365,7 +369,6 @@ const ChatInput = forwardRef<{ focus: () => void }, ChatInputProps>(
|
|||
activeNote={currentActiveNote}
|
||||
contextUrls={contextUrls}
|
||||
onRemoveUrl={(url: string) => setContextUrls((prev) => prev.filter((u) => u !== url))}
|
||||
chatHistory={chatHistory}
|
||||
/>
|
||||
|
||||
{selectedImages.length > 0 && (
|
||||
|
|
@ -382,7 +385,7 @@ const ChatInput = forwardRef<{ focus: () => void }, ChatInputProps>(
|
|||
onClick={() => setSelectedImages((prev) => prev.filter((_, i) => i !== index))}
|
||||
title="Remove image"
|
||||
>
|
||||
×
|
||||
<X className="size-4" />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
|
|
@ -391,7 +394,7 @@ const ChatInput = forwardRef<{ focus: () => void }, ChatInputProps>(
|
|||
|
||||
<textarea
|
||||
ref={textAreaRef}
|
||||
className="chat-input-textarea"
|
||||
className="w-full bg-transparent focus-visible:ring-0 border-none min-h-10 max-h-40 overflow-y-auto resize-none px-2 rounded-md text-sm text-normal"
|
||||
placeholder={
|
||||
"Ask anything. [[ for notes. / for custom prompts. " +
|
||||
(currentChain === ChainType.COPILOT_PLUS_CHAIN ? "@ for tools." : "")
|
||||
|
|
@ -401,79 +404,93 @@ const ChatInput = forwardRef<{ focus: () => void }, ChatInputProps>(
|
|||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
|
||||
<div className="chat-input-controls">
|
||||
<div className="chat-input-left">
|
||||
<DropdownMenu.Root open={isModelDropdownOpen} onOpenChange={setIsModelDropdownOpen}>
|
||||
<DropdownMenu.Trigger className="model-select-button">
|
||||
{settings.activeModels.find(
|
||||
(model) => getModelKeyFromModel(model) === currentModelKey
|
||||
)?.name || "Select Model"}
|
||||
<ChevronUp size={10} />
|
||||
</DropdownMenu.Trigger>
|
||||
<div className="flex gap-1 justify-between px-1">
|
||||
<div className="flex items-center gap-1">
|
||||
<DropdownMenu open={isModelDropdownOpen} onOpenChange={setIsModelDropdownOpen}>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost2" size="fit">
|
||||
{settings.activeModels.find(
|
||||
(model) => getModelKeyFromModel(model) === currentModelKey
|
||||
)?.name || "Select Model"}
|
||||
<ChevronDown className="size-5 mt-0.5" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
<DropdownMenu.Portal container={activeDocument.body}>
|
||||
<DropdownMenu.Content className="model-select-content" align="start">
|
||||
{settings.activeModels
|
||||
.filter((model) => model.enabled)
|
||||
.map((model) => (
|
||||
<DropdownMenu.Item
|
||||
key={getModelKeyFromModel(model)}
|
||||
onSelect={() => setCurrentModelKey(getModelKeyFromModel(model))}
|
||||
>
|
||||
{model.name}
|
||||
</DropdownMenu.Item>
|
||||
))}
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Portal>
|
||||
</DropdownMenu.Root>
|
||||
<DropdownMenuContent align="start">
|
||||
{settings.activeModels
|
||||
.filter((model) => model.enabled)
|
||||
.map((model) => (
|
||||
<DropdownMenuItem
|
||||
key={getModelKeyFromModel(model)}
|
||||
onSelect={() => setCurrentModelKey(getModelKeyFromModel(model))}
|
||||
>
|
||||
{model.name}
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
{currentChain === ChainType.COPILOT_PLUS_CHAIN && (
|
||||
<TooltipActionButton
|
||||
<Button
|
||||
variant="ghost2"
|
||||
size="fit"
|
||||
onClick={() => {
|
||||
new AddImageModal(app, onAddImage).open();
|
||||
}}
|
||||
Icon={
|
||||
<div className="button-content">
|
||||
<span>image</span>
|
||||
<Image className="icon-scaler" />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
Add Image
|
||||
</TooltipActionButton>
|
||||
<div className="flex items-center gap-1">
|
||||
<span>image</span>
|
||||
<Image className="!size-3" />
|
||||
</div>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="chat-input-buttons">
|
||||
<div className="flex items-center gap-1">
|
||||
{isGenerating && (
|
||||
<button onClick={() => onStopGenerating()} className="submit-button cancel">
|
||||
<StopCircle />
|
||||
</button>
|
||||
<Button
|
||||
variant="ghost2"
|
||||
size="fit"
|
||||
className="text-muted"
|
||||
onClick={() => onStopGenerating()}
|
||||
>
|
||||
<StopCircle className="size-4" />
|
||||
</Button>
|
||||
)}
|
||||
<button onClick={() => onSendMessage(false)} className="submit-button">
|
||||
<CornerDownLeft size={16} />
|
||||
<Button
|
||||
variant="ghost2"
|
||||
size="fit"
|
||||
className="text-muted"
|
||||
onClick={() => onSendMessage(false)}
|
||||
>
|
||||
<CornerDownLeft className="!size-3" />
|
||||
<span>chat</span>
|
||||
</button>
|
||||
</Button>
|
||||
|
||||
{currentChain === "copilot_plus" && (
|
||||
<button onClick={() => onSendMessage(true)} className="submit-button vault">
|
||||
<div className="button-content">
|
||||
<Button
|
||||
variant="ghost2"
|
||||
size="fit"
|
||||
className="text-muted"
|
||||
onClick={() => onSendMessage(true)}
|
||||
>
|
||||
<div className="flex items-center gap-1">
|
||||
{Platform.isMacOS ? (
|
||||
<>
|
||||
<Command size={12} />
|
||||
<ArrowBigUp size={16} />
|
||||
<CornerDownLeft size={16} />
|
||||
</>
|
||||
<div className="flex items-center">
|
||||
<Command className="!size-3" />
|
||||
<ArrowBigUp className="!size-3" />
|
||||
<CornerDownLeft className="!size-3" />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex items-center">
|
||||
<span>Ctrl</span>
|
||||
<ArrowBigUp size={16} />
|
||||
<CornerDownLeft size={16} />
|
||||
</>
|
||||
<ArrowBigUp className="size-4" />
|
||||
<CornerDownLeft className="!size-3" />
|
||||
</div>
|
||||
)}
|
||||
<span>vault</span>
|
||||
</div>
|
||||
</button>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -248,8 +248,8 @@ const ChatSingleMessage: React.FC<ChatSingleMessageProps> = ({
|
|||
<div className="flex flex-col w-full my-1">
|
||||
<div
|
||||
className={cn(
|
||||
"flex rounded-md p-2 mx-2 gap-2",
|
||||
message.sender === USER_SENDER && "bg-primary-alt"
|
||||
"flex rounded-md p-2 mx-2 gap-2 group",
|
||||
message.sender === USER_SENDER && "border border-border border-solid"
|
||||
)}
|
||||
>
|
||||
<div className="w-6 shrink-0">{message.sender === USER_SENDER ? <User /> : <Bot />}</div>
|
||||
|
|
@ -258,8 +258,8 @@ const ChatSingleMessage: React.FC<ChatSingleMessageProps> = ({
|
|||
<div className="message-content">{renderMessageContent()}</div>
|
||||
|
||||
{!isStreaming && (
|
||||
<div className="message-buttons-wrapper">
|
||||
<div className="message-timestamp">{message.timestamp?.display}</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="text-faint text-xs">{message.timestamp?.display}</div>
|
||||
<ChatButtons
|
||||
message={message}
|
||||
onCopy={copyToClipboard}
|
||||
|
|
|
|||
89
src/components/chat-components/ContextControl.tsx
Normal file
89
src/components/chat-components/ContextControl.tsx
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
import { useChainType } from "@/aiParams";
|
||||
import { App } from "obsidian";
|
||||
import React from "react";
|
||||
|
||||
import { ChainType } from "@/chainFactory";
|
||||
import { AddContextNoteModal } from "@/components/modals/AddContextNoteModal";
|
||||
import { TFile } from "obsidian";
|
||||
import { ChatContextMenu } from "./ChatContextMenu";
|
||||
|
||||
interface ChatControlsProps {
|
||||
app: App;
|
||||
contextNotes: TFile[];
|
||||
setContextNotes: React.Dispatch<React.SetStateAction<TFile[]>>;
|
||||
includeActiveNote: boolean;
|
||||
setIncludeActiveNote: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
activeNote: TFile | null;
|
||||
contextUrls: string[];
|
||||
onRemoveUrl: (url: string) => void;
|
||||
}
|
||||
|
||||
const ContextControl: React.FC<ChatControlsProps> = ({
|
||||
app,
|
||||
contextNotes,
|
||||
setContextNotes,
|
||||
includeActiveNote,
|
||||
setIncludeActiveNote,
|
||||
activeNote,
|
||||
contextUrls,
|
||||
onRemoveUrl,
|
||||
}) => {
|
||||
const [selectedChain] = useChainType();
|
||||
|
||||
const handleAddContext = () => {
|
||||
const excludeNotes = [
|
||||
...contextNotes.map((note) => note.path),
|
||||
...(includeActiveNote && activeNote ? [activeNote.path] : []),
|
||||
].filter(Boolean) as string[];
|
||||
|
||||
new AddContextNoteModal({
|
||||
app,
|
||||
onNoteSelect: (note) => {
|
||||
if (activeNote && note.path === activeNote.path) {
|
||||
setIncludeActiveNote(true);
|
||||
// Remove the note from contextNotes if it exists there
|
||||
setContextNotes((prev) => prev.filter((n) => n.path !== note.path));
|
||||
} else {
|
||||
// Add wasAddedManually flag to distinguish from reference-added notes
|
||||
setContextNotes((prev) => [...prev, Object.assign(note, { wasAddedManually: true })]);
|
||||
}
|
||||
},
|
||||
excludeNotes,
|
||||
}).open();
|
||||
};
|
||||
|
||||
const handleRemoveContext = (path: string) => {
|
||||
// First check if this note was added manually
|
||||
const noteToRemove = contextNotes.find((note) => note.path === path);
|
||||
const wasAddedManually = noteToRemove && (noteToRemove as any).wasAddedManually;
|
||||
|
||||
if (wasAddedManually) {
|
||||
// If it was added manually, just remove it from contextNotes
|
||||
setContextNotes((prev) => prev.filter((note) => note.path !== path));
|
||||
} else {
|
||||
// If it wasn't added manually, it could be either:
|
||||
// 1. The active note (controlled by includeActiveNote)
|
||||
// 2. A note added via [[reference]]
|
||||
// In either case, we should:
|
||||
setIncludeActiveNote(false); // Turn off includeActiveNote if this was the active note
|
||||
setContextNotes((prev) => prev.filter((note) => note.path !== path)); // Remove from contextNotes if it was there
|
||||
}
|
||||
};
|
||||
|
||||
if (selectedChain !== ChainType.COPILOT_PLUS_CHAIN) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ChatContextMenu
|
||||
activeNote={includeActiveNote ? activeNote : null}
|
||||
contextNotes={contextNotes}
|
||||
onAddContext={handleAddContext}
|
||||
onRemoveContext={handleRemoveContext}
|
||||
contextUrls={contextUrls}
|
||||
onRemoveUrl={onRemoveUrl}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContextControl;
|
||||
|
|
@ -21,6 +21,7 @@ import { Notice, TFile } from "obsidian";
|
|||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useActiveFile } from "@/hooks/useActiveFile";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
function useRelevantNotes(refresher: number) {
|
||||
const [relevantNotes, setRelevantNotes] = useState<RelevantNoteEntry[]>([]);
|
||||
|
|
@ -135,12 +136,9 @@ function RelevantNote({
|
|||
<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}
|
||||
>
|
||||
<Button variant="ghost2" size="icon" onClick={onAddToChat}>
|
||||
<PlusCircle className="size-4" />
|
||||
</button>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Add to Chat</TooltipContent>
|
||||
</Tooltip>
|
||||
|
|
@ -225,7 +223,7 @@ export const RelevantNotes = memo(
|
|||
)}
|
||||
>
|
||||
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
|
||||
<div className="flex justify-between items-center px-2 pb-2">
|
||||
<div className="flex justify-between items-center pl-1 pb-2">
|
||||
<div className="flex gap-2 items-center flex-1">
|
||||
<span className="font-semibold text-normal">Relevant Notes</span>
|
||||
<Tooltip>
|
||||
|
|
@ -246,34 +244,31 @@ export const RelevantNotes = memo(
|
|||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex gap-2 items-center">
|
||||
<div className="flex items-center">
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
className="size-6 p-0 !bg-transparent border-none !shadow-none hover:!bg-interactive-hover"
|
||||
onClick={refreshIndex}
|
||||
>
|
||||
<Button variant="ghost2" size="icon" onClick={refreshIndex}>
|
||||
<RefreshCcw className="size-4" />
|
||||
</button>
|
||||
</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>
|
||||
<Button variant="ghost2" size="icon">
|
||||
{isOpen ? <ChevronUp className="size-5" /> : <ChevronDown className="size-5" />}
|
||||
</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">
|
||||
<div className="flex flex-wrap gap-x-2 gap-y-1 max-h-12 overflow-y-hidden px-1">
|
||||
<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">
|
||||
<div className="flex flex-wrap gap-x-2 gap-y-1 max-h-6 overflow-y-hidden px-1">
|
||||
{relevantNotes.map((note) => (
|
||||
<RelevantNotePopover
|
||||
key={note.document.path}
|
||||
|
|
@ -295,7 +290,7 @@ export const RelevantNotes = memo(
|
|||
</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">
|
||||
<div className="px-1 py-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)}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useChainType } from "@/aiParams";
|
||||
import { ChainType } from "@/chainFactory";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardTitle, CardContent, CardHeader } from "@/components/ui/card";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { VAULT_VECTOR_STORE_STRATEGY } from "@/constants";
|
||||
|
|
@ -99,7 +100,7 @@ export const SuggestedPrompts: React.FC<SuggestedPromptsProps> = ({ onClick }) =
|
|||
<CardHeader className="px-2">
|
||||
<CardTitle>Suggested Prompts</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="p-2 px-2 pt-0">
|
||||
<CardContent className="p-2 pt-0">
|
||||
<div className="flex flex-col gap-2">
|
||||
{prompts.map((prompt, i) => (
|
||||
<div
|
||||
|
|
@ -110,17 +111,21 @@ export const SuggestedPrompts: React.FC<SuggestedPromptsProps> = ({ onClick }) =
|
|||
<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 className="flex items-start h-full">
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost2"
|
||||
size="fit"
|
||||
className="text-muted"
|
||||
onClick={() => onClick(prompt.text)}
|
||||
>
|
||||
<PlusCircle className="size-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Add to Chat</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
import React from "react";
|
||||
import * as Tooltip from "@radix-ui/react-tooltip";
|
||||
import { PropsWithChildren } from "react";
|
||||
|
||||
interface Props {
|
||||
onClick: () => void;
|
||||
Icon: React.ReactNode;
|
||||
}
|
||||
|
||||
export function TooltipActionButton({ onClick, Icon, children }: PropsWithChildren<Props>) {
|
||||
return (
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger asChild>
|
||||
<button className="chat-icon-button clickable-icon" onClick={onClick}>
|
||||
{Icon}
|
||||
</button>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Portal container={activeDocument.body}>
|
||||
<Tooltip.Content sideOffset={5} className="tooltip-text">
|
||||
{children}
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Portal>
|
||||
</Tooltip.Root>
|
||||
);
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority";
|
|||
import { cn } from "@/lib/utils";
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:shrink-0",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
|
@ -14,14 +14,17 @@ const buttonVariants = cva(
|
|||
outline:
|
||||
"border border-border bg-primary shadow-sm hover:bg-interactive-accent hover:text-on-accent",
|
||||
secondary: "bg-secondary text-normal shadow-sm hover:bg-secondary/80",
|
||||
ghost: "hover:bg-interactive-accent hover:text-on-accent",
|
||||
ghost: "clickable-icon bg-transparent hover:bg-interactive-accent hover:text-on-accent",
|
||||
link: "text-accent underline-offset-4 hover:underline",
|
||||
ghost2:
|
||||
"text-faint clickable-icon bg-transparent hover:bg-opacity-100 hover:text-on-accent hover:bg-transparent outline-none focus-visible:outline-none focus-visible:text-on-accent focus-visible:ring-0",
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2",
|
||||
sm: "h-8 rounded-md px-3 text-xs",
|
||||
lg: "h-10 rounded-md px-8",
|
||||
icon: "h-9 w-9",
|
||||
icon: "size-7",
|
||||
fit: "px-1 text-xs gap-1",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
|
|
|||
186
src/components/ui/dropdown-menu.tsx
Normal file
186
src/components/ui/dropdown-menu.tsx
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
import * as React from "react";
|
||||
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
||||
import { Check, ChevronRight, Circle } from "lucide-react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const DropdownMenu = DropdownMenuPrimitive.Root;
|
||||
|
||||
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
||||
|
||||
const DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
||||
|
||||
const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
||||
|
||||
const DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
||||
|
||||
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
||||
|
||||
const DropdownMenuSubTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
|
||||
inset?: boolean;
|
||||
}
|
||||
>(({ className, inset, children, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-dropdown-hover data-[state=open]:bg-dropdown-hover [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
||||
inset && "pl-8",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRight className="ml-auto" />
|
||||
</DropdownMenuPrimitive.SubTrigger>
|
||||
));
|
||||
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
|
||||
|
||||
const DropdownMenuSubContent = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-primary p-1 text-normal shadow-lg 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}
|
||||
/>
|
||||
));
|
||||
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
|
||||
|
||||
const DropdownMenuContent = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
|
||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Portal container={activeDocument.body}>
|
||||
<DropdownMenuPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-border border-solid bg-primary p-1 text-normal shadow-md",
|
||||
"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}
|
||||
/>
|
||||
</DropdownMenuPrimitive.Portal>
|
||||
));
|
||||
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
||||
|
||||
const DropdownMenuItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
|
||||
inset?: boolean;
|
||||
}
|
||||
>(({ className, inset, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-dropdown-hover focus:text-normal data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
|
||||
inset && "pl-8",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
||||
|
||||
const DropdownMenuCheckboxItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
|
||||
>(({ className, children, checked, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-dropdown-hover focus:text-normal data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className
|
||||
)}
|
||||
checked={checked}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<Check className="h-4 w-4" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.CheckboxItem>
|
||||
));
|
||||
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
|
||||
|
||||
const DropdownMenuRadioItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-dropdown-hover focus:text-normal data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<Circle className="h-2 w-2 fill-current" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.RadioItem>
|
||||
));
|
||||
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
||||
|
||||
const DropdownMenuLabel = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
|
||||
inset?: boolean;
|
||||
}
|
||||
>(({ className, inset, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Label
|
||||
ref={ref}
|
||||
className={cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
||||
|
||||
const DropdownMenuSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn("-mx-1 my-1 h-px bg-muted", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
||||
|
||||
const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
|
||||
return (
|
||||
<span className={cn("ml-auto text-xs tracking-widest opacity-60", className)} {...props} />
|
||||
);
|
||||
};
|
||||
DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
|
||||
|
||||
export {
|
||||
DropdownMenu,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuRadioGroup,
|
||||
};
|
||||
|
|
@ -100,14 +100,6 @@ If your plugin does not need CSS, delete this file.
|
|||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.chat-icons-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 0 0 8px;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.chat-context-menu {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -117,37 +109,6 @@ If your plugin does not need CSS, delete this file.
|
|||
flex: 1; /* Take up available space */
|
||||
}
|
||||
|
||||
.context-notes {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
flex-wrap: wrap;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.context-note {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 2px 6px;
|
||||
background: var(--background-secondary-alt);
|
||||
border-radius: 4px;
|
||||
font-size: 10px;
|
||||
height: 20px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.context-note.active {
|
||||
background: var(--background-modifier-hover);
|
||||
color: var(--text-normal);
|
||||
border-left: 2px solid var(--interactive-accent);
|
||||
}
|
||||
|
||||
.context-note.with-hover {
|
||||
/* Use the same background as active note but without border */
|
||||
background: var(--background-modifier-hover);
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.note-badge {
|
||||
font-size: 10px;
|
||||
padding: 0 4px;
|
||||
|
|
@ -269,54 +230,6 @@ If your plugin does not need CSS, delete this file.
|
|||
opacity: 1;
|
||||
}
|
||||
|
||||
.chat-input-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
background: var(--background-secondary-alt);
|
||||
border-radius: 4px;
|
||||
padding: 4px;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.chat-input-textarea {
|
||||
width: 100%;
|
||||
min-height: 44px;
|
||||
max-height: 440px;
|
||||
overflow-y: auto;
|
||||
resize: none;
|
||||
padding: 2px 12px;
|
||||
background: var(--background-secondary-alt);
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
color: var(--text-normal);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.chat-input-textarea::placeholder {
|
||||
color: var(--text-muted);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.chat-input-textarea:focus {
|
||||
outline: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.chat-input-controls {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.chat-input-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.selected-images {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
|
@ -379,11 +292,6 @@ If your plugin does not need CSS, delete this file.
|
|||
height: 18px;
|
||||
}
|
||||
|
||||
.chat-input-buttons {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.model-select-content {
|
||||
min-width: 120px;
|
||||
background: var(--background-secondary);
|
||||
|
|
@ -567,54 +475,6 @@ If your plugin does not need CSS, delete this file.
|
|||
border: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.message-buttons-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.message-timestamp {
|
||||
font-size: 0.7em;
|
||||
color: var(--text-faint, #bbb);
|
||||
opacity: 0.7;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.chat-message-buttons {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
height: 20px;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.chat-message-buttons button {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
padding: 0;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: transparent;
|
||||
opacity: 0.5;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: var(--text-muted);
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.chat-message-buttons button:hover {
|
||||
color: var(--text-normal);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.chat-message-buttons button svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.copilot-command-modal {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@ module.exports = {
|
|||
},
|
||||
dropdown: {
|
||||
DEFAULT: "var(--dropdown-background)",
|
||||
blend: "var(--dropdown-background-blend-mode)",
|
||||
hover: "var(--dropdown-background-hover)",
|
||||
},
|
||||
callout: {
|
||||
warning: "rgba(var(--callout-warning),<alpha-value>)",
|
||||
|
|
|
|||
Loading…
Reference in a new issue