mirror of
https://github.com/logancyang/obsidian-copilot.git
synced 2026-07-22 07:50:24 +00:00
Merge branch '3.1.0-preview' into memory-preview
This commit is contained in:
commit
61d423fd5f
31 changed files with 1716 additions and 911 deletions
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "copilot",
|
||||
"name": "Copilot",
|
||||
"version": "3.0.2",
|
||||
"version": "3.0.3",
|
||||
"minAppVersion": "0.15.0",
|
||||
"description": "Your AI Copilot: Chat with Your Second Brain, Learn Faster, Work Smarter.",
|
||||
"author": "Logan Yang",
|
||||
|
|
|
|||
584
package-lock.json
generated
584
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "obsidian-copilot",
|
||||
"version": "3.0.2",
|
||||
"version": "3.0.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "obsidian-copilot",
|
||||
"version": "3.0.2",
|
||||
"version": "3.0.3",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
|
|
@ -5506,18 +5506,18 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-dropdown-menu": {
|
||||
"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==",
|
||||
"version": "2.1.16",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz",
|
||||
"integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==",
|
||||
"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-menu": "2.1.4",
|
||||
"@radix-ui/react-primitive": "2.0.1",
|
||||
"@radix-ui/react-use-controllable-state": "1.1.0"
|
||||
"@radix-ui/primitive": "1.1.3",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@radix-ui/react-id": "1.1.1",
|
||||
"@radix-ui/react-menu": "2.1.16",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-use-controllable-state": "1.2.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
|
|
@ -5535,15 +5535,15 @@
|
|||
}
|
||||
},
|
||||
"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==",
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
|
||||
"integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
|
||||
"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==",
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
||||
"integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
|
|
@ -5555,13 +5555,46 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-context": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
|
||||
"integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
|
||||
"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-id": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz",
|
||||
"integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.1.1"
|
||||
"@radix-ui/react-use-layout-effect": "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-dropdown-menu/node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
|
|
@ -5578,13 +5611,14 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-use-controllable-state": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
|
||||
"integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.1"
|
||||
"@radix-ui/react-use-effect-event": "0.0.2",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
|
|
@ -5596,6 +5630,21 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-use-layout-effect": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
|
||||
"integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
|
||||
"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-focus-guards": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz",
|
||||
|
|
@ -5675,29 +5724,29 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-menu": {
|
||||
"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==",
|
||||
"version": "2.1.16",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz",
|
||||
"integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@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.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-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.1"
|
||||
"@radix-ui/primitive": "1.1.3",
|
||||
"@radix-ui/react-collection": "1.1.7",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@radix-ui/react-direction": "1.1.1",
|
||||
"@radix-ui/react-dismissable-layer": "1.1.11",
|
||||
"@radix-ui/react-focus-guards": "1.1.3",
|
||||
"@radix-ui/react-focus-scope": "1.1.7",
|
||||
"@radix-ui/react-id": "1.1.1",
|
||||
"@radix-ui/react-popper": "1.2.8",
|
||||
"@radix-ui/react-portal": "1.1.9",
|
||||
"@radix-ui/react-presence": "1.1.5",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-roving-focus": "1.1.11",
|
||||
"@radix-ui/react-slot": "1.2.3",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.1",
|
||||
"aria-hidden": "^1.2.4",
|
||||
"react-remove-scroll": "^2.6.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
|
|
@ -5715,18 +5764,18 @@
|
|||
}
|
||||
},
|
||||
"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==",
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
|
||||
"integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
|
||||
"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==",
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz",
|
||||
"integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-primitive": "2.0.1"
|
||||
"@radix-ui/react-primitive": "2.1.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
|
|
@ -5744,15 +5793,15 @@
|
|||
}
|
||||
},
|
||||
"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==",
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz",
|
||||
"integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==",
|
||||
"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"
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-slot": "1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
|
|
@ -5770,9 +5819,39 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-compose-refs": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
||||
"integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
|
||||
"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-context": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
|
||||
"integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
|
||||
"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-direction": {
|
||||
"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==",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
|
||||
"integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
|
|
@ -5785,16 +5864,16 @@
|
|||
}
|
||||
},
|
||||
"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==",
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz",
|
||||
"integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==",
|
||||
"license": "MIT",
|
||||
"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"
|
||||
"@radix-ui/primitive": "1.1.3",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.1",
|
||||
"@radix-ui/react-use-escape-keydown": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
|
|
@ -5811,15 +5890,30 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-focus-guards": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz",
|
||||
"integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==",
|
||||
"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-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==",
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz",
|
||||
"integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==",
|
||||
"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"
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
|
|
@ -5836,22 +5930,40 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-id": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz",
|
||||
"integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-layout-effect": "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/@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==",
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz",
|
||||
"integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==",
|
||||
"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"
|
||||
"@radix-ui/react-arrow": "1.1.7",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.1",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1",
|
||||
"@radix-ui/react-use-rect": "1.1.1",
|
||||
"@radix-ui/react-use-size": "1.1.1",
|
||||
"@radix-ui/rect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
|
|
@ -5869,13 +5981,37 @@
|
|||
}
|
||||
},
|
||||
"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==",
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
|
||||
"integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-primitive": "2.0.1",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-presence": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
|
||||
"integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
|
|
@ -5893,12 +6029,12 @@
|
|||
}
|
||||
},
|
||||
"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==",
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.1.1"
|
||||
"@radix-ui/react-slot": "1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
|
|
@ -5915,13 +6051,28 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-slot": {
|
||||
"node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-use-callback-ref": {
|
||||
"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==",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
|
||||
"integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==",
|
||||
"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-use-escape-keydown": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz",
|
||||
"integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.1"
|
||||
"@radix-ui/react-use-callback-ref": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
|
|
@ -5933,17 +6084,74 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-use-layout-effect": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
|
||||
"integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
|
||||
"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-use-rect": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz",
|
||||
"integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/rect": "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/@radix-ui/react-use-size": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz",
|
||||
"integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-layout-effect": "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/@radix-ui/rect": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
|
||||
"integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"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==",
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz",
|
||||
"integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react-remove-scroll-bar": "^2.3.7",
|
||||
"react-style-singleton": "^2.2.1",
|
||||
"react-style-singleton": "^2.2.3",
|
||||
"tslib": "^2.1.0",
|
||||
"use-callback-ref": "^1.3.3",
|
||||
"use-sidecar": "^1.1.2"
|
||||
"use-sidecar": "^1.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
|
|
@ -6423,20 +6631,20 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-roving-focus": {
|
||||
"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==",
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz",
|
||||
"integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@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.1",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.0",
|
||||
"@radix-ui/react-use-controllable-state": "1.1.0"
|
||||
"@radix-ui/primitive": "1.1.3",
|
||||
"@radix-ui/react-collection": "1.1.7",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@radix-ui/react-direction": "1.1.1",
|
||||
"@radix-ui/react-id": "1.1.1",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.1",
|
||||
"@radix-ui/react-use-controllable-state": "1.2.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
|
|
@ -6454,21 +6662,21 @@
|
|||
}
|
||||
},
|
||||
"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==",
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
|
||||
"integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
|
||||
"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==",
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz",
|
||||
"integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==",
|
||||
"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"
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-slot": "1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
|
|
@ -6486,9 +6694,9 @@
|
|||
}
|
||||
},
|
||||
"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==",
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
||||
"integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
|
|
@ -6500,13 +6708,61 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-context": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
|
||||
"integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
|
||||
"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-direction": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
|
||||
"integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==",
|
||||
"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-id": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz",
|
||||
"integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.1.1"
|
||||
"@radix-ui/react-use-layout-effect": "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-roving-focus/node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
|
|
@ -6523,13 +6779,29 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-slot": {
|
||||
"node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-use-callback-ref": {
|
||||
"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==",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
|
||||
"integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==",
|
||||
"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-use-controllable-state": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
|
||||
"integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.1"
|
||||
"@radix-ui/react-use-effect-event": "0.0.2",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
|
|
@ -6541,6 +6813,21 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-use-layout-effect": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
|
||||
"integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
|
||||
"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-scroll-area": {
|
||||
"version": "1.2.9",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.9.tgz",
|
||||
|
|
@ -21540,9 +21827,10 @@
|
|||
}
|
||||
},
|
||||
"node_modules/use-sidecar": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
|
||||
"integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==",
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
|
||||
"integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"detect-node-es": "^1.1.0",
|
||||
"tslib": "^2.0.0"
|
||||
|
|
@ -21551,8 +21839,8 @@
|
|||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^16.9.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": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "obsidian-copilot",
|
||||
"version": "3.0.2",
|
||||
"version": "3.0.3",
|
||||
"description": "Your AI Copilot: Chat with Your Second Brain, Learn Faster, Work Smarter.",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -96,7 +96,8 @@ More content
|
|||
const dashHeading = "Content\nSources -\n[^1]: [[Doc]]";
|
||||
expect(hasExistingCitations(dashHeading)).toBe(true);
|
||||
|
||||
const summary = "<details><summary>Sources</summary>List</details>";
|
||||
const summary =
|
||||
'<details><summary class="copilot-sources__summary">Sources</summary>List</details>';
|
||||
expect(hasExistingCitations(summary)).toBe(true);
|
||||
});
|
||||
|
||||
|
|
@ -188,9 +189,10 @@ More content
|
|||
[^2]: [[Document 2]]`;
|
||||
|
||||
const result = processInlineCitations(content, true);
|
||||
expect(result).toContain("Sources</summary>");
|
||||
expect(result).toContain("Document 1");
|
||||
expect(result).toContain("Document 2");
|
||||
expect(result).toContain("copilot-sources__summary");
|
||||
expect(result).toContain('copilot-sources__index">[1]');
|
||||
expect(result).toContain('copilot-sources__text">[[Document 1]]');
|
||||
expect(result).toContain('copilot-sources__text">[[Document 2]]');
|
||||
});
|
||||
|
||||
it("should use simple expandable list when disabled", () => {
|
||||
|
|
@ -201,9 +203,10 @@ More content
|
|||
- [[Document 2]]`;
|
||||
|
||||
const result = processInlineCitations(content, false);
|
||||
expect(result).toContain("Sources</summary>");
|
||||
expect(result).toContain("Document 1");
|
||||
expect(result).toContain("Document 2");
|
||||
expect(result).toContain("copilot-sources__summary");
|
||||
expect(result).toContain('copilot-sources__index">[1]');
|
||||
expect(result).toContain('copilot-sources__text">[[Document 1]]');
|
||||
expect(result).toContain('copilot-sources__text">[[Document 2]]');
|
||||
});
|
||||
|
||||
it("should return unchanged content when no sources section", () => {
|
||||
|
|
@ -350,10 +353,18 @@ More content
|
|||
// [^2] (third mention) -> [3] -> should map to [[2024-03-26]]
|
||||
// [^18] (fourth mention) -> [4] -> should map to [[2024-04-08]]
|
||||
|
||||
expect(result).toContain("<strong>[1]</strong> [[2024-03-13]]"); // [^9] -> [1]
|
||||
expect(result).toContain("<strong>[2]</strong> [[2024-03-18]]"); // [^1] -> [2]
|
||||
expect(result).toContain("<strong>[3]</strong> [[2024-03-26]]"); // [^2] -> [3]
|
||||
expect(result).toContain("<strong>[4]</strong> [[2024-04-08]]"); // [^18] -> [4]
|
||||
expect(result).toContain(
|
||||
'<span class="copilot-sources__index">[1]</span><span class="copilot-sources__text">[[2024-03-13]]</span>'
|
||||
);
|
||||
expect(result).toContain(
|
||||
'<span class="copilot-sources__index">[2]</span><span class="copilot-sources__text">[[2024-03-18]]</span>'
|
||||
);
|
||||
expect(result).toContain(
|
||||
'<span class="copilot-sources__index">[3]</span><span class="copilot-sources__text">[[2024-03-26]]</span>'
|
||||
);
|
||||
expect(result).toContain(
|
||||
'<span class="copilot-sources__index">[4]</span><span class="copilot-sources__text">[[2024-04-08]]</span>'
|
||||
);
|
||||
|
||||
// Verify the citations in text are renumbered correctly
|
||||
expect(result).toContain("Dolce Arts Studio [1]"); // [^9] -> [1]
|
||||
|
|
@ -380,10 +391,14 @@ More content
|
|||
const result = processInlineCitations(content, true);
|
||||
|
||||
// Should consolidate the 3 "How to Make Wealth" entries into 1
|
||||
expect(result).toContain("<strong>[1]</strong> [[How to Make Wealth]]");
|
||||
expect(result).toContain("<strong>[2]</strong> [[Superlinear Returns]]");
|
||||
expect(result).not.toContain("<strong>[3]</strong>");
|
||||
expect(result).not.toContain("<strong>[4]</strong>");
|
||||
expect(result).toContain(
|
||||
'<span class="copilot-sources__index">[1]</span><span class="copilot-sources__text">[[How to Make Wealth]]</span>'
|
||||
);
|
||||
expect(result).toContain(
|
||||
'<span class="copilot-sources__index">[2]</span><span class="copilot-sources__text">[[Superlinear Returns]]</span>'
|
||||
);
|
||||
expect(result).not.toContain('copilot-sources__index">[3]');
|
||||
expect(result).not.toContain('copilot-sources__index">[4]');
|
||||
|
||||
// Verify periods after citations are removed
|
||||
expect(result).not.toContain("[1].");
|
||||
|
|
@ -419,10 +434,16 @@ More content
|
|||
// [^22] (fourth mention) -> [4] -> [[Document B]] (should consolidate with [^14])
|
||||
|
||||
// After consolidation, should only have 3 unique sources:
|
||||
expect(result).toContain("<strong>[1]</strong> [[Document B]]"); // [^14] and [^22] consolidated
|
||||
expect(result).toContain("<strong>[2]</strong> [[Document C]]"); // [^17]
|
||||
expect(result).toContain("<strong>[3]</strong> [[Document A]]"); // [^3]
|
||||
expect(result).not.toContain("<strong>[4]</strong>");
|
||||
expect(result).toContain(
|
||||
'<span class="copilot-sources__index">[1]</span><span class="copilot-sources__text">[[Document B]]</span>'
|
||||
);
|
||||
expect(result).toContain(
|
||||
'<span class="copilot-sources__index">[2]</span><span class="copilot-sources__text">[[Document C]]</span>'
|
||||
);
|
||||
expect(result).toContain(
|
||||
'<span class="copilot-sources__index">[3]</span><span class="copilot-sources__text">[[Document A]]</span>'
|
||||
);
|
||||
expect(result).not.toContain('copilot-sources__index">[4]');
|
||||
|
||||
// Verify citations in text point to correct consolidated sources
|
||||
expect(result).toContain("references [1] and also [2]"); // [^14]->[1], [^17]->[2]
|
||||
|
|
@ -447,8 +468,12 @@ More content
|
|||
// [^7] (first mention) -> [1] -> [[Superlinear Returns]]
|
||||
// [^8] (second mention) -> [2] -> [[How to Do Great Work]]
|
||||
|
||||
expect(result).toContain("<strong>[1]</strong> [[Superlinear Returns]]"); // [^7] -> [1]
|
||||
expect(result).toContain("<strong>[2]</strong> [[How to Do Great Work]]"); // [^8] -> [2]
|
||||
expect(result).toContain(
|
||||
'<span class="copilot-sources__index">[1]</span><span class="copilot-sources__text">[[Superlinear Returns]]</span>'
|
||||
);
|
||||
expect(result).toContain(
|
||||
'<span class="copilot-sources__index">[2]</span><span class="copilot-sources__text">[[How to Do Great Work]]</span>'
|
||||
);
|
||||
|
||||
// CRITICAL: Both citations in text must be converted (not partial like [4][^8])
|
||||
expect(result).toContain("thresholds). [1][2]"); // [^7][^8] -> [1][2]
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ export function hasExistingCitations(response: string): boolean {
|
|||
const content = response || "";
|
||||
const hasMarkdownHeading = /(^|\n)\s*#{1,6}\s*Sources\b/i.test(content);
|
||||
const hasPlainLabel = /(^|\n)\s*Sources\s*(?:[:-]\s*)?(\n|$)/i.test(content);
|
||||
const hasSummaryTag = /<summary>\s*Sources\s*<\/summary>/i.test(content);
|
||||
const hasSummaryTag = /<summary[^>]*>\s*Sources\s*<\/summary>/i.test(content);
|
||||
// More robust detection: look for ANY line starting with [^digits]:
|
||||
const hasFootnoteDefinitions = /(^|\n)\s*\[\^\d+\]:\s*/.test(content);
|
||||
return hasMarkdownHeading || hasPlainLabel || hasSummaryTag || hasFootnoteDefinitions;
|
||||
|
|
@ -403,6 +403,93 @@ export function updateCitationsForConsolidation(
|
|||
});
|
||||
}
|
||||
|
||||
interface SourcesDisplayItem {
|
||||
index: number;
|
||||
html: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes HTML-sensitive characters to avoid unintended markup injection.
|
||||
*/
|
||||
function escapeHtml(value: string): string {
|
||||
return value
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard that filters out nullish values when mapping optional entries.
|
||||
*/
|
||||
function isNonNull<T>(value: T | null | undefined): value is T {
|
||||
return value !== null && value !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a normalized sources block into display-ready list items.
|
||||
*/
|
||||
function parseSimpleSources(sourcesBlock: string): SourcesDisplayItem[] {
|
||||
const lines = sourcesBlock.split("\n");
|
||||
const items: SourcesDisplayItem[] = [];
|
||||
|
||||
lines.forEach((rawLine) => {
|
||||
const trimmed = rawLine.trim();
|
||||
if (!trimmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const content = trimmed
|
||||
.replace(/^<li>/i, "")
|
||||
.replace(/<\/li>$/i, "")
|
||||
.replace(/^[-*]\s*/, "")
|
||||
.trim();
|
||||
|
||||
if (!content) {
|
||||
return;
|
||||
}
|
||||
|
||||
const markdownLink = content.match(/\[([^\]]+)\]\(([^)]+)\)/);
|
||||
const wikiLink = content.match(/\[\[(.*?)\]\]/);
|
||||
|
||||
let html: string;
|
||||
if (markdownLink) {
|
||||
html = `<a href="${markdownLink[2]}">${markdownLink[1]}</a>`;
|
||||
} else if (wikiLink) {
|
||||
html = `[[${wikiLink[1]}]]`;
|
||||
} else {
|
||||
html = escapeHtml(content);
|
||||
}
|
||||
|
||||
items.push({ index: items.length + 1, html });
|
||||
});
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a styled, collapsible sources list to the main message content.
|
||||
*/
|
||||
function buildSourcesDetails(mainContent: string, items: SourcesDisplayItem[]): string {
|
||||
if (items.length === 0) {
|
||||
return mainContent;
|
||||
}
|
||||
|
||||
const listItems = items
|
||||
.map(
|
||||
({ index, html }) =>
|
||||
`<li class="copilot-sources__item"><span class="copilot-sources__index">[${index}]</span><span class="copilot-sources__text">${html}</span></li>`
|
||||
)
|
||||
.join("\n");
|
||||
|
||||
return (
|
||||
`${mainContent}\n\n<br/>\n` +
|
||||
`<details class="copilot-sources"><summary class="copilot-sources__summary">Sources</summary>\n` +
|
||||
`<ul class="copilot-sources__list">\n${listItems}\n</ul>\n</details>`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function to process inline citations in content.
|
||||
* Uses settings to determine if inline citations should be processed.
|
||||
|
|
@ -416,46 +503,15 @@ export function processInlineCitations(content: string, useInlineCitations: bool
|
|||
|
||||
// If inline citations are disabled, use simple expandable sources list
|
||||
if (!useInlineCitations) {
|
||||
const sourceLinks = sourcesBlock
|
||||
.split("\n")
|
||||
.map((line) => {
|
||||
const match = line.match(/- \[\[(.*?)\]\]/);
|
||||
if (match) {
|
||||
return `<li>[[${match[1]}]]</li>`;
|
||||
}
|
||||
return line;
|
||||
})
|
||||
.join("\n");
|
||||
|
||||
return (
|
||||
mainContent +
|
||||
"\n\n<br/>\n<details><summary>Sources</summary>\n<ul>\n" +
|
||||
sourceLinks +
|
||||
"\n</ul>\n</details>"
|
||||
);
|
||||
const simpleItems = parseSimpleSources(sourcesBlock);
|
||||
return buildSourcesDetails(mainContent, simpleItems);
|
||||
}
|
||||
|
||||
// Process inline citations
|
||||
const footnoteLines = parseFootnoteDefinitions(sourcesBlock);
|
||||
if (footnoteLines.length === 0) {
|
||||
// Not footnote format, use simple sources list
|
||||
const sourceLinks = sourcesBlock
|
||||
.split("\n")
|
||||
.map((line) => {
|
||||
const match = line.match(/- \[\[(.*?)\]\]/);
|
||||
if (match) {
|
||||
return `<li>[[${match[1]}]]</li>`;
|
||||
}
|
||||
return line;
|
||||
})
|
||||
.join("\n");
|
||||
|
||||
return (
|
||||
mainContent +
|
||||
"\n\n<br/>\n<details><summary>Sources</summary>\n<ul>\n" +
|
||||
sourceLinks +
|
||||
"\n</ul>\n</details>"
|
||||
);
|
||||
const simpleItems = parseSimpleSources(sourcesBlock);
|
||||
return buildSourcesDetails(mainContent, simpleItems);
|
||||
}
|
||||
|
||||
// Process footnote-style citations
|
||||
|
|
@ -471,31 +527,16 @@ export function processInlineCitations(content: string, useInlineCitations: bool
|
|||
items = uniqueItems;
|
||||
}
|
||||
|
||||
// Build sources list maintaining proper citation mapping
|
||||
// The old code used .filter().map() which destroyed the mapping by renumbering
|
||||
// Now we build the list respecting the citation numbers used in the text
|
||||
const maxCitationNum = Math.max(
|
||||
...Array.from(citationMap.values()).concat(
|
||||
consolidationMap.size > 0 ? Array.from(consolidationMap.values()) : []
|
||||
)
|
||||
);
|
||||
const detailedItems = items
|
||||
.map<SourcesDisplayItem | null>((item, index) => {
|
||||
if (!item) {
|
||||
return null;
|
||||
}
|
||||
return { index: index + 1, html: item };
|
||||
})
|
||||
.filter(isNonNull);
|
||||
|
||||
const sourcesList: string[] = [];
|
||||
for (let i = 1; i <= maxCitationNum; i++) {
|
||||
const item = items[i - 1];
|
||||
if (item) {
|
||||
sourcesList.push(`<li><strong>[${i}]</strong> ${item}</li>`);
|
||||
}
|
||||
}
|
||||
|
||||
const formattedSourcesList = sourcesList.join("\n");
|
||||
|
||||
return (
|
||||
mainContent +
|
||||
"\n\n<br/>\n<details><summary>Sources</summary>\n<ul>\n" +
|
||||
formattedSourcesList +
|
||||
"\n</ul>\n</details>"
|
||||
);
|
||||
return buildSourcesDetails(mainContent, detailedItems);
|
||||
}
|
||||
|
||||
// ===== SOURCE CATALOG UTILITIES =====
|
||||
|
|
|
|||
|
|
@ -561,9 +561,8 @@ const Chat: React.FC<ChatProps> = ({
|
|||
safeSet.setCurrentAiMessage("");
|
||||
setContextNotes([]);
|
||||
clearSelectedTextContexts();
|
||||
// Only modify includeActiveNote if in a non-COPILOT_PLUS_CHAIN mode
|
||||
// In COPILOT_PLUS_CHAIN mode, respect the settings.includeActiveNoteAsContext value
|
||||
if (selectedChain !== ChainType.COPILOT_PLUS_CHAIN) {
|
||||
// Respect the includeActiveNote setting for all non-project chains
|
||||
if (selectedChain === ChainType.PROJECT_CHAIN) {
|
||||
setIncludeActiveNote(false);
|
||||
} else {
|
||||
setIncludeActiveNote(settings.includeActiveNoteAsContext);
|
||||
|
|
@ -604,11 +603,10 @@ const Chat: React.FC<ChatProps> = ({
|
|||
useEffect(() => {
|
||||
if (settings.includeActiveNoteAsContext !== undefined) {
|
||||
// Only apply the setting if not in Project mode
|
||||
if (selectedChain === ChainType.COPILOT_PLUS_CHAIN) {
|
||||
setIncludeActiveNote(settings.includeActiveNoteAsContext);
|
||||
} else {
|
||||
// In other modes, always disable including active note
|
||||
if (selectedChain === ChainType.PROJECT_CHAIN) {
|
||||
setIncludeActiveNote(false);
|
||||
} else {
|
||||
setIncludeActiveNote(settings.includeActiveNoteAsContext);
|
||||
}
|
||||
}
|
||||
}, [settings.includeActiveNoteAsContext, selectedChain]);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { ChatButtons } from "@/components/chat-components/ChatButtons";
|
|||
import { ToolCallBanner } from "@/components/chat-components/ToolCallBanner";
|
||||
import { SourcesModal } from "@/components/modals/SourcesModal";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { HelpTooltip } from "@/components/ui/help-tooltip";
|
||||
import { USER_SENDER } from "@/constants";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { parseToolCallMarkers } from "@/LLMProviders/chainRunner/utils/toolCallParser";
|
||||
|
|
@ -10,7 +10,6 @@ import { processInlineCitations } from "@/LLMProviders/chainRunner/utils/citatio
|
|||
import { useSettingsValue } from "@/settings/model";
|
||||
import { ChatMessage } from "@/types/message";
|
||||
import { cleanMessageForCopy, insertIntoEditor } from "@/utils";
|
||||
import { Bot, User } from "lucide-react";
|
||||
import { App, Component, MarkdownRenderer, MarkdownView, TFile } from "obsidian";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import ReactDOM, { Root } from "react-dom/client";
|
||||
|
|
@ -66,24 +65,18 @@ function MessageContext({ context }: { context: ChatMessage["context"] }) {
|
|||
return (
|
||||
<div className="tw-flex tw-flex-wrap tw-gap-2">
|
||||
{context.notes.map((note, index) => (
|
||||
<Tooltip key={`${index}-${note.path}`}>
|
||||
<TooltipTrigger asChild>
|
||||
<Badge variant="secondary">
|
||||
<span className="tw-max-w-40 tw-truncate">{note.basename}</span>
|
||||
</Badge>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{note.path}</TooltipContent>
|
||||
</Tooltip>
|
||||
<HelpTooltip key={`${index}-${note.path}`} content={note.path} side="top">
|
||||
<Badge variant="secondary">
|
||||
<span className="tw-max-w-40 tw-truncate">{note.basename}</span>
|
||||
</Badge>
|
||||
</HelpTooltip>
|
||||
))}
|
||||
{context.urls.map((url, index) => (
|
||||
<Tooltip key={`${index}-${url}`}>
|
||||
<TooltipTrigger asChild>
|
||||
<Badge variant="secondary">
|
||||
<span className="tw-max-w-40 tw-truncate">{url}</span>
|
||||
</Badge>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{url}</TooltipContent>
|
||||
</Tooltip>
|
||||
<HelpTooltip key={`${index}-${url}`} content={url} side="top">
|
||||
<Badge variant="secondary">
|
||||
<span className="tw-max-w-40 tw-truncate">{url}</span>
|
||||
</Badge>
|
||||
</HelpTooltip>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
|
@ -644,14 +637,16 @@ const ChatSingleMessage: React.FC<ChatSingleMessageProps> = ({
|
|||
<div className="tw-my-1 tw-flex tw-w-full tw-flex-col">
|
||||
<div
|
||||
className={cn(
|
||||
"tw-group tw-mx-2 tw-flex tw-gap-2 tw-rounded-md tw-p-2",
|
||||
"tw-group tw-mx-2 tw-rounded-md tw-p-2",
|
||||
message.sender === USER_SENDER && "tw-border tw-border-solid tw-border-border"
|
||||
)}
|
||||
style={
|
||||
message.sender === USER_SENDER
|
||||
? { backgroundColor: "var(--background-modifier-hover)" }
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<div className="tw-w-6 tw-shrink-0">
|
||||
{message.sender === USER_SENDER ? <User /> : <Bot />}
|
||||
</div>
|
||||
<div className="tw-flex tw-max-w-full tw-grow tw-flex-col tw-gap-2 tw-overflow-hidden">
|
||||
<div className="tw-flex tw-max-w-full tw-flex-col tw-gap-2 tw-overflow-hidden">
|
||||
{!isEditing && <MessageContext context={message.context} />}
|
||||
<div className="message-content">{renderMessageContent()}</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {
|
|||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { HelpTooltip } from "@/components/ui/help-tooltip";
|
||||
import { SearchBar } from "@/components/ui/SearchBar";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { logError } from "@/logger";
|
||||
|
|
@ -20,7 +20,6 @@ import {
|
|||
ChevronUp,
|
||||
Edit2,
|
||||
Folder,
|
||||
Info,
|
||||
MessageSquare,
|
||||
Plus,
|
||||
Search,
|
||||
|
|
@ -30,6 +29,7 @@ import {
|
|||
import { App, Notice } from "obsidian";
|
||||
import React, { memo, useEffect, useMemo, useState } from "react";
|
||||
import { filterProjects } from "@/utils/projectUtils";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
|
||||
function ProjectItem({
|
||||
project,
|
||||
|
|
@ -236,7 +236,7 @@ export const ProjectList = memo(
|
|||
<div className="tw-flex tw-flex-col">
|
||||
{showChatInput && selectedProject ? (
|
||||
<div className="tw-flex tw-items-center tw-justify-between tw-px-2 tw-py-3">
|
||||
<div className="tw-flex tw-items-center tw-gap-2">
|
||||
<div className="tw-flex tw-min-w-0 tw-items-center tw-gap-2">
|
||||
<span className="tw-font-semibold tw-text-normal">Projects</span>
|
||||
<Select
|
||||
value={selectedProject.name}
|
||||
|
|
@ -247,31 +247,31 @@ export const ProjectList = memo(
|
|||
}
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="tw-w-[200px]">
|
||||
<SelectTrigger className="tw-truncate">
|
||||
<SelectValue>
|
||||
<div className="tw-flex tw-items-center tw-gap-2">
|
||||
<Folder className="tw-size-4 tw-text-accent/70" />
|
||||
<span>{selectedProject.name}</span>
|
||||
<div className="tw-flex tw-min-w-0 tw-items-center tw-gap-2">
|
||||
<Folder className="tw-size-4 tw-shrink-0 tw-text-accent/70" />
|
||||
<span className="tw-flex-1 tw-truncate">{selectedProject.name}</span>
|
||||
</div>
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectContent className="tw-truncate">
|
||||
{projects.map((project) => (
|
||||
<SelectItem
|
||||
key={project.name}
|
||||
value={project.name}
|
||||
className="tw-flex tw-items-center tw-gap-2"
|
||||
>
|
||||
<div className="tw-flex tw-items-center tw-gap-2">
|
||||
<Folder className="tw-size-4" />
|
||||
{project.name}
|
||||
<div className="tw-flex tw-min-w-0 tw-items-center tw-gap-2">
|
||||
<Folder className="tw-size-4 tw-shrink-0" />
|
||||
<span className="tw-truncate">{project.name}</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="tw-flex tw-items-center tw-gap-2">
|
||||
<div className="tw-ml-1 tw-flex tw-items-center tw-gap-2">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => handleEditProject(selectedProject)}
|
||||
|
|
@ -307,14 +307,11 @@ export const ProjectList = memo(
|
|||
<div className="tw-flex tw-items-center tw-justify-between tw-px-4 tw-py-3">
|
||||
<div className="tw-flex tw-flex-1 tw-items-center tw-gap-2">
|
||||
<span className="tw-font-semibold tw-text-normal">Projects</span>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Info className="tw-size-4 tw-text-muted" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" className="tw-w-64">
|
||||
Manage your projects with different contexts and configurations.
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<HelpTooltip
|
||||
content="Manage your projects with different contexts and configurations."
|
||||
contentClassName="tw-w-64"
|
||||
buttonClassName="tw-size-4 tw-text-muted"
|
||||
/>
|
||||
</div>
|
||||
<div className="tw-flex tw-items-center tw-gap-2">
|
||||
<Button className="tw-px-2" variant="secondary" onClick={handleAddProject}>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { Badge } from "@/components/ui/badge";
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { HelpTooltip } from "@/components/ui/help-tooltip";
|
||||
import { useActiveFile } from "@/hooks/useActiveFile";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
|
|
@ -17,13 +17,13 @@ import {
|
|||
ChevronUp,
|
||||
FileInput,
|
||||
FileOutput,
|
||||
Info,
|
||||
PlusCircle,
|
||||
RefreshCcw,
|
||||
TriangleAlert,
|
||||
} from "lucide-react";
|
||||
import { Notice, TFile } from "obsidian";
|
||||
import React, { memo, useCallback, useEffect, useState } from "react";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
|
||||
function useRelevantNotes(refresher: number) {
|
||||
const [relevantNotes, setRelevantNotes] = useState<RelevantNoteEntry[]>([]);
|
||||
|
|
@ -47,6 +47,7 @@ function useRelevantNotes(refresher: number) {
|
|||
setRelevantNotes([]);
|
||||
}
|
||||
}
|
||||
|
||||
fetchNotes();
|
||||
}, [activeFile?.path, refresher]);
|
||||
|
||||
|
|
@ -57,6 +58,7 @@ function useHasIndex(notePath: string, refresher: number) {
|
|||
const [hasIndex, setHasIndex] = useState(true);
|
||||
useEffect(() => {
|
||||
if (!notePath) return;
|
||||
|
||||
async function fetchHasIndex() {
|
||||
try {
|
||||
const VectorStoreManager = (await import("@/search/vectorStoreManager")).default;
|
||||
|
|
@ -66,6 +68,7 @@ function useHasIndex(notePath: string, refresher: number) {
|
|||
setHasIndex(false);
|
||||
}
|
||||
}
|
||||
|
||||
fetchHasIndex();
|
||||
}, [notePath, refresher]);
|
||||
return hasIndex;
|
||||
|
|
@ -290,22 +293,16 @@ export const RelevantNotes = memo(
|
|||
<div className="tw-flex tw-items-center tw-justify-between tw-pb-2 tw-pl-1">
|
||||
<div className="tw-flex tw-flex-1 tw-items-center tw-gap-2">
|
||||
<span className="tw-font-semibold tw-text-normal">Relevant Notes</span>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Info className="tw-size-4 tw-text-muted" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" className="tw-w-64">
|
||||
Relevance is a combination of semantic similarity and links.
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<HelpTooltip
|
||||
content="Relevance is a combination of semantic similarity and links."
|
||||
contentClassName="tw-w-64"
|
||||
buttonClassName="tw-size-4 tw-text-muted"
|
||||
/>
|
||||
|
||||
{!hasIndex && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<TriangleAlert className="tw-size-4 tw-text-warning" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">Note has not been indexed</TooltipContent>
|
||||
</Tooltip>
|
||||
<HelpTooltip content="Note has not been indexed" side="bottom">
|
||||
<TriangleAlert className="tw-size-4 tw-text-warning" />
|
||||
</HelpTooltip>
|
||||
)}
|
||||
</div>
|
||||
<div className="tw-flex tw-items-center">
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
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 { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { VAULT_VECTOR_STORE_STRATEGY } from "@/constants";
|
||||
import { useSettingsValue } from "@/settings/model";
|
||||
import { PlusCircle, TriangleAlert } from "lucide-react";
|
||||
import React, { useMemo } from "react";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
|
||||
interface NotePrompt {
|
||||
title: string;
|
||||
prompts: string[];
|
||||
}
|
||||
|
||||
const SUGGESTED_PROMPTS: Record<string, NotePrompt> = {
|
||||
activeNote: {
|
||||
title: "Active Note Insights",
|
||||
|
|
|
|||
|
|
@ -8,15 +8,14 @@ import { getModelDisplayWithIcons } from "@/components/ui/model-display";
|
|||
import { ObsidianNativeSelect } from "@/components/ui/obsidian-native-select";
|
||||
import { SettingSlider } from "@/components/ui/setting-slider";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { DEFAULT_MODEL_SETTING } from "@/constants";
|
||||
import { getDecodedPatterns } from "@/search/searchUtils";
|
||||
import { getModelKeyFromModel, useSettingsValue } from "@/settings/model";
|
||||
import { checkModelApiKey, err2String, randomUUID } from "@/utils";
|
||||
import { HelpCircle } from "lucide-react";
|
||||
import { App, Modal, Notice } from "obsidian";
|
||||
import React, { useState } from "react";
|
||||
import { createRoot, Root } from "react-dom/client";
|
||||
import { HelpTooltip } from "@/components/ui/help-tooltip";
|
||||
|
||||
interface AddProjectModalContentProps {
|
||||
initialProject?: ProjectConfig;
|
||||
|
|
@ -259,31 +258,27 @@ function AddProjectModalContent({ initialProject, onSave, onCancel }: AddProject
|
|||
label={
|
||||
<div className="tw-flex tw-items-center tw-gap-2">
|
||||
<span>File Context</span>
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="tw-size-4 tw-text-muted" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<div className="tw-max-w-80">
|
||||
<strong>Supported File Types:</strong>
|
||||
<br />
|
||||
<strong>• Documents:</strong> pdf, doc, docx, ppt, pptx, epub, txt, rtf and
|
||||
many more
|
||||
<br />
|
||||
<strong>• Images:</strong> jpg, png, svg, gif, bmp, webp, tiff
|
||||
<br />
|
||||
<strong>• Spreadsheets:</strong> xlsx, xls, csv, numbers
|
||||
<br />
|
||||
<br />
|
||||
Non-markdown files are converted to markdown in the background.
|
||||
<br />
|
||||
<strong>Rate limit:</strong> 50 files or 100MB per 3 hours, whichever is
|
||||
reached first.
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
<HelpTooltip
|
||||
buttonClassName="tw-size-4 tw-text-muted"
|
||||
content={
|
||||
<div className="tw-max-w-80">
|
||||
<strong>Supported File Types:</strong>
|
||||
<br />
|
||||
<strong>• Documents:</strong> pdf, doc, docx, ppt, pptx, epub, txt, rtf and
|
||||
many more
|
||||
<br />
|
||||
<strong>• Images:</strong> jpg, png, svg, gif, bmp, webp, tiff
|
||||
<br />
|
||||
<strong>• Spreadsheets:</strong> xlsx, xls, csv, numbers
|
||||
<br />
|
||||
<br />
|
||||
Non-markdown files are converted to markdown in the background.
|
||||
<br />
|
||||
<strong>Rate limit:</strong> 50 files or 100MB per 3 hours, whichever is
|
||||
reached first.
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
description="Define patterns to include specific files, folders or tags (specified in the note property) in the project context."
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/componen
|
|||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { SearchBar } from "@/components/ui/SearchBar";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
createPatternSettingsValue,
|
||||
|
|
@ -25,15 +24,15 @@ import {
|
|||
FileText,
|
||||
FileVideo,
|
||||
FolderIcon,
|
||||
HelpCircle,
|
||||
Plus,
|
||||
PlusCircle,
|
||||
TagIcon,
|
||||
XIcon,
|
||||
} from "lucide-react";
|
||||
import { App, Modal, Notice, TFile } from "obsidian";
|
||||
import { App, Modal, Notice, Platform, TFile } from "obsidian";
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { createRoot, Root } from "react-dom/client";
|
||||
import { HelpTooltip } from "@/components/ui/help-tooltip";
|
||||
|
||||
function FileIcon({ extension, size = "tw-size-4" }: { extension: string; size?: string }) {
|
||||
const ext = extension.toLowerCase().replace("*.", "");
|
||||
|
|
@ -78,18 +77,13 @@ const SectionHeader: React.FC<SectionHeaderProps> = ({
|
|||
<IconComponent className={`tw-mr-2 tw-size-4 ${iconColorClassName}`} />
|
||||
<h3 className={`tw-text-sm tw-font-semibold ${iconColorClassName}`}>{title}</h3>
|
||||
{tooltip && (
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="tw-ml-2 tw-size-4 tw-text-muted" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<div className="tw-max-w-80">{tooltip}</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
<HelpTooltip
|
||||
buttonClassName="tw-ml-2 tw-size-4 tw-text-muted"
|
||||
content={<div className="tw-max-w-80">{tooltip}</div>}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="fit"
|
||||
|
|
@ -309,6 +303,7 @@ function isCategoryItem(item: DisplayItem): item is CategoryItem {
|
|||
}
|
||||
|
||||
function ContextManage({ initialProject, onSave, onCancel, app }: ContextManageProps) {
|
||||
const isMobile = Platform.isMobile;
|
||||
const { inclusions: inclusionPatterns, exclusions: exclusionPatterns } = useMemo(() => {
|
||||
return getMatchingPatterns({
|
||||
inclusions: initialProject?.contextSource.inclusions,
|
||||
|
|
@ -997,7 +992,7 @@ function ContextManage({ initialProject, onSave, onCancel, app }: ContextManageP
|
|||
<div className="tw-flex tw-h-full tw-flex-col">
|
||||
<ResizablePanelGroup direction="horizontal" className="tw-flex-1">
|
||||
{/* Left Sidebar - Navigation */}
|
||||
<ResizablePanel defaultSize={30} minSize={20} maxSize={40}>
|
||||
<ResizablePanel defaultSize={isMobile ? 35 : 30} minSize={20} maxSize={40}>
|
||||
<div className="tw-flex tw-h-full tw-flex-col">
|
||||
{/* Header */}
|
||||
<div className="tw-border-b tw-p-4">
|
||||
|
|
@ -1094,7 +1089,7 @@ function ContextManage({ initialProject, onSave, onCancel, app }: ContextManageP
|
|||
)}
|
||||
onClick={groupHandlers.click.ignoreFiles}
|
||||
>
|
||||
Ignore Files ({ignoreItems.files.size})
|
||||
Files ({ignoreItems.files.size})
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1105,7 +1100,7 @@ function ContextManage({ initialProject, onSave, onCancel, app }: ContextManageP
|
|||
<ResizableHandle withHandle />
|
||||
|
||||
{/* Right Content Area */}
|
||||
<ResizablePanel defaultSize={70}>
|
||||
<ResizablePanel defaultSize={isMobile ? 65 : 70}>
|
||||
<div className="tw-flex tw-h-full tw-flex-col">
|
||||
{/* Header */}
|
||||
<div className="tw-border-b tw-p-4">
|
||||
|
|
|
|||
91
src/components/ui/help-tooltip.tsx
Normal file
91
src/components/ui/help-tooltip.tsx
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { HelpCircle } from "lucide-react";
|
||||
import React, { useState } from "react";
|
||||
import { Platform } from "obsidian";
|
||||
|
||||
interface TooltipProps {
|
||||
content: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
side?: "top" | "right" | "bottom" | "left";
|
||||
delayDuration?: number;
|
||||
contentClassName?: string;
|
||||
buttonClassName?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A versatile tooltip component that works on both desktop and mobile devices.
|
||||
* On desktop, it shows on hover. On mobile, it shows on click.
|
||||
*
|
||||
* Can be used in two modes:
|
||||
* 1. Help mode (default): Shows a help icon with tooltip
|
||||
* 2. Wrapper mode (with children): Wraps any component with tooltip functionality
|
||||
*/
|
||||
export const HelpTooltip: React.FC<TooltipProps> = ({
|
||||
content,
|
||||
children,
|
||||
side = "bottom",
|
||||
delayDuration = 0,
|
||||
contentClassName,
|
||||
buttonClassName,
|
||||
}) => {
|
||||
const isMobile = Platform.isMobile;
|
||||
const [showTooltip, setShowTooltip] = useState(false);
|
||||
const isClickingRef = React.useRef(false);
|
||||
|
||||
const handleTouchStart = () => {
|
||||
if (isMobile) {
|
||||
isClickingRef.current = true;
|
||||
}
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
if (isMobile) {
|
||||
setShowTooltip(!showTooltip);
|
||||
// Reset the flag after a brief delay
|
||||
setTimeout(() => {
|
||||
isClickingRef.current = false;
|
||||
}, 100);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<TooltipProvider delayDuration={delayDuration}>
|
||||
<Tooltip
|
||||
open={showTooltip}
|
||||
onOpenChange={(open) => {
|
||||
// Ignore onOpenChange events on mobile when we're handling a click
|
||||
if (isMobile && isClickingRef.current) {
|
||||
return;
|
||||
}
|
||||
setShowTooltip(open);
|
||||
}}
|
||||
>
|
||||
<TooltipTrigger asChild>
|
||||
{children ? (
|
||||
<div
|
||||
onClick={handleClick}
|
||||
onTouchStart={handleTouchStart}
|
||||
className="tw-cursor-pointer"
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
) : (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={handleClick}
|
||||
onTouchStart={handleTouchStart}
|
||||
className={`tw-inline-flex tw-size-6 tw-items-center tw-justify-center tw-p-0 hover:tw-bg-transparent hover:tw-text-normal ${buttonClassName || ""}`}
|
||||
>
|
||||
<HelpCircle className="tw-size-4" />
|
||||
</Button>
|
||||
)}
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side={side} className={contentClassName}>
|
||||
{content}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
);
|
||||
};
|
||||
211
src/components/ui/mobile-card.tsx
Normal file
211
src/components/ui/mobile-card.tsx
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
import React, { useState } from "react";
|
||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useSortable } from "@dnd-kit/sortable";
|
||||
import { CSS } from "@dnd-kit/utilities";
|
||||
import { ChevronDown, ChevronRight, GripVertical, MoreVertical } from "lucide-react";
|
||||
|
||||
export interface MobileCardDropdownAction<T = any> {
|
||||
icon: React.ReactNode;
|
||||
label: string;
|
||||
onClick: (item: T) => void;
|
||||
variant?: "default" | "destructive";
|
||||
}
|
||||
|
||||
export interface MobileCardProps<T extends object> {
|
||||
id: string;
|
||||
item: T;
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
badge?: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
isDraggable?: boolean;
|
||||
isExpandable?: boolean;
|
||||
defaultExpanded?: boolean;
|
||||
expandedContent?: React.ReactNode;
|
||||
primaryAction?: {
|
||||
icon: React.ReactNode;
|
||||
onClick: (item: T) => void;
|
||||
tooltip?: string;
|
||||
};
|
||||
dropdownActions?: Array<MobileCardDropdownAction<T>>;
|
||||
containerRef?: React.RefObject<HTMLDivElement>;
|
||||
className?: string;
|
||||
onExpandToggle?: (expanded: boolean) => void;
|
||||
}
|
||||
|
||||
export function MobileCard<T extends object>({
|
||||
id,
|
||||
item,
|
||||
title,
|
||||
subtitle,
|
||||
badge,
|
||||
icon,
|
||||
isDraggable = false,
|
||||
isExpandable = false,
|
||||
defaultExpanded = false,
|
||||
expandedContent,
|
||||
primaryAction,
|
||||
dropdownActions = [],
|
||||
containerRef,
|
||||
className,
|
||||
onExpandToggle,
|
||||
}: MobileCardProps<T>) {
|
||||
const [isExpanded, setIsExpanded] = useState(defaultExpanded);
|
||||
|
||||
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
|
||||
id,
|
||||
disabled: !isDraggable,
|
||||
});
|
||||
|
||||
const style = {
|
||||
transform: CSS.Transform.toString(transform),
|
||||
transition,
|
||||
};
|
||||
|
||||
const handleExpandToggle = () => {
|
||||
if (isExpandable) {
|
||||
const newExpanded = !isExpanded;
|
||||
setIsExpanded(newExpanded);
|
||||
onExpandToggle?.(newExpanded);
|
||||
}
|
||||
};
|
||||
|
||||
const showDropdownMenu = dropdownActions.length > 0;
|
||||
|
||||
return (
|
||||
<Card
|
||||
ref={setNodeRef}
|
||||
style={style}
|
||||
className={cn(
|
||||
"tw-mb-2",
|
||||
isDragging && [
|
||||
"tw-opacity-90",
|
||||
"tw-shadow-lg",
|
||||
"tw-border-accent/50",
|
||||
"tw-relative",
|
||||
"tw-z-[9999]",
|
||||
"tw-bg-primary",
|
||||
"tw-rounded-lg",
|
||||
"tw-transform-gpu",
|
||||
],
|
||||
isDraggable && "tw-touch-manipulation",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<CardHeader className="tw-p-3">
|
||||
<div className="tw-flex tw-items-center tw-justify-between">
|
||||
{/* Drag handle */}
|
||||
{isDraggable && (
|
||||
<div
|
||||
className="tw-mr-2 tw-cursor-grab tw-touch-none active:tw-cursor-grabbing"
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
>
|
||||
<GripVertical className="tw-size-4" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Main content area */}
|
||||
<div
|
||||
className="tw-flex-1 tw-touch-auto"
|
||||
onClick={isExpandable ? handleExpandToggle : undefined}
|
||||
style={{ cursor: isExpandable ? "pointer" : "default" }}
|
||||
>
|
||||
<div className="tw-flex tw-items-center tw-gap-2">
|
||||
{/* Expand/collapse icon */}
|
||||
{isExpandable && (
|
||||
<div className="tw-flex tw-size-3 tw-items-center tw-justify-center">
|
||||
{isExpanded ? (
|
||||
<ChevronDown className="tw-size-3 tw-stroke-[7]" />
|
||||
) : (
|
||||
<ChevronRight className="tw-size-3 tw-stroke-[7]" />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Custom icon */}
|
||||
{icon && <div className="tw-flex tw-items-center tw-justify-center">{icon}</div>}
|
||||
|
||||
{/* Title and subtitle area */}
|
||||
<div className="tw-min-w-0 tw-flex-1">
|
||||
<div className="tw-break-words tw-font-medium tw-leading-relaxed">
|
||||
{title}
|
||||
{badge && <span className="tw-ml-1 tw-inline-flex tw-items-center">{badge}</span>}
|
||||
</div>
|
||||
{subtitle && (
|
||||
<div className="tw-flex tw-items-center tw-gap-2">
|
||||
<span className="tw-bg-secondary tw-text-sm tw-text-muted">{subtitle}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Action buttons */}
|
||||
<div className="tw-flex tw-items-center tw-gap-2">
|
||||
{primaryAction && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
primaryAction.onClick(item);
|
||||
}}
|
||||
title={primaryAction.tooltip}
|
||||
>
|
||||
{primaryAction.icon}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{showDropdownMenu && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="icon">
|
||||
<MoreVertical className="tw-size-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" container={containerRef?.current}>
|
||||
{dropdownActions.map((action, index) => (
|
||||
<DropdownMenuItem
|
||||
key={index}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
action.onClick(item);
|
||||
}}
|
||||
className={cn(action.variant === "destructive" && "tw-text-error")}
|
||||
>
|
||||
<span className="tw-mr-2 tw-flex tw-size-4 tw-items-center tw-justify-center">
|
||||
{action.icon}
|
||||
</span>
|
||||
{action.label}
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
||||
{/* Expandable content */}
|
||||
{isExpandable && (
|
||||
<div
|
||||
className={cn(
|
||||
"tw-transition-all tw-duration-300 tw-ease-in-out",
|
||||
isExpanded ? "tw-max-h-96 tw-opacity-100" : "tw-max-h-0 tw-overflow-hidden tw-opacity-0"
|
||||
)}
|
||||
>
|
||||
<CardContent className="tw-p-3 tw-pt-0">{expandedContent}</CardContent>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
|
@ -9,7 +9,8 @@ import {
|
|||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { HelpCircle, Zap, ZapOff } from "lucide-react";
|
||||
import { Zap, ZapOff } from "lucide-react";
|
||||
import { HelpTooltip } from "@/components/ui/help-tooltip";
|
||||
|
||||
interface BaseParameterControlProps {
|
||||
label: string;
|
||||
|
|
@ -83,14 +84,10 @@ export function ParameterControl(props: ParameterControlProps) {
|
|||
{label}
|
||||
</span>
|
||||
{helpText && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="tw-size-4 tw-text-muted" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">
|
||||
<div className="tw-max-w-[300px]">{helpText}</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<HelpTooltip
|
||||
buttonClassName="tw-size-4 tw-text-muted"
|
||||
content={<div className="tw-max-w-[300px]">{helpText}</div>}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className={`tw-flex tw-items-center ${optional ? "tw-gap-3" : "tw-gap-0"}`}>
|
||||
|
|
|
|||
|
|
@ -33,17 +33,16 @@ export const TabItem: React.FC<TabItemProps> = ({ tab, isSelected, onClick, isFi
|
|||
"tw-whitespace-nowrap",
|
||||
"tw-text-sm",
|
||||
"tw-border tw-border-solid tw-border-border",
|
||||
"tw-rounded-b-[2px] tw-rounded-t-md",
|
||||
"tw-rounded-b-[2px] tw-rounded-t-sm",
|
||||
"tw-bg-primary",
|
||||
"tw-transition-all tw-duration-300 tw-ease-in-out",
|
||||
"hover:tw-border-interactive-accent",
|
||||
isSelected && [
|
||||
"!tw-bg-interactive-accent",
|
||||
"tw-text-on-accent",
|
||||
"!tw-px-3",
|
||||
"!tw-max-w-full",
|
||||
"tw-transition-all tw-duration-300 tw-ease-in-out",
|
||||
"tw-delay-200",
|
||||
"tw-delay-100",
|
||||
],
|
||||
"lg:tw-max-w-32",
|
||||
"md:tw-max-w-32"
|
||||
|
|
|
|||
|
|
@ -153,6 +153,9 @@ export enum ChatModels {
|
|||
OPENROUTER_GEMINI_2_5_FLASH = "google/gemini-2.5-flash",
|
||||
OPENROUTER_GEMINI_2_5_PRO = "google/gemini-2.5-pro",
|
||||
OPENROUTER_GEMINI_2_5_FLASH_LITE = "google/gemini-2.5-flash-lite",
|
||||
OPENROUTER_GPT_41 = "openai/gpt-4.1",
|
||||
OPENROUTER_GPT_41_MINI = "openai/gpt-4.1-mini",
|
||||
OPENROUTER_GROK_4_FAST_FREE = "x-ai/grok-4-fast:free",
|
||||
}
|
||||
|
||||
// Model Providers
|
||||
|
|
@ -223,6 +226,32 @@ export const BUILTIN_CHAT_MODELS: CustomModel[] = [
|
|||
projectEnabled: true,
|
||||
capabilities: [ModelCapability.VISION],
|
||||
},
|
||||
{
|
||||
name: ChatModels.OPENROUTER_GPT_41,
|
||||
provider: ChatModelProviders.OPENROUTERAI,
|
||||
enabled: true,
|
||||
isBuiltIn: true,
|
||||
core: false,
|
||||
projectEnabled: true,
|
||||
capabilities: [ModelCapability.VISION],
|
||||
},
|
||||
{
|
||||
name: ChatModels.OPENROUTER_GPT_41_MINI,
|
||||
provider: ChatModelProviders.OPENROUTERAI,
|
||||
enabled: true,
|
||||
isBuiltIn: true,
|
||||
core: false,
|
||||
projectEnabled: true,
|
||||
capabilities: [ModelCapability.VISION],
|
||||
},
|
||||
{
|
||||
name: ChatModels.OPENROUTER_GROK_4_FAST_FREE,
|
||||
provider: ChatModelProviders.OPENROUTERAI,
|
||||
enabled: true,
|
||||
isBuiltIn: true,
|
||||
core: false,
|
||||
projectEnabled: true,
|
||||
},
|
||||
{
|
||||
name: ChatModels.GPT_5,
|
||||
provider: ChatModelProviders.OPENAI,
|
||||
|
|
|
|||
|
|
@ -287,11 +287,11 @@ describe("Agent Prompt Integration Test - Direct Model Testing", () => {
|
|||
<title>test.md</title>
|
||||
<content>
|
||||
New York City, USA - The city that never sleeps, New York buzzes with energy, towering skyscrapers, cultural diversity, and endless ambition. From Broadway to Wall Street, it’s a global symbol of dreams and hustle.
|
||||
|
||||
|
||||
Tokyo, Japan - A dazzling blend of futuristic tech and centuries-old tradition, Tokyo moves fast yet bows deep. Neon lights, tranquil shrines, and sushi perfection coexist in this mesmerizing metropolis.
|
||||
|
||||
|
||||
Paris, France - Romantic and refined, Paris is a living museum of art, fashion, and gastronomy. Every street corner whispers history, and every café terrace invites you to linger.
|
||||
|
||||
|
||||
London, UK - A city of kings and punks, rain and rebellion. London blends royal heritage with cutting-edge creativity, from the Tower of London to Shoreditch street art.
|
||||
</content>
|
||||
</active_note>
|
||||
|
|
@ -301,13 +301,9 @@ describe("Agent Prompt Integration Test - Direct Model Testing", () => {
|
|||
toolName: "replaceInFile",
|
||||
// Check if args.diff contains the correct search text
|
||||
argumentValidator: (args) => {
|
||||
expect(args).toEqual(
|
||||
expect.objectContaining({
|
||||
path: "test.md",
|
||||
diff: expect.stringContaining(
|
||||
`------- SEARCH\nLondon, UK - A city of kings and punks, rain and rebellion. London blends royal heritage with cutting-edge creativity, from the Tower of London to Shoreditch street art.\n=======`
|
||||
),
|
||||
})
|
||||
expect(args.path).toBe("test.md");
|
||||
expect(args.diff).toContain(
|
||||
`------- SEARCH\nLondon, UK - A city of kings and punks, rain and rebellion. London blends royal heritage with cutting-edge creativity, from the Tower of London to Shoreditch street art.\n=======`
|
||||
);
|
||||
},
|
||||
mockedReturnValue: "File updated successfully",
|
||||
|
|
@ -323,11 +319,11 @@ describe("Agent Prompt Integration Test - Direct Model Testing", () => {
|
|||
<title>test.md</title>
|
||||
<content>
|
||||
New York City, USA - The city that never sleeps, New York buzzes with energy, towering skyscrapers, cultural diversity, and endless ambition. From Broadway to Wall Street, it’s a global symbol of dreams and hustle.
|
||||
|
||||
|
||||
Tokyo, Japan - A dazzling blend of futuristic tech and centuries-old tradition, Tokyo moves fast yet bows deep. Neon lights, tranquil shrines, and sushi perfection coexist in this mesmerizing metropolis.
|
||||
|
||||
|
||||
Paris, France - Romantic and refined, Paris is a living museum of art, fashion, and gastronomy. Every street corner whispers history, and every café terrace invites you to linger.
|
||||
|
||||
|
||||
London, UK - A city of kings and punks, rain and rebellion. London blends royal heritage with cutting-edge creativity, from the Tower of London to Shoreditch street art.
|
||||
</content>
|
||||
</active_note>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { getChainType } from "@/aiParams";
|
||||
import { ChainType } from "@/chainFactory";
|
||||
import { getSettings } from "@/settings/model";
|
||||
import { logInfo } from "@/logger";
|
||||
import { getSettings, subscribeToSettingsChange } from "@/settings/model";
|
||||
import { App, MarkdownView, Platform, TAbstractFile, TFile } from "obsidian";
|
||||
import { DBOperations } from "./dbOperations";
|
||||
import { IndexOperations } from "./indexOperations";
|
||||
|
|
@ -12,24 +13,65 @@ export class IndexEventHandler {
|
|||
private debounceTimer: number | null = null;
|
||||
private lastActiveFile: TFile | null = null;
|
||||
private lastActiveFileMtime: number | null = null;
|
||||
private listenersActive = false;
|
||||
|
||||
constructor(
|
||||
private app: App,
|
||||
private indexOps: IndexOperations,
|
||||
private dbOps: DBOperations
|
||||
) {
|
||||
this.initializeEventListeners();
|
||||
this.syncEventListeners();
|
||||
subscribeToSettingsChange(() => {
|
||||
this.syncEventListeners();
|
||||
});
|
||||
}
|
||||
|
||||
private initializeEventListeners() {
|
||||
if (getSettings().debug) {
|
||||
console.log("Copilot Plus: Initializing event listeners");
|
||||
/**
|
||||
* Determine whether indexing-related events should be processed based on current settings.
|
||||
*
|
||||
* @returns {boolean} True when semantic search indexing should run.
|
||||
*/
|
||||
private shouldHandleEvents(): boolean {
|
||||
return getSettings().enableSemanticSearchV3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure event listeners are registered only when semantic search indexing is enabled.
|
||||
*/
|
||||
private syncEventListeners(): void {
|
||||
const shouldListen = this.shouldHandleEvents();
|
||||
if (shouldListen && !this.listenersActive) {
|
||||
logInfo("Copilot Plus: Initializing semantic index event listeners");
|
||||
this.app.workspace.on("active-leaf-change", this.handleActiveLeafChange);
|
||||
this.app.vault.on("delete", this.handleFileDelete);
|
||||
this.listenersActive = true;
|
||||
} else if (!shouldListen && this.listenersActive) {
|
||||
this.teardownEventListeners();
|
||||
}
|
||||
this.app.workspace.on("active-leaf-change", this.handleActiveLeafChange);
|
||||
this.app.vault.on("delete", this.handleFileDelete);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove indexing event listeners and reset any pending timers or cached state.
|
||||
*/
|
||||
private teardownEventListeners(): void {
|
||||
if (!this.listenersActive) {
|
||||
return;
|
||||
}
|
||||
this.app.workspace.off("active-leaf-change", this.handleActiveLeafChange);
|
||||
this.app.vault.off("delete", this.handleFileDelete);
|
||||
if (this.debounceTimer !== null) {
|
||||
window.clearTimeout(this.debounceTimer);
|
||||
this.debounceTimer = null;
|
||||
}
|
||||
this.lastActiveFile = null;
|
||||
this.lastActiveFileMtime = null;
|
||||
this.listenersActive = false;
|
||||
}
|
||||
|
||||
private handleActiveLeafChange = async (leaf: any) => {
|
||||
if (!this.shouldHandleEvents()) {
|
||||
return;
|
||||
}
|
||||
if (Platform.isMobile && getSettings().disableIndexOnMobile) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -73,6 +115,9 @@ export class IndexEventHandler {
|
|||
};
|
||||
|
||||
private debouncedReindexFile = (file: TFile) => {
|
||||
if (!this.shouldHandleEvents()) {
|
||||
return;
|
||||
}
|
||||
if (this.debounceTimer !== null) {
|
||||
window.clearTimeout(this.debounceTimer);
|
||||
}
|
||||
|
|
@ -87,28 +132,19 @@ export class IndexEventHandler {
|
|||
};
|
||||
|
||||
private handleFileDelete = async (file: TAbstractFile) => {
|
||||
if (!this.shouldHandleEvents()) {
|
||||
return;
|
||||
}
|
||||
if (file instanceof TFile) {
|
||||
await this.dbOps.removeDocs(file.path);
|
||||
}
|
||||
};
|
||||
|
||||
public cleanup() {
|
||||
if (this.debounceTimer !== null) {
|
||||
window.clearTimeout(this.debounceTimer);
|
||||
}
|
||||
this.app.workspace.off("active-leaf-change", this.handleActiveLeafChange);
|
||||
this.app.vault.off("delete", this.handleFileDelete);
|
||||
this.teardownEventListeners();
|
||||
}
|
||||
|
||||
public unload() {
|
||||
if (this.debounceTimer !== null) {
|
||||
window.clearTimeout(this.debounceTimer);
|
||||
}
|
||||
// Clean up file tracking
|
||||
this.lastActiveFile = null;
|
||||
this.lastActiveFileMtime = null;
|
||||
|
||||
this.app.workspace.off("active-leaf-change", this.handleActiveLeafChange);
|
||||
this.app.vault.off("delete", this.handleFileDelete);
|
||||
this.teardownEventListeners();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ const SettingsContent: React.FC = () => {
|
|||
|
||||
return (
|
||||
<div className="tw-flex tw-flex-col">
|
||||
<div className="tw-inline-flex tw-rounded-lg">
|
||||
<div className="tw-flex tw-flex-wrap tw-rounded-lg">
|
||||
{tabs.map((tab, index) => (
|
||||
<TabItem
|
||||
key={tab.id}
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@ import { Button } from "@/components/ui/button";
|
|||
import { Input } from "@/components/ui/input";
|
||||
import { getModelDisplayWithIcons } from "@/components/ui/model-display";
|
||||
import { SettingItem } from "@/components/ui/setting-item";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { HelpTooltip } from "@/components/ui/help-tooltip";
|
||||
import { DEFAULT_OPEN_AREA, PLUS_UTM_MEDIUMS } from "@/constants";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { createPlusPageUrl } from "@/plusUtils";
|
||||
import { getModelKeyFromModel, updateSetting, useSettingsValue } from "@/settings/model";
|
||||
import { PlusSettings } from "@/settings/v2/components/PlusSettings";
|
||||
import { checkModelApiKey, formatDateTime } from "@/utils";
|
||||
import { HelpCircle, Key, Loader2 } from "lucide-react";
|
||||
import { Key, Loader2 } from "lucide-react";
|
||||
import { Notice } from "obsidian";
|
||||
import React, { useState } from "react";
|
||||
import { ApiKeyDialog } from "./ApiKeyDialog";
|
||||
|
|
@ -104,12 +104,9 @@ export const BasicSettings: React.FC = () => {
|
|||
<span className="tw-leading-none">
|
||||
Configure API keys for different AI providers
|
||||
</span>
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="tw-size-4" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="tw-flex tw-max-w-96 tw-flex-col tw-gap-2 tw-py-4">
|
||||
<HelpTooltip
|
||||
content={
|
||||
<div className="tw-flex tw-max-w-96 tw-flex-col tw-gap-2 tw-py-4">
|
||||
<div className="tw-text-sm tw-font-medium tw-text-accent">
|
||||
API key required for chat and QA features
|
||||
</div>
|
||||
|
|
@ -117,9 +114,9 @@ export const BasicSettings: React.FC = () => {
|
|||
To enable chat and QA functionality, please provide an API key from your
|
||||
selected provider.
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
|
|
@ -141,12 +138,9 @@ export const BasicSettings: React.FC = () => {
|
|||
description={
|
||||
<div className="tw-flex tw-items-center tw-gap-1.5">
|
||||
<span className="tw-leading-none">Select the Chat model to use</span>
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="tw-size-4" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="tw-flex tw-max-w-96 tw-flex-col tw-gap-2 tw-py-4">
|
||||
<HelpTooltip
|
||||
content={
|
||||
<div className="tw-flex tw-max-w-96 tw-flex-col tw-gap-2 tw-py-4">
|
||||
<div className="tw-text-sm tw-font-medium tw-text-accent">
|
||||
Default model is OpenRouter Gemini 2.5 Flash
|
||||
</div>
|
||||
|
|
@ -154,9 +148,9 @@ export const BasicSettings: React.FC = () => {
|
|||
Set your OpenRouter API key in 'API keys' to use this model, or
|
||||
select a different model from another provider.
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
value={defaultModelActivated ? settings.defaultModelKey : "Select Model"}
|
||||
|
|
@ -188,12 +182,9 @@ export const BasicSettings: React.FC = () => {
|
|||
description={
|
||||
<div className="tw-flex tw-items-center tw-gap-1.5">
|
||||
<span className="tw-leading-none">Select the default chat mode</span>
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="tw-size-4" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="tw-flex tw-max-w-96 tw-flex-col tw-gap-2">
|
||||
<HelpTooltip
|
||||
content={
|
||||
<div className="tw-flex tw-max-w-96 tw-flex-col tw-gap-2">
|
||||
<ul className="tw-pl-4 tw-text-sm tw-text-muted">
|
||||
<li>
|
||||
<strong>Chat:</strong> Regular chat mode for general conversations and
|
||||
|
|
@ -218,9 +209,9 @@ export const BasicSettings: React.FC = () => {
|
|||
for more details.
|
||||
</li>
|
||||
</ul>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
value={settings.defaultChainType}
|
||||
|
|
@ -269,12 +260,9 @@ export const BasicSettings: React.FC = () => {
|
|||
<span className="tw-leading-none">
|
||||
Customize the format of saved conversation note names.
|
||||
</span>
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="tw-size-4" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="tw-flex tw-max-w-96 tw-flex-col tw-gap-2 tw-py-4">
|
||||
<HelpTooltip
|
||||
content={
|
||||
<div className="tw-flex tw-max-w-96 tw-flex-col tw-gap-2 tw-py-4">
|
||||
<div className="tw-text-sm tw-font-medium tw-text-accent">
|
||||
Note: All the following variables must be included in the template.
|
||||
</div>
|
||||
|
|
@ -298,9 +286,9 @@ export const BasicSettings: React.FC = () => {
|
|||
20250114_153232__polish_this_article_[[Readme]]
|
||||
</i>
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { useCustomCommands } from "@/commands/state";
|
||||
import React, { useMemo, useRef } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Copy, GripVertical, Info, Lightbulb, PenLine, Plus, Trash2 } from "lucide-react";
|
||||
import React, { useMemo } from "react";
|
||||
import { useCustomCommands } from "@/commands/state";
|
||||
import { MobileCard, MobileCardDropdownAction } from "@/components/ui/mobile-card";
|
||||
import { Copy, GripVertical, Lightbulb, PenLine, Plus, Trash2 } from "lucide-react";
|
||||
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import {
|
||||
|
|
@ -12,10 +13,6 @@ import {
|
|||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { logError } from "@/logger";
|
||||
import { updateSetting, useSettingsValue } from "@/settings/model";
|
||||
import { PromptSortStrategy } from "@/types";
|
||||
import {
|
||||
closestCenter,
|
||||
DndContext,
|
||||
|
|
@ -33,6 +30,10 @@ import {
|
|||
} from "@dnd-kit/sortable";
|
||||
import { CSS } from "@dnd-kit/utilities";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { updateSetting, useSettingsValue } from "@/settings/model";
|
||||
import { PromptSortStrategy } from "@/types";
|
||||
import { HelpTooltip } from "@/components/ui/help-tooltip";
|
||||
import { EMPTY_COMMAND } from "@/commands/constants";
|
||||
import { CustomCommandManager } from "@/commands/customCommandManager";
|
||||
import { CustomCommandSettingsModal } from "@/commands/CustomCommandSettingsModal";
|
||||
|
|
@ -45,9 +46,124 @@ import { generateDefaultCommands } from "@/commands/migrator";
|
|||
import { CustomCommand } from "@/commands/type";
|
||||
import { ConfirmModal } from "@/components/modals/ConfirmModal";
|
||||
import { SettingItem } from "@/components/ui/setting-item";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { Notice } from "obsidian";
|
||||
|
||||
const MobileCommandCard: React.FC<{
|
||||
command: CustomCommand;
|
||||
commands: CustomCommand[];
|
||||
onUpdate: (newCommand: CustomCommand, prevCommandTitle: string) => void;
|
||||
onRemove: (command: CustomCommand) => void;
|
||||
onCopy: (command: CustomCommand) => void;
|
||||
containerRef: React.RefObject<HTMLDivElement>;
|
||||
}> = ({ command, commands, onUpdate, onRemove, onCopy, containerRef }) => {
|
||||
const handleEdit = (cmd: CustomCommand) => {
|
||||
const modal = new CustomCommandSettingsModal(app, commands, cmd, async (updatedCommand) => {
|
||||
await onUpdate(updatedCommand, cmd.title);
|
||||
});
|
||||
modal.open();
|
||||
};
|
||||
|
||||
const dropdownActions: MobileCardDropdownAction<CustomCommand>[] = [
|
||||
{
|
||||
icon: <PenLine className="tw-size-4" />,
|
||||
label: "Edit",
|
||||
onClick: handleEdit,
|
||||
},
|
||||
{
|
||||
icon: <Copy className="tw-size-4" />,
|
||||
label: "Copy",
|
||||
onClick: onCopy,
|
||||
},
|
||||
{
|
||||
icon: <Trash2 className="tw-size-4" />,
|
||||
label: "Delete",
|
||||
onClick: (cmd) => {
|
||||
new ConfirmModal(
|
||||
app,
|
||||
() => onRemove(cmd),
|
||||
`Are you sure you want to delete the command "${cmd.title}"? This will permanently remove the command file and cannot be undone.`,
|
||||
"Delete Command",
|
||||
"Delete",
|
||||
"Cancel"
|
||||
).open();
|
||||
},
|
||||
variant: "destructive",
|
||||
},
|
||||
];
|
||||
|
||||
const expandedContent = (
|
||||
<div className="tw-flex tw-flex-wrap tw-justify-around">
|
||||
<div className="tw-flex tw-items-center tw-justify-between tw-gap-2">
|
||||
<div className="tw-flex tw-items-center tw-gap-1">
|
||||
<span className="tw-text-sm tw-font-medium">In Menu</span>
|
||||
<HelpTooltip
|
||||
content={
|
||||
<div className="tw-max-w-xs tw-text-xs">
|
||||
If enabled, the command will be available in the context menu when you right-click
|
||||
in the editor.
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<Checkbox
|
||||
checked={command.showInContextMenu}
|
||||
onCheckedChange={(checked) => {
|
||||
onUpdate(
|
||||
{
|
||||
...command,
|
||||
showInContextMenu: checked === true,
|
||||
},
|
||||
command.title
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="tw-flex tw-items-center tw-justify-between tw-gap-2">
|
||||
<div className="tw-flex tw-items-center tw-gap-1">
|
||||
<span className="tw-text-sm tw-font-medium">In Slash</span>
|
||||
<HelpTooltip
|
||||
content={
|
||||
<div className="tw-max-w-xs tw-text-xs">
|
||||
If enabled, the command will be available as a slash command in the chat.
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<Checkbox
|
||||
checked={command.showInSlashMenu}
|
||||
onCheckedChange={(checked) =>
|
||||
onUpdate(
|
||||
{
|
||||
...command,
|
||||
showInSlashMenu: checked === true,
|
||||
},
|
||||
command.title
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<MobileCard
|
||||
id={command.title}
|
||||
item={command}
|
||||
title={command.title}
|
||||
isDraggable
|
||||
isExpandable
|
||||
expandedContent={expandedContent}
|
||||
primaryAction={{
|
||||
icon: <PenLine className="tw-size-4" />,
|
||||
onClick: handleEdit,
|
||||
tooltip: "Edit Command",
|
||||
}}
|
||||
dropdownActions={dropdownActions}
|
||||
containerRef={containerRef}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const SortableTableRow: React.FC<{
|
||||
command: CustomCommand;
|
||||
commands: CustomCommand[];
|
||||
|
|
@ -169,6 +285,7 @@ export const CommandSettings: React.FC = () => {
|
|||
}, [rawCommands]);
|
||||
|
||||
const settings = useSettingsValue();
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor, {
|
||||
activationConstraint: {
|
||||
|
|
@ -241,8 +358,40 @@ export const CommandSettings: React.FC = () => {
|
|||
await CustomCommandManager.getInstance().reorderCommands(newCommands);
|
||||
};
|
||||
|
||||
// Mobile view rendering
|
||||
const renderMobileView = () => (
|
||||
<div className="tw-relative md:tw-hidden">
|
||||
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
|
||||
<SortableContext
|
||||
items={commands.map((command) => command.title)}
|
||||
strategy={verticalListSortingStrategy}
|
||||
>
|
||||
<div className="tw-space-y-2">
|
||||
{commands.length === 0 ? (
|
||||
<div className="tw-rounded-lg tw-border tw-border-border tw-bg-primary tw-p-8 tw-text-center tw-text-muted">
|
||||
No custom prompt files found.
|
||||
</div>
|
||||
) : (
|
||||
commands.map((command) => (
|
||||
<MobileCommandCard
|
||||
key={command.title}
|
||||
command={command}
|
||||
commands={commands}
|
||||
onUpdate={handleUpdate}
|
||||
onRemove={handleRemove}
|
||||
onCopy={handleCopy}
|
||||
containerRef={containerRef}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="tw-space-y-4">
|
||||
<div className="tw-space-y-4" ref={containerRef}>
|
||||
<section>
|
||||
<div className="tw-mb-4 tw-flex tw-flex-col tw-gap-2">
|
||||
<div className="tw-text-xl tw-font-bold">Custom Commands</div>
|
||||
|
|
@ -296,7 +445,7 @@ export const CommandSettings: React.FC = () => {
|
|||
</div>
|
||||
|
||||
<div className="tw-flex tw-flex-col tw-gap-4">
|
||||
<div className="tw-flex tw-w-full tw-justify-between">
|
||||
<div className="tw-flex tw-w-full tw-justify-between tw-gap-2 md:tw-justify-end">
|
||||
<div>
|
||||
<Button
|
||||
variant="secondary"
|
||||
|
|
@ -309,7 +458,7 @@ export const CommandSettings: React.FC = () => {
|
|||
).open()
|
||||
}
|
||||
>
|
||||
Generate Default Commands
|
||||
Generate Default
|
||||
</Button>
|
||||
</div>
|
||||
<Button
|
||||
|
|
@ -330,82 +479,83 @@ export const CommandSettings: React.FC = () => {
|
|||
modal.open();
|
||||
}}
|
||||
>
|
||||
<Plus className="tw-size-4" />
|
||||
Add Command
|
||||
<Plus className="tw-size-2 md:tw-size-4" />
|
||||
Add Cmd
|
||||
</Button>
|
||||
</div>
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="tw-w-10"></TableHead>
|
||||
<TableHead>Name</TableHead>
|
||||
<TableHead className="tw-w-24 tw-text-center">
|
||||
<div className="tw-flex tw-items-center tw-justify-center tw-gap-1">
|
||||
In Menu
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Info className="tw-size-4" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="tw-max-w-xs tw-text-xs">
|
||||
If enabled, the command will be available in the context menu when you
|
||||
right-click in the editor.
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
</TableHead>
|
||||
<TableHead className="tw-w-28 tw-text-center">
|
||||
<div className="tw-flex tw-items-center tw-justify-center tw-gap-1">
|
||||
Slash Cmd
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Info className="tw-size-4" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="tw-max-w-xs tw-text-xs">
|
||||
If enabled, the command will be available as a slash command in the
|
||||
chat.
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
</TableHead>
|
||||
<TableHead className="tw-w-32 tw-text-center">Actions</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<SortableContext
|
||||
items={commands.map((command) => command.title)}
|
||||
strategy={verticalListSortingStrategy}
|
||||
>
|
||||
<TableBody>
|
||||
{commands.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={5} className="tw-py-8 tw-text-center tw-text-muted">
|
||||
No custom prompt files found.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
commands.map((command) => (
|
||||
<SortableTableRow
|
||||
key={command.title}
|
||||
command={command}
|
||||
commands={commands}
|
||||
onUpdate={handleUpdate}
|
||||
onRemove={handleRemove}
|
||||
onCopy={handleCopy}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</TableBody>
|
||||
</SortableContext>
|
||||
</Table>
|
||||
</DndContext>
|
||||
|
||||
{/* Desktop view */}
|
||||
<div className="tw-hidden md:tw-block">
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="tw-w-10"></TableHead>
|
||||
<TableHead>Name</TableHead>
|
||||
<TableHead className="tw-w-24 tw-text-center">
|
||||
<div className="tw-flex tw-items-center tw-justify-center tw-gap-1">
|
||||
In Menu
|
||||
<HelpTooltip
|
||||
content={
|
||||
<div className="tw-max-w-xs tw-text-xs">
|
||||
If enabled, the command will be available in the context menu when you
|
||||
right-click in the editor.
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</TableHead>
|
||||
<TableHead className="tw-w-28 tw-text-center">
|
||||
<div className="tw-flex tw-items-center tw-justify-center tw-gap-1">
|
||||
Slash Cmd
|
||||
<HelpTooltip
|
||||
content={
|
||||
<div className="tw-max-w-xs tw-text-xs">
|
||||
If enabled, the command will be available as a slash command in the
|
||||
chat.
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</TableHead>
|
||||
<TableHead className="tw-w-32 tw-text-center">Actions</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<SortableContext
|
||||
items={commands.map((command) => command.title)}
|
||||
strategy={verticalListSortingStrategy}
|
||||
>
|
||||
<TableBody>
|
||||
{commands.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={5} className="tw-py-8 tw-text-center tw-text-muted">
|
||||
No custom prompt files found.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
commands.map((command) => (
|
||||
<SortableTableRow
|
||||
key={command.title}
|
||||
command={command}
|
||||
commands={commands}
|
||||
onUpdate={handleUpdate}
|
||||
onRemove={handleRemove}
|
||||
onCopy={handleCopy}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</TableBody>
|
||||
</SortableContext>
|
||||
</Table>
|
||||
</DndContext>
|
||||
</div>
|
||||
|
||||
{/* Mobile view */}
|
||||
{renderMobileView()}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -10,12 +10,12 @@ import {
|
|||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { SettingItem } from "@/components/ui/setting-item";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { HelpTooltip } from "@/components/ui/help-tooltip";
|
||||
import { AUTOCOMPLETE_CONFIG } from "@/constants";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { logError } from "@/logger";
|
||||
import { updateSetting, useSettingsValue } from "@/settings/model";
|
||||
import { HelpCircle, RefreshCw } from "lucide-react";
|
||||
import { RefreshCw } from "lucide-react";
|
||||
import { Notice } from "obsidian";
|
||||
import React, { useState } from "react";
|
||||
import { ToolSettingsSection } from "./ToolSettingsSection";
|
||||
|
|
@ -192,19 +192,16 @@ export const CopilotPlusSettings: React.FC = () => {
|
|||
<span className="tw-leading-none">
|
||||
The key used to accept autocomplete suggestions
|
||||
</span>
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="tw-size-4" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="tw-flex tw-max-w-96 tw-flex-col tw-gap-2">
|
||||
<HelpTooltip
|
||||
content={
|
||||
<div className="tw-flex tw-max-w-96 tw-flex-col tw-gap-2">
|
||||
<div className="tw-text-sm tw-text-muted">
|
||||
Select the key you want to use for accepting suggestions. Default is
|
||||
"Tab".
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import { Label } from "@/components/ui/label";
|
|||
import { Input } from "@/components/ui/input";
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ChevronDown, HelpCircle, Loader2 } from "lucide-react";
|
||||
import { ChevronDown, Loader2 } from "lucide-react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
|
|
@ -36,7 +36,7 @@ import {
|
|||
} from "@/components/ui/select";
|
||||
import { PasswordInput } from "@/components/ui/password-input";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { HelpTooltip } from "@/components/ui/help-tooltip";
|
||||
import { FormField } from "@/components/ui/form-field";
|
||||
|
||||
interface FormErrors {
|
||||
|
|
@ -422,25 +422,21 @@ export const ModelAddDialog: React.FC<ModelAddDialogProps> = ({
|
|||
label={
|
||||
<div className="tw-flex tw-items-center tw-gap-1.5">
|
||||
<span className="tw-leading-none">Display Name</span>
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="tw-size-4" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent align="start" className="tw-max-w-96" side="bottom">
|
||||
<div className="tw-flex tw-flex-col tw-gap-0.5 tw-text-sm tw-text-muted">
|
||||
<div className="tw-text-[12px] tw-font-bold">Suggested format:</div>
|
||||
<div className="tw-text-accent">[Source]-[Payment]:[Pretty Model Name]</div>
|
||||
<div className="tw-text-[12px]">
|
||||
Example:
|
||||
<li>Direct-Paid:Ds-r1</li>
|
||||
<li>OpenRouter-Paid:Ds-r1</li>
|
||||
<li>Perplexity-Paid:lg</li>
|
||||
</div>
|
||||
<HelpTooltip
|
||||
content={
|
||||
<div className="tw-flex tw-flex-col tw-gap-0.5 tw-text-sm tw-text-muted">
|
||||
<div className="tw-text-[12px] tw-font-bold">Suggested format:</div>
|
||||
<div className="tw-text-accent">[Source]-[Payment]:[Pretty Model Name]</div>
|
||||
<div className="tw-text-[12px]">
|
||||
Example:
|
||||
<li>Direct-Paid:Ds-r1</li>
|
||||
<li>OpenRouter-Paid:Ds-r1</li>
|
||||
<li>Perplexity-Paid:lg</li>
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
}
|
||||
contentClassName="tw-max-w-96"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
|
|
@ -501,18 +497,14 @@ export const ModelAddDialog: React.FC<ModelAddDialogProps> = ({
|
|||
label={
|
||||
<div className="tw-flex tw-items-center tw-gap-1.5">
|
||||
<span className="tw-leading-none">Model Capabilities</span>
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="tw-size-4" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent align="start" className="tw-max-w-96" side="bottom">
|
||||
<div className="tw-text-sm tw-text-muted">
|
||||
Only used to display model capabilities, does not affect model functionality
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
<HelpTooltip
|
||||
content={
|
||||
<div className="tw-text-sm tw-text-muted">
|
||||
Only used to display model capabilities, does not affect model functionality
|
||||
</div>
|
||||
}
|
||||
contentClassName="tw-max-w-96"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
|
|
@ -532,16 +524,11 @@ export const ModelAddDialog: React.FC<ModelAddDialogProps> = ({
|
|||
});
|
||||
}}
|
||||
/>
|
||||
<Label htmlFor={id} className="tw-text-sm">
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span>{label}</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">{description}</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</Label>
|
||||
<HelpTooltip content={description}>
|
||||
<Label htmlFor={id} className="tw-text-sm">
|
||||
{label}
|
||||
</Label>
|
||||
</HelpTooltip>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -550,39 +537,35 @@ export const ModelAddDialog: React.FC<ModelAddDialogProps> = ({
|
|||
{renderProviderSpecificFields()}
|
||||
</div>
|
||||
|
||||
<div className="tw-flex tw-items-center tw-justify-end tw-gap-4">
|
||||
<div className="tw-flex tw-items-center tw-justify-between tw-gap-4">
|
||||
<div className="tw-flex tw-items-center tw-gap-2">
|
||||
<Checkbox
|
||||
id="enable-cors"
|
||||
checked={model.enableCors || false}
|
||||
onCheckedChange={(checked: boolean) => setModel({ ...model, enableCors: checked })}
|
||||
/>
|
||||
<Label htmlFor="enable-cors" className="tw-text-sm">
|
||||
<div className="tw-flex tw-items-center tw-gap-1.5">
|
||||
<span>Enable CORS</span>
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="tw-size-4" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent align="start" className="tw-max-w-96" side="bottom">
|
||||
<div className="tw-text-sm tw-text-muted">
|
||||
Only check this option when prompted that CORS is needed
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
<Label htmlFor="enable-cors">
|
||||
<div className="tw-flex tw-items-center tw-gap-0.5">
|
||||
<span className="tw-text-xs md:tw-text-sm">CORS</span>
|
||||
<HelpTooltip
|
||||
content={
|
||||
<div className="tw-text-sm tw-text-muted">
|
||||
Only check this option when prompted that CORS is needed
|
||||
</div>
|
||||
}
|
||||
contentClassName="tw-max-w-96"
|
||||
/>
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
<div className="tw-flex tw-gap-2">
|
||||
<div className="tw-flex tw-gap-2 tw-text-xs md:tw-text-sm">
|
||||
<Button variant="secondary" onClick={handleAdd} disabled={isButtonDisabled()}>
|
||||
Add Model
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={handleVerify} disabled={isButtonDisabled()}>
|
||||
{isVerifying ? (
|
||||
<>
|
||||
<Loader2 className="tw-mr-2 tw-size-4 tw-animate-spin" />
|
||||
<Loader2 className="tw-mr-2 tw-size-2 tw-animate-spin md:tw-size-4 " />
|
||||
Verify
|
||||
</>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { Input } from "@/components/ui/input";
|
|||
import { Label } from "@/components/ui/label";
|
||||
import { PasswordInput } from "@/components/ui/password-input";
|
||||
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { HelpTooltip } from "@/components/ui/help-tooltip";
|
||||
import {
|
||||
DEFAULT_MODEL_SETTING,
|
||||
MODEL_CAPABILITIES,
|
||||
|
|
@ -17,7 +17,6 @@ import {
|
|||
} from "@/constants";
|
||||
import { getSettings } from "@/settings/model";
|
||||
import { debounce, getProviderInfo, getProviderLabel } from "@/utils";
|
||||
import { HelpCircle } from "lucide-react";
|
||||
import { App, Modal, Platform } from "obsidian";
|
||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { createRoot, Root } from "react-dom/client";
|
||||
|
|
@ -143,25 +142,21 @@ export const ModelEditModalContent: React.FC<ModelEditModalContentProps> = ({
|
|||
label={
|
||||
<div className="tw-flex tw-items-center tw-gap-1.5">
|
||||
<span className="tw-leading-none">Display Name</span>
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="tw-size-4" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent align="start" className="tw-max-w-96" side="bottom">
|
||||
<div className="tw-flex tw-flex-col tw-gap-0.5 tw-text-sm tw-text-muted">
|
||||
<div className="tw-text-[12px] tw-font-bold">Suggested format:</div>
|
||||
<div className="tw-text-accent">[Source]-[Payment]:[Pretty Model Name]</div>
|
||||
<div className="tw-text-[12px]">
|
||||
Example:
|
||||
<li>Direct-Paid:Ds-r1</li>
|
||||
<li>OpenRouter-Paid:Ds-r1</li>
|
||||
<li>Perplexity-Paid:lg</li>
|
||||
</div>
|
||||
<HelpTooltip
|
||||
content={
|
||||
<div className="tw-flex tw-flex-col tw-gap-0.5 tw-text-sm tw-text-muted">
|
||||
<div className="tw-text-[12px] tw-font-bold">Suggested format:</div>
|
||||
<div className="tw-text-accent">[Source]-[Payment]:[Pretty Model Name]</div>
|
||||
<div className="tw-text-[12px]">
|
||||
Example:
|
||||
<li>Direct-Paid:Ds-r1</li>
|
||||
<li>OpenRouter-Paid:Ds-r1</li>
|
||||
<li>Perplexity-Paid:lg</li>
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
}
|
||||
contentClassName="tw-max-w-96"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
|
|
@ -207,19 +202,14 @@ export const ModelEditModalContent: React.FC<ModelEditModalContentProps> = ({
|
|||
label={
|
||||
<div className="tw-flex tw-items-center tw-gap-1.5">
|
||||
<span className="tw-leading-none">Model Capabilities</span>
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="tw-size-4" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent align="start" className="tw-max-w-96" side="bottom">
|
||||
<div className="tw-text-sm tw-text-muted">
|
||||
Only used to display model capabilities, does not affect model
|
||||
functionality
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
<HelpTooltip
|
||||
content={
|
||||
<div className="tw-text-sm tw-text-muted">
|
||||
Only used to display model capabilities, does not affect model functionality
|
||||
</div>
|
||||
}
|
||||
contentClassName="tw-max-w-96"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
|
|
@ -237,16 +227,11 @@ export const ModelEditModalContent: React.FC<ModelEditModalContentProps> = ({
|
|||
handleLocalUpdate("capabilities", value);
|
||||
}}
|
||||
/>
|
||||
<Label htmlFor={id} className="tw-text-sm">
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span>{label}</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">{description}</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</Label>
|
||||
<HelpTooltip content={description}>
|
||||
<Label htmlFor={id} className="tw-text-sm">
|
||||
{label}
|
||||
</Label>
|
||||
</HelpTooltip>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -143,7 +143,6 @@ export const ModelSettings: React.FC = () => {
|
|||
return (
|
||||
<div className="tw-space-y-4">
|
||||
<section>
|
||||
<div className="tw-mb-3 tw-text-xl tw-font-bold">Chat Models</div>
|
||||
<ModelTable
|
||||
models={settings.activeModels}
|
||||
onEdit={(model) => handleEditModel(model)}
|
||||
|
|
@ -153,7 +152,7 @@ export const ModelSettings: React.FC = () => {
|
|||
onUpdateModel={handleTableUpdate}
|
||||
onReorderModels={(newModels) => handleModelReorder(newModels)}
|
||||
onRefresh={handleRefreshChatModels}
|
||||
title="Chat Model"
|
||||
title="Chat Models"
|
||||
/>
|
||||
|
||||
{/* model add dialog */}
|
||||
|
|
@ -184,7 +183,6 @@ export const ModelSettings: React.FC = () => {
|
|||
</section>
|
||||
|
||||
<section>
|
||||
<div className="tw-mb-3 tw-text-xl tw-font-bold">Embedding Models</div>
|
||||
<ModelTable
|
||||
models={settings.activeEmbeddingModels}
|
||||
onEdit={(model) => handleEditModel(model, true)}
|
||||
|
|
@ -194,7 +192,7 @@ export const ModelSettings: React.FC = () => {
|
|||
onUpdateModel={handleEmbeddingModelUpdate}
|
||||
onReorderModels={(newModels) => handleModelReorder(newModels, true)}
|
||||
onRefresh={handleRefreshEmbeddingModels}
|
||||
title="Embedding Model"
|
||||
title="Embedding Models"
|
||||
/>
|
||||
|
||||
{/* Embedding model add dialog */}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { CustomModel } from "@/aiParams";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { MobileCard, MobileCardDropdownAction } from "@/components/ui/mobile-card";
|
||||
import { ModelCapabilityIcons } from "@/components/ui/model-display";
|
||||
import {
|
||||
Table,
|
||||
|
|
@ -11,7 +11,7 @@ import {
|
|||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { HelpTooltip } from "@/components/ui/help-tooltip";
|
||||
import { MODEL_CAPABILITIES, ModelCapability } from "@/constants";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { getModelKeyFromModel } from "@/settings/model";
|
||||
|
|
@ -34,8 +34,6 @@ import {
|
|||
} from "@dnd-kit/sortable";
|
||||
import { CSS } from "@dnd-kit/utilities";
|
||||
import {
|
||||
ChevronDown,
|
||||
ChevronRight,
|
||||
Copy,
|
||||
Eye,
|
||||
Globe,
|
||||
|
|
@ -49,14 +47,13 @@ import {
|
|||
RefreshCw,
|
||||
Trash2,
|
||||
} from "lucide-react";
|
||||
import React, { ForwardRefExoticComponent, RefAttributes } from "react";
|
||||
import React, { ForwardRefExoticComponent, RefAttributes, useRef } from "react";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { useContainerContext } from "@/settings/v2/components/ContainerContext";
|
||||
|
||||
const CAPABILITY_ICONS: Record<
|
||||
ModelCapability,
|
||||
|
|
@ -89,34 +86,60 @@ const CAPABILITY_ORDER = [
|
|||
ModelCapability.WEB_SEARCH,
|
||||
] as const;
|
||||
|
||||
interface ModelTableHeaderProps {
|
||||
title: string;
|
||||
onRefresh?: () => void;
|
||||
onAdd: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the model table header with a title and aligned action buttons.
|
||||
*/
|
||||
const ModelTableHeader: React.FC<ModelTableHeaderProps> = ({ title, onRefresh, onAdd }) => (
|
||||
<div className="tw-mb-3 tw-flex tw-flex-col tw-gap-2 md:tw-flex-row md:tw-items-center md:tw-justify-between">
|
||||
<h3 className="tw-text-xl tw-font-bold">{title}</h3>
|
||||
<div className="tw-flex tw-flex-col tw-gap-2 sm:tw-flex-row sm:tw-items-center sm:tw-justify-end">
|
||||
{onRefresh && (
|
||||
<Button
|
||||
onClick={onRefresh}
|
||||
variant="secondary"
|
||||
className="tw-flex tw-items-center tw-gap-2"
|
||||
>
|
||||
<RefreshCw className="tw-size-2 md:tw-size-4" />
|
||||
Refresh Built-ins
|
||||
</Button>
|
||||
)}
|
||||
<Button onClick={onAdd} variant="default" className="tw-flex tw-items-center tw-gap-2">
|
||||
<Plus className="tw-size-2 md:tw-size-4" />
|
||||
Add Model
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderCapabilities = (model: CustomModel) => {
|
||||
return (
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<div className="tw-mx-auto tw-grid tw-w-16 tw-grid-cols-3 tw-gap-1">
|
||||
{CAPABILITY_ORDER.map((capability) => {
|
||||
const config = CAPABILITY_ICONS[capability];
|
||||
if (!config) return <div key={capability} className="tw-w-4" />;
|
||||
<div className="tw-mx-auto tw-grid tw-w-16 tw-grid-cols-3 tw-gap-1">
|
||||
{CAPABILITY_ORDER.map((capability) => {
|
||||
const config = CAPABILITY_ICONS[capability];
|
||||
if (!config) return <div key={capability} className="tw-w-4" />;
|
||||
|
||||
const Icon = config.icon;
|
||||
const hasCapability = model.capabilities?.includes(capability);
|
||||
const Icon = config.icon;
|
||||
const hasCapability = model.capabilities?.includes(capability);
|
||||
|
||||
return hasCapability ? (
|
||||
<Tooltip key={capability}>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="tw-flex tw-items-center tw-justify-center">
|
||||
<Icon className={cn("tw-size-4", config.color)} />
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">{config.tooltip}</TooltipContent>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<div key={capability} className="tw-flex tw-items-center tw-justify-center">
|
||||
<div className="tw-size-4" />
|
||||
return hasCapability ? (
|
||||
<HelpTooltip key={capability} content={config.tooltip} side="bottom">
|
||||
<div className="tw-flex tw-items-center tw-justify-center">
|
||||
<Icon className={cn("tw-size-4", config.color)} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
</HelpTooltip>
|
||||
) : (
|
||||
<div key={capability} className="tw-flex tw-items-center tw-justify-center">
|
||||
<div className="tw-size-4" />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -127,6 +150,7 @@ interface ModelCardProps {
|
|||
onDelete: (modelKey: string) => void;
|
||||
onUpdateModel: (model: CustomModel) => void;
|
||||
id: string;
|
||||
containerRef: React.RefObject<HTMLDivElement>;
|
||||
}
|
||||
|
||||
const ModelCard: React.FC<ModelCardProps> = ({
|
||||
|
|
@ -136,169 +160,82 @@ const ModelCard: React.FC<ModelCardProps> = ({
|
|||
onDelete,
|
||||
onUpdateModel,
|
||||
id,
|
||||
containerRef,
|
||||
}) => {
|
||||
const [isExpanded, setIsExpanded] = React.useState(false);
|
||||
const container = useContainerContext();
|
||||
const dropdownActions: MobileCardDropdownAction<CustomModel>[] = [];
|
||||
|
||||
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
|
||||
id,
|
||||
disabled: model.core,
|
||||
});
|
||||
if (onEdit) {
|
||||
dropdownActions.push({
|
||||
icon: <PencilLine className="tw-size-4" />,
|
||||
label: "Edit",
|
||||
onClick: onEdit,
|
||||
});
|
||||
}
|
||||
|
||||
const style = {
|
||||
transform: CSS.Transform.toString(transform),
|
||||
transition,
|
||||
};
|
||||
if (onCopy && !model.core) {
|
||||
dropdownActions.push({
|
||||
icon: <Copy className="tw-size-4" />,
|
||||
label: "Copy",
|
||||
onClick: onCopy,
|
||||
});
|
||||
}
|
||||
|
||||
if (!model.core) {
|
||||
dropdownActions.push({
|
||||
icon: <Trash2 className="tw-size-4" />,
|
||||
label: "Delete",
|
||||
onClick: () => onDelete(getModelKeyFromModel(model)),
|
||||
variant: "destructive",
|
||||
});
|
||||
}
|
||||
|
||||
const expandedContent = (
|
||||
<div className="tw-flex tw-justify-around">
|
||||
{!model.isEmbeddingModel && (
|
||||
<div className="tw-flex tw-items-center tw-gap-2">
|
||||
<span className="tw-text-sm">Enabled</span>
|
||||
<Checkbox
|
||||
checked={model.enabled}
|
||||
onCheckedChange={(checked: boolean) => onUpdateModel({ ...model, enabled: checked })}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="tw-flex tw-items-center tw-gap-2">
|
||||
<span className="tw-text-sm">CORS</span>
|
||||
<Checkbox
|
||||
checked={model.enableCors}
|
||||
onCheckedChange={(checked: boolean) => onUpdateModel({ ...model, enableCors: checked })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Card
|
||||
ref={setNodeRef}
|
||||
style={style}
|
||||
className={cn(
|
||||
"tw-mb-2",
|
||||
isDragging && [
|
||||
"tw-opacity-90",
|
||||
"tw-shadow-lg",
|
||||
"tw-border-accent/50",
|
||||
"tw-relative",
|
||||
"tw-z-[9999]",
|
||||
"tw-bg-primary",
|
||||
"tw-rounded-lg",
|
||||
"tw-transform-gpu",
|
||||
],
|
||||
!model.core && "tw-touch-none"
|
||||
)}
|
||||
>
|
||||
<CardHeader className="tw-p-3">
|
||||
<div className="tw-flex tw-items-center tw-justify-between">
|
||||
{!model.core && (
|
||||
<div
|
||||
className="tw-mr-2 tw-cursor-grab tw-touch-none active:tw-cursor-grabbing"
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
>
|
||||
<GripVertical className="tw-size-4" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="tw-flex-1 tw-cursor-pointer" onClick={() => setIsExpanded(!isExpanded)}>
|
||||
<div className="tw-flex tw-items-center tw-gap-2">
|
||||
<div className="tw-flex tw-size-3 tw-items-center tw-justify-center">
|
||||
{isExpanded ? (
|
||||
<ChevronDown className="tw-size-3 tw-stroke-[7]" />
|
||||
) : (
|
||||
<ChevronRight className="tw-size-3 tw-stroke-[7]" />
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<div className="tw-flex tw-items-center tw-gap-1">
|
||||
<span className="tw-font-medium">{model.displayName || model.name}</span>
|
||||
{model.capabilities && model.capabilities.length > 0 && (
|
||||
<ModelCapabilityIcons capabilities={model.capabilities} iconSize={14} />
|
||||
)}
|
||||
</div>
|
||||
<div className="tw-flex tw-items-center tw-gap-2">
|
||||
<span className="tw-bg-secondary tw-text-sm tw-text-muted">
|
||||
{getProviderLabel(model.provider, model)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="tw-flex tw-items-center tw-gap-2">
|
||||
{onEdit && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onEdit(model);
|
||||
}}
|
||||
>
|
||||
<Pencil className="tw-size-4" />
|
||||
</Button>
|
||||
)}
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="icon">
|
||||
<MoreVertical className="tw-size-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" container={container}>
|
||||
{onEdit && (
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onEdit(model);
|
||||
}}
|
||||
>
|
||||
<PencilLine className="tw-mr-2 tw-size-4" />
|
||||
Edit
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
|
||||
{onCopy && !model.core && (
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onCopy(model);
|
||||
}}
|
||||
>
|
||||
<Copy className="tw-mr-2 tw-size-4" />
|
||||
Copy
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
|
||||
{!model.core && (
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onDelete(getModelKeyFromModel(model));
|
||||
}}
|
||||
className="tw-text-error"
|
||||
>
|
||||
<Trash2 className="tw-mr-2 tw-size-4" />
|
||||
Delete
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<div
|
||||
className={cn(
|
||||
"tw-transition-all tw-duration-300 tw-ease-in-out",
|
||||
isExpanded ? "tw-max-h-20 tw-opacity-100" : "tw-max-h-0 tw-overflow-hidden tw-opacity-0"
|
||||
)}
|
||||
>
|
||||
<CardContent className="tw-p-3 tw-pt-0">
|
||||
<div className="tw-flex tw-justify-around">
|
||||
{!model.isEmbeddingModel && (
|
||||
<div className="tw-flex tw-items-center tw-gap-2">
|
||||
<span className="tw-text-sm">Enabled</span>
|
||||
<Checkbox
|
||||
checked={model.enabled}
|
||||
onCheckedChange={(checked: boolean) =>
|
||||
onUpdateModel({ ...model, enabled: checked })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="tw-flex tw-items-center tw-gap-2">
|
||||
<span className="tw-text-sm">CORS</span>
|
||||
<Checkbox
|
||||
checked={model.enableCors}
|
||||
onCheckedChange={(checked: boolean) =>
|
||||
onUpdateModel({ ...model, enableCors: checked })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</div>
|
||||
</Card>
|
||||
<MobileCard
|
||||
id={id}
|
||||
item={model}
|
||||
title={model.displayName || model.name}
|
||||
subtitle={getProviderLabel(model.provider, model)}
|
||||
badge={
|
||||
model.capabilities && model.capabilities.length > 0 ? (
|
||||
<ModelCapabilityIcons capabilities={model.capabilities} iconSize={14} />
|
||||
) : undefined
|
||||
}
|
||||
isDraggable={!model.core}
|
||||
isExpandable
|
||||
expandedContent={expandedContent}
|
||||
primaryAction={
|
||||
onEdit
|
||||
? {
|
||||
icon: <Pencil className="tw-size-4" />,
|
||||
onClick: onEdit,
|
||||
tooltip: "Edit Model",
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
dropdownActions={dropdownActions}
|
||||
containerRef={containerRef}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -309,12 +246,12 @@ const DesktopSortableTableRow: React.FC<{
|
|||
onDelete: (modelKey: string) => void;
|
||||
onUpdateModel: (model: CustomModel) => void;
|
||||
isEmbeddingModel: boolean;
|
||||
}> = ({ model, onEdit, onCopy, onDelete, onUpdateModel, isEmbeddingModel }) => {
|
||||
containerRef: React.RefObject<HTMLDivElement>;
|
||||
}> = ({ model, onEdit, onCopy, onDelete, onUpdateModel, isEmbeddingModel, containerRef }) => {
|
||||
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
|
||||
id: getModelKeyFromModel(model),
|
||||
disabled: model.core,
|
||||
});
|
||||
const container = useContainerContext();
|
||||
|
||||
const style = {
|
||||
transform: CSS.Transform.toString(transform),
|
||||
|
|
@ -387,7 +324,7 @@ const DesktopSortableTableRow: React.FC<{
|
|||
<MoreVertical className="tw-size-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" container={container}>
|
||||
<DropdownMenuContent align="end" container={containerRef.current}>
|
||||
{onEdit && (
|
||||
<DropdownMenuItem onClick={() => onEdit(model)}>
|
||||
<PencilLine className="tw-mr-2 tw-size-4" />
|
||||
|
|
@ -452,6 +389,8 @@ export const ModelTable: React.FC<ModelTableProps> = ({
|
|||
})
|
||||
);
|
||||
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Find the index of the first non-core model
|
||||
const firstDraggableIndex = models.findIndex((model) => !model.core);
|
||||
|
||||
|
|
@ -483,7 +422,7 @@ export const ModelTable: React.FC<ModelTableProps> = ({
|
|||
return {
|
||||
...transform,
|
||||
x: 0,
|
||||
y: currentIndex * rowHeight,
|
||||
y: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -529,16 +468,25 @@ export const ModelTable: React.FC<ModelTableProps> = ({
|
|||
collisionDetection={closestCenter}
|
||||
onDragEnd={handleDragEnd}
|
||||
modifiers={[createDragModifier(true)]}
|
||||
autoScroll={{
|
||||
enabled: true,
|
||||
acceleration: 10,
|
||||
threshold: {
|
||||
x: 0,
|
||||
y: 0.2,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<SortableContext
|
||||
items={models.map((model) => getModelKeyFromModel(model))}
|
||||
strategy={verticalListSortingStrategy}
|
||||
>
|
||||
<div className="tw-relative tw-space-y-2">
|
||||
<div className="tw-relative tw-touch-auto tw-space-y-2 tw-overflow-auto tw-pb-2">
|
||||
{models.map((model) => (
|
||||
<ModelCard
|
||||
key={getModelKeyFromModel(model)}
|
||||
id={getModelKeyFromModel(model)}
|
||||
containerRef={containerRef}
|
||||
model={model}
|
||||
onEdit={onEdit}
|
||||
onCopy={onCopy}
|
||||
|
|
@ -553,7 +501,8 @@ export const ModelTable: React.FC<ModelTableProps> = ({
|
|||
);
|
||||
|
||||
return (
|
||||
<div className="tw-mb-4">
|
||||
<div ref={containerRef} className="tw-mb-4">
|
||||
<ModelTableHeader title={title} onRefresh={onRefresh} onAdd={onAdd} />
|
||||
{/* Desktop view */}
|
||||
<div className="tw-hidden md:tw-block">
|
||||
<DndContext
|
||||
|
|
@ -583,6 +532,7 @@ export const ModelTable: React.FC<ModelTableProps> = ({
|
|||
{models.map((model) => (
|
||||
<DesktopSortableTableRow
|
||||
key={getModelKeyFromModel(model)}
|
||||
containerRef={containerRef}
|
||||
model={model}
|
||||
onEdit={onEdit}
|
||||
onCopy={onCopy}
|
||||
|
|
@ -600,23 +550,6 @@ export const ModelTable: React.FC<ModelTableProps> = ({
|
|||
|
||||
{/* Mobile view */}
|
||||
{renderMobileView()}
|
||||
|
||||
<div className="tw-mt-4 tw-flex tw-justify-end tw-gap-2">
|
||||
{onRefresh && (
|
||||
<Button
|
||||
onClick={onRefresh}
|
||||
variant="secondary"
|
||||
className="tw-flex tw-items-center tw-gap-2"
|
||||
>
|
||||
<RefreshCw className="tw-size-4" />
|
||||
Refresh Built-in Models
|
||||
</Button>
|
||||
)}
|
||||
<Button onClick={onAdd} variant="secondary" className="tw-flex tw-items-center tw-gap-2">
|
||||
<Plus className="tw-size-4" />
|
||||
Add Custom Model
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -31,8 +31,12 @@ export function PlusSettings() {
|
|||
<div className="tw-flex tw-flex-col tw-gap-2 tw-text-sm tw-text-muted">
|
||||
<div>
|
||||
Copilot Plus takes your Obsidian experience to the next level with cutting-edge AI
|
||||
capabilities. This premium tier unlocks advanced features, including chat context, PDF and
|
||||
image support, web search integration, exclusive chat and embedding models, and much more.
|
||||
capabilities. This premium tier unlocks advanced features:{" "}
|
||||
<strong>
|
||||
{" "}
|
||||
including chat context, PDF and image support, web search integration, exclusive chat
|
||||
and embedding models, and much more.
|
||||
</strong>
|
||||
</div>
|
||||
<div>
|
||||
Copilot Plus is evolving fast, with new features and improvements rolling out regularly.
|
||||
|
|
@ -62,12 +66,16 @@ export function PlusSettings() {
|
|||
new CopilotPlusWelcomeModal(app).open();
|
||||
}
|
||||
}}
|
||||
className="tw-min-w-20"
|
||||
className="tw-min-w-10 tw-text-xs md:tw-text-sm"
|
||||
>
|
||||
{isChecking ? <Loader2 className="tw-size-4 tw-animate-spin" /> : "Apply"}
|
||||
{isChecking ? <Loader2 className="tw-size-2 tw-animate-spin md:tw-size-4" /> : "Apply"}
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={() => navigateToPlusPage(PLUS_UTM_MEDIUMS.SETTINGS)}>
|
||||
Join Now <ExternalLink className="tw-size-4" />
|
||||
<Button
|
||||
className="tw-text-xs md:tw-text-sm"
|
||||
variant="secondary"
|
||||
onClick={() => navigateToPlusPage(PLUS_UTM_MEDIUMS.SETTINGS)}
|
||||
>
|
||||
Join Now <ExternalLink className="tw-size-2 md:tw-size-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="tw-text-error">{error}</div>
|
||||
|
|
|
|||
|
|
@ -4,10 +4,9 @@ import { SemanticSearchToggleModal } from "@/components/modals/SemanticSearchTog
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { getModelDisplayWithIcons } from "@/components/ui/model-display";
|
||||
import { SettingItem } from "@/components/ui/setting-item";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { HelpTooltip } from "@/components/ui/help-tooltip";
|
||||
import { VAULT_VECTOR_STORE_STRATEGIES } from "@/constants";
|
||||
import { getModelKeyFromModel, updateSetting, useSettingsValue } from "@/settings/model";
|
||||
import { HelpCircle } from "lucide-react";
|
||||
import { Notice } from "obsidian";
|
||||
import React from "react";
|
||||
|
||||
|
|
@ -83,25 +82,20 @@ export const QASettings: React.FC = () => {
|
|||
Powers Semantic Vault Search and Relevant Notes. Enable Semantic Search to use
|
||||
it.
|
||||
</span>
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="tw-size-4" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="tw-flex tw-max-w-96 tw-flex-col tw-gap-2">
|
||||
<div className="tw-pt-2 tw-text-sm tw-text-muted">
|
||||
This model converts text into vector representations, essential for
|
||||
semantic search and Question Answering (QA) functionality. Changing the
|
||||
embedding model will:
|
||||
</div>
|
||||
<ul className="tw-pl-4 tw-text-sm tw-text-muted">
|
||||
<li>Require rebuilding your vault's vector index</li>
|
||||
<li>Affect semantic search quality</li>
|
||||
<li>Impact Question Answering feature performance</li>
|
||||
</ul>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
<HelpTooltip
|
||||
content={<div className="tw-flex tw-max-w-96 tw-flex-col tw-gap-2">
|
||||
<div className="tw-pt-2 tw-text-sm tw-text-muted">
|
||||
This model converts text into vector representations, essential for
|
||||
semantic search and Question Answering (QA) functionality. Changing the
|
||||
embedding model will:
|
||||
</div>
|
||||
<ul className="tw-pl-4 tw-text-sm tw-text-muted">
|
||||
<li>Require rebuilding your vault's vector index</li>
|
||||
<li>Affect semantic search quality</li>
|
||||
<li>Impact Question Answering feature performance</li>
|
||||
</ul>
|
||||
</div>}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
|
@ -123,51 +117,46 @@ export const QASettings: React.FC = () => {
|
|||
<span className="tw-leading-none">
|
||||
Decide when you want the vault to be indexed.
|
||||
</span>
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="tw-size-4" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<div className="tw-space-y-2 tw-py-2">
|
||||
<div className="tw-space-y-1">
|
||||
<div className="tw-text-sm tw-text-muted">
|
||||
Choose when to index your vault:
|
||||
</div>
|
||||
<ul className="tw-list-disc tw-space-y-1 tw-pl-2 tw-text-sm">
|
||||
<li>
|
||||
<div className="tw-flex tw-items-center tw-gap-1">
|
||||
<strong className="tw-inline-block tw-whitespace-nowrap">
|
||||
NEVER:
|
||||
</strong>
|
||||
<span>Manual indexing via command or refresh only</span>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div className="tw-flex tw-items-center tw-gap-1">
|
||||
<strong className="tw-inline-block tw-whitespace-nowrap">
|
||||
ON STARTUP:
|
||||
</strong>
|
||||
<span>Index updates when plugin loads or reloads</span>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div className="tw-flex tw-items-center tw-gap-1">
|
||||
<strong className="tw-inline-block tw-whitespace-nowrap">
|
||||
ON MODE SWITCH:
|
||||
</strong>
|
||||
<span>Updates when entering QA mode (Recommended)</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<HelpTooltip
|
||||
content={
|
||||
<div className="tw-space-y-2 tw-py-2">
|
||||
<div className="tw-space-y-1">
|
||||
<div className="tw-text-sm tw-text-muted">
|
||||
Choose when to index your vault:
|
||||
</div>
|
||||
<p className="tw-text-sm tw-text-callout-warning">
|
||||
Warning: Cost implications for large vaults with paid models
|
||||
</p>
|
||||
<ul className="tw-list-disc tw-space-y-1 tw-pl-2 tw-text-sm">
|
||||
<li>
|
||||
<div className="tw-flex tw-items-center tw-gap-1">
|
||||
<strong className="tw-inline-block tw-whitespace-nowrap">
|
||||
NEVER:
|
||||
</strong>
|
||||
<span>Manual indexing via command or refresh only</span>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div className="tw-flex tw-items-center tw-gap-1">
|
||||
<strong className="tw-inline-block tw-whitespace-nowrap">
|
||||
ON STARTUP:
|
||||
</strong>
|
||||
<span>Index updates when plugin loads or reloads</span>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div className="tw-flex tw-items-center tw-gap-1">
|
||||
<strong className="tw-inline-block tw-whitespace-nowrap">
|
||||
ON MODE SWITCH:
|
||||
</strong>
|
||||
<span>Updates when entering QA mode (Recommended)</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
<p className="tw-text-sm tw-text-callout-warning">
|
||||
Warning: Cost implications for large vaults with paid models
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
value={settings.indexVaultToVectorStore}
|
||||
|
|
|
|||
|
|
@ -881,3 +881,85 @@ If your plugin does not need CSS, delete this file.
|
|||
.workspace-leaf-content[data-type="copilot-chat-view"] .view-content {
|
||||
padding-bottom: max(var(--safe-area-inset-bottom), var(--size-4-8)) !important;
|
||||
}
|
||||
|
||||
/* Collapsible sources styling */
|
||||
.copilot-sources {
|
||||
margin-top: var(--size-4-1);
|
||||
padding: calc(var(--size-4-2) / 2) var(--size-4-2);
|
||||
border-radius: var(--radius-s);
|
||||
border: 1px solid color-mix(in srgb, var(--background-modifier-border) 70%, transparent);
|
||||
background-color: color-mix(
|
||||
in srgb,
|
||||
var(--background-primary) 94%,
|
||||
var(--background-modifier-border) 6%
|
||||
);
|
||||
width: min(100%, 32rem);
|
||||
}
|
||||
|
||||
.copilot-sources__summary {
|
||||
font-size: var(--font-ui-smaller);
|
||||
color: color-mix(in srgb, var(--text-muted) 90%, transparent);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--size-4-2);
|
||||
padding: calc(var(--size-4-2) / 4) 0;
|
||||
padding-left: var(--size-4-2);
|
||||
padding-right: var(--size-4-2);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.copilot-sources__summary::-webkit-details-marker {
|
||||
color: color-mix(in srgb, var(--text-muted) 80%, transparent);
|
||||
}
|
||||
|
||||
.copilot-sources__summary::after {
|
||||
content: ">";
|
||||
font-size: var(--font-ui-smaller);
|
||||
color: color-mix(in srgb, var(--text-muted) 70%, transparent);
|
||||
transition: transform 0.15s ease;
|
||||
}
|
||||
|
||||
.copilot-sources[open] .copilot-sources__summary {
|
||||
margin-bottom: calc(var(--size-4-2) / 2);
|
||||
}
|
||||
|
||||
.copilot-sources[open] .copilot-sources__summary::after {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.copilot-sources__list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding-left: var(--size-4-2);
|
||||
padding-right: var(--size-4-2);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: calc(var(--size-4-2) / 2);
|
||||
font-size: var(--font-ui-smaller);
|
||||
color: color-mix(in srgb, var(--text-muted) 85%, transparent);
|
||||
}
|
||||
|
||||
.copilot-sources__item {
|
||||
display: flex;
|
||||
gap: var(--size-4-2);
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.copilot-sources__index {
|
||||
color: color-mix(in srgb, var(--text-muted) 65%, transparent);
|
||||
font-weight: var(--font-medium);
|
||||
min-width: var(--size-4-3);
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.copilot-sources__text {
|
||||
color: color-mix(in srgb, var(--text-muted) 90%, transparent);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.copilot-sources__text a {
|
||||
color: var(--text-accent);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,5 +92,6 @@
|
|||
"2.9.5": "0.15.0",
|
||||
"3.0.0": "0.15.0",
|
||||
"3.0.1": "0.15.0",
|
||||
"3.0.2": "0.15.0"
|
||||
"3.0.2": "0.15.0",
|
||||
"3.0.3": "0.15.0"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue