feat: 暂存.

This commit is contained in:
wyh 2025-08-29 14:01:23 +08:00
parent 2785b170d5
commit 8d7561d58c
8 changed files with 1225 additions and 202 deletions

685
package-lock.json generated
View file

@ -26,20 +26,20 @@
"@langchain/openai": "^0.6.6",
"@langchain/xai": "^0.0.2",
"@orama/orama": "^3.0.0-rc-2",
"@radix-ui/react-checkbox": "^1.1.3",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-collapsible": "^1.1.2",
"@radix-ui/react-dialog": "^1.1.3",
"@radix-ui/react-dropdown-menu": "^2.1.4",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.4",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-progress": "^1.1.7",
"@radix-ui/react-scroll-area": "^1.2.9",
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slider": "^1.3.5",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-switch": "^1.1.1",
"@radix-ui/react-tabs": "^1.1.3",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-tooltip": "^1.2.7",
"@tabler/icons-react": "^2.14.0",
"async-mutex": "^0.5.0",
@ -5023,19 +5023,19 @@
}
},
"node_modules/@radix-ui/react-checkbox": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.3.tgz",
"integrity": "sha512-HD7/ocp8f1B3e6OHygH0n7ZKjONkhciy1Nh0yuBgObqThc3oyx+vuMfFHKAknXRHHWVE9XvXStxJFyjUmB8PIw==",
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz",
"integrity": "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==",
"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-presence": "1.1.2",
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-use-controllable-state": "1.1.0",
"@radix-ui/react-use-previous": "1.1.0",
"@radix-ui/react-use-size": "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-presence": "1.1.5",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-controllable-state": "1.2.2",
"@radix-ui/react-use-previous": "1.1.1",
"@radix-ui/react-use-size": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
@ -5053,15 +5053,15 @@
}
},
"node_modules/@radix-ui/react-checkbox/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-checkbox/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": "*",
@ -5073,13 +5073,29 @@
}
}
},
"node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-primitive": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz",
"integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==",
"node_modules/@radix-ui/react-checkbox/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-checkbox/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-slot": "1.1.1"
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
@ -5096,13 +5112,85 @@
}
}
},
"node_modules/@radix-ui/react-checkbox/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-checkbox/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-compose-refs": "1.1.1"
"@radix-ui/react-slot": "1.2.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-checkbox/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-use-effect-event": "0.0.2",
"@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-checkbox/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-checkbox/node_modules/@radix-ui/react-use-previous": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz",
"integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==",
"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-checkbox/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": "*",
@ -5959,26 +6047,26 @@
}
},
"node_modules/@radix-ui/react-popover": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.4.tgz",
"integrity": "sha512-aUACAkXx8LaFymDma+HQVji7WhvEhpFJ7+qPz17Nf4lLZqtreGOFRiNQWQmhzp7kEWg9cOyyQJpdIMUMPc/CPw==",
"version": "1.1.15",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz",
"integrity": "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.1",
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-context": "1.1.1",
"@radix-ui/react-dismissable-layer": "1.1.3",
"@radix-ui/react-focus-guards": "1.1.1",
"@radix-ui/react-focus-scope": "1.1.1",
"@radix-ui/react-id": "1.1.0",
"@radix-ui/react-popper": "1.2.1",
"@radix-ui/react-portal": "1.1.3",
"@radix-ui/react-presence": "1.1.2",
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-slot": "1.1.1",
"@radix-ui/react-use-controllable-state": "1.1.0",
"aria-hidden": "^1.1.1",
"react-remove-scroll": "^2.6.1"
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@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-slot": "1.2.3",
"@radix-ui/react-use-controllable-state": "1.2.2",
"aria-hidden": "^1.2.4",
"react-remove-scroll": "^2.6.3"
},
"peerDependencies": {
"@types/react": "*",
@ -5996,18 +6084,18 @@
}
},
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/primitive": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz",
"integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==",
"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-popover/node_modules/@radix-ui/react-arrow": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.1.tgz",
"integrity": "sha512-NaVpZfmv8SKeZbn4ijN2V3jlHA9ngBG16VnIIm22nUR0Yk8KUALyBxT3KYEUnNuch9sTE8UTsS3whzBgKOL30w==",
"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": "*",
@ -6025,9 +6113,24 @@
}
},
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
"integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
"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-popover/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": "*",
@ -6040,16 +6143,16 @@
}
},
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-dismissable-layer": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.3.tgz",
"integrity": "sha512-onrWn/72lQoEucDmJnr8uczSNTujT0vJnA/X5+3AkChVPowr8n1yvIKIabhWyMQeMvvmdpsvcyDqx3X1LEXCPg==",
"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": "*",
@ -6066,15 +6169,30 @@
}
}
},
"node_modules/@radix-ui/react-popover/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-popover/node_modules/@radix-ui/react-focus-scope": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.1.tgz",
"integrity": "sha512-01omzJAYRxXdG2/he/+xy+c8a8gCydoQ1yOxnWNcRhrrBW5W+RQJ22EK1SaO8tb3WoUsuEw7mJjBozPzihDFjA==",
"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": "*",
@ -6091,22 +6209,40 @@
}
}
},
"node_modules/@radix-ui/react-popover/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-popover/node_modules/@radix-ui/react-popper": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.1.tgz",
"integrity": "sha512-3kn5Me69L+jv82EKRuQCXdYyf1DqHwD2U/sxoNgBGCB7K9TRc3bQamQ+5EPM9EvyPdli0W41sROd+ZU1dTCztw==",
"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": "*",
@ -6124,13 +6260,37 @@
}
},
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-portal": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.3.tgz",
"integrity": "sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw==",
"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-popover/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": "*",
@ -6148,12 +6308,12 @@
}
},
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-primitive": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz",
"integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==",
"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": "*",
@ -6170,13 +6330,29 @@
}
}
},
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-slot": {
"node_modules/@radix-ui/react-popover/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-popover/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": "*",
@ -6188,17 +6364,92 @@
}
}
},
"node_modules/@radix-ui/react-popover/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-use-callback-ref": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popover/node_modules/@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-popover/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-popover/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-popover/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-popover/node_modules/react-remove-scroll": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.2.tgz",
"integrity": "sha512-KmONPx5fnlXYJQqC62Q+lwIeAk64ws/cUw6omIumRzMRPqgnYqhSSti99nbj0Ry13bv7dF+BKn7NB+OqkdZGTw==",
"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"
@ -6542,17 +6793,17 @@
}
},
"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",
"integrity": "sha512-YSjEfBXnhUELsO2VzjdtYYD4CfQjvao+lhhrX5XsHD7/cyUNzljF1FHEbgTPN7LH2MClfwRMIsYlqTYpKTTe2A==",
"version": "1.2.10",
"resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.10.tgz",
"integrity": "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==",
"license": "MIT",
"dependencies": {
"@radix-ui/number": "1.1.1",
"@radix-ui/primitive": "1.1.2",
"@radix-ui/primitive": "1.1.3",
"@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-presence": "1.1.4",
"@radix-ui/react-presence": "1.1.5",
"@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"
@ -6579,9 +6830,9 @@
"license": "MIT"
},
"node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/primitive": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz",
"integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==",
"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-scroll-area/node_modules/@radix-ui/react-compose-refs": {
@ -6630,9 +6881,9 @@
}
},
"node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-presence": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz",
"integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==",
"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",
@ -7105,19 +7356,19 @@
}
},
"node_modules/@radix-ui/react-tabs": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.3.tgz",
"integrity": "sha512-9mFyI30cuRDImbmFF6O2KUJdgEOsGh9Vmx9x/Dh9tOhL7BngmQPQfwW4aejKm5OHpfWIdmeV6ySyuxoOGjtNng==",
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz",
"integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "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-presence": "1.1.2",
"@radix-ui/react-primitive": "2.0.2",
"@radix-ui/react-roving-focus": "1.1.2",
"@radix-ui/react-use-controllable-state": "1.1.0"
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-direction": "1.1.1",
"@radix-ui/react-id": "1.1.1",
"@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-use-controllable-state": "1.2.2"
},
"peerDependencies": {
"@types/react": "*",
@ -7135,21 +7386,21 @@
}
},
"node_modules/@radix-ui/react-tabs/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-tabs/node_modules/@radix-ui/react-collection": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.2.tgz",
"integrity": "sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==",
"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.2",
"@radix-ui/react-slot": "1.1.2"
"@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": "*",
@ -7167,9 +7418,9 @@
}
},
"node_modules/@radix-ui/react-tabs/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": "*",
@ -7181,13 +7432,85 @@
}
}
},
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-primitive": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz",
"integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==",
"node_modules/@radix-ui/react-tabs/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-tabs/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-tabs/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.2"
"@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-tabs/node_modules/@radix-ui/react-presence": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
"integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tabs/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": "*",
@ -7205,20 +7528,20 @@
}
},
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-roving-focus": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.2.tgz",
"integrity": "sha512-zgMQWkNO169GtGqRvYrzb0Zf8NhMHS2DuEB/TiEmVnpr5OqPU3i8lfbxaAmC2J/KYuIQxyoQQ6DxepyXp61/xw==",
"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.2",
"@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.2",
"@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": "*",
@ -7235,13 +7558,29 @@
}
}
},
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-slot": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz",
"integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==",
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.1.1",
"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-tabs/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": "*",
@ -7253,6 +7592,21 @@
}
}
},
"node_modules/@radix-ui/react-tabs/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-tooltip": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.7.tgz",
@ -21540,9 +21894,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 +21906,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": {

View file

@ -93,20 +93,20 @@
"@langchain/openai": "^0.6.6",
"@langchain/xai": "^0.0.2",
"@orama/orama": "^3.0.0-rc-2",
"@radix-ui/react-checkbox": "^1.1.3",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-collapsible": "^1.1.2",
"@radix-ui/react-dialog": "^1.1.3",
"@radix-ui/react-dropdown-menu": "^2.1.4",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.4",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-progress": "^1.1.7",
"@radix-ui/react-scroll-area": "^1.2.9",
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slider": "^1.3.5",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-switch": "^1.1.1",
"@radix-ui/react-tabs": "^1.1.3",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-tooltip": "^1.2.7",
"@tabler/icons-react": "^2.14.0",
"async-mutex": "^0.5.0",

View file

@ -0,0 +1,408 @@
import React, { useMemo, useState, useEffect } from "react";
import { App, TFile } from "obsidian";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Button } from "@/components/ui/button";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Checkbox } from "@/components/ui/checkbox";
import { ScrollArea } from "@/components/ui/scroll-area";
import { SearchBar } from "@/components/ui/SearchBar";
import { FileText, Folder, Info, Trash2, X } from "lucide-react";
import { ChainType } from "@/chainFactory";
import { TruncatedText } from "@/components/TruncatedText";
import { getFolders, getMarkdownFiles, getOtherFiles } from "@/utils/addContextUtils";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
interface AddContextModalProps {
app: App;
chainType: ChainType;
excludeNotePaths: string[];
activeNote: TFile | null;
contextNotes: TFile[]; // Currently selected notes
contextFolders: string[]; // Currently selected folders
onNoteSelect: (note: TFile | string) => void; // Support files and folders
isOpen: boolean;
onClose: () => void;
children: React.ReactNode;
}
export function AddContextModal({
app,
chainType,
excludeNotePaths,
activeNote,
contextNotes,
contextFolders,
onNoteSelect,
isOpen,
onClose,
children,
}: AddContextModalProps) {
const [searchQuery, setSearchQuery] = useState("");
const [activeTab, setActiveTab] = useState("markdown");
const [selectedFiles, setSelectedFiles] = useState<string[]>([]);
// Initialize selected files with current context when modal opens
useEffect(() => {
if (isOpen) {
const contextFilePaths = contextNotes.map((note) => note.path);
const initialSelected = [...contextFilePaths, ...contextFolders];
console.log("initialSelected", initialSelected);
setSelectedFiles(initialSelected);
}
}, [isOpen, contextNotes, contextFolders]);
const allFiles = useMemo(() => {
// Don't exclude context files - we want to show them as selected
const contextNotePaths = contextNotes.map((note) => note.path);
const currentExcludePaths = excludeNotePaths.filter((path) => !contextNotePaths.includes(path));
const markdownFiles = getMarkdownFiles(app, chainType, currentExcludePaths, activeNote);
const folders = getFolders(app);
const otherFiles = getOtherFiles(app, chainType, currentExcludePaths, activeNote);
return [...markdownFiles, ...folders, ...otherFiles];
}, [app, chainType, excludeNotePaths, activeNote, contextNotes]);
const filteredFiles = useMemo(() => {
let files = allFiles;
// Filter by type
files = files.filter((file) => {
if (activeTab === "markdown") {
return file.type === "markdown";
} else if (activeTab === "folder") {
return file.type === "folder";
} else if (activeTab === "other") {
return file.type === "other";
}
return false;
});
// Filter by search query
if (searchQuery) {
files = files.filter(
(file) =>
file.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
file.path.toLowerCase().includes(searchQuery.toLowerCase())
);
}
return files;
}, [allFiles, activeTab, searchQuery]);
const getFilteredCounts = useMemo(() => {
let searchFilteredFiles = allFiles;
// Filter only by search queries, not by type.
if (searchQuery) {
searchFilteredFiles = allFiles.filter(
(file) =>
file.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
file.path.toLowerCase().includes(searchQuery.toLowerCase())
);
}
return {
markdown: searchFilteredFiles.filter((f) => f.type === "markdown").length,
folder: searchFilteredFiles.filter((f) => f.type === "folder").length,
other: searchFilteredFiles.filter((f) => f.type === "other").length,
};
}, [allFiles, searchQuery]);
const getFileIcon = (type: string) => {
switch (type) {
case "markdown":
return <FileText className="tw-size-5" />;
case "folder":
return <Folder className="tw-size-5" />;
default:
return <FileText className="tw-size-5" />;
}
};
const toggleFileSelection = (fileId: string) => {
if (selectedFiles.includes(fileId)) {
setSelectedFiles(selectedFiles.filter((id) => id !== fileId));
} else {
setSelectedFiles([...selectedFiles, fileId]);
}
};
const clearAllSelections = () => {
// Remove all currently selected files and folders from context
selectedFiles.forEach((fileId) => {
// Check if it's a folder
if (contextFolders.includes(fileId)) {
// Remove folder from context
onNoteSelect(`REMOVE_FOLDER:${fileId}`);
} else {
// Remove file from context
onNoteSelect(`REMOVE_FILE:${fileId}`);
}
});
// Clear selection state and close modal
setSelectedFiles([]);
onClose();
};
// Processing file added to context
const handleAddToContext = () => {
const contextFilePaths = contextNotes.map((note) => note.path);
const initialSelected = [...contextFilePaths, ...contextFolders];
// Files/folders to add (newly selected)
const toAdd = selectedFiles.filter((fileId) => !initialSelected.includes(fileId));
// Files/folders to remove (previously selected but now unselected)
const toRemove = initialSelected.filter((fileId) => !selectedFiles.includes(fileId));
// Add new selections
toAdd.forEach((fileId) => {
const file = allFiles.find((f) => f.id === fileId);
if (file) {
if (file.type === "folder") {
// For folders, pass the folder path
onNoteSelect(file.path);
} else {
// For files, pass the TFile
const tFile = app.vault.getAbstractFileByPath(file.path);
if (tFile instanceof TFile) {
onNoteSelect(tFile);
}
}
}
});
// Remove unselected items by calling their respective remove handlers
toRemove.forEach((fileId) => {
// Check if it's a folder
if (contextFolders.includes(fileId)) {
// This is a folder - we need a way to remove it
// For now, we'll call onNoteSelect with a special format to indicate removal
onNoteSelect(`REMOVE_FOLDER:${fileId}`);
} else {
// This is a file - we need a way to remove it
onNoteSelect(`REMOVE_FILE:${fileId}`);
}
});
setSelectedFiles([]);
onClose();
};
// Get statistical information
/*
const getStats = () => {
const markdownCount = selectedFiles.filter(
(id) => allFiles.find((f) => f.id === id)?.type === "markdown"
).length;
const folderCount = selectedFiles.filter(
(id) => allFiles.find((f) => f.id === id)?.type === "folder"
).length;
const otherCount = selectedFiles.filter(
(id) => allFiles.find((f) => f.id === id)?.type === "other"
).length;
return { markdownCount, folderCount, otherCount };
};
*/
/* const stats = getStats();*/
return (
<Popover open={isOpen} onOpenChange={onClose}>
<PopoverTrigger asChild>{children}</PopoverTrigger>
<PopoverContent className="tw-h-[380px] tw-w-[400px] tw-p-0" side="bottom" align="center">
<div className="tw-flex tw-h-full tw-flex-col">
<div className="tw-flex tw-items-center tw-justify-between tw-border-x-[0] tw-border-b tw-border-t-[0] tw-border-solid tw-border-border tw-p-2">
<div className="tw-text-base tw-font-semibold">Add Context</div>
{selectedFiles.length > 0 && (
<Button
variant="secondary"
size="sm"
onClick={clearAllSelections}
className="tw-h-6 tw-bg-transparent tw-px-2 tw-text-xs !tw-text-error hover:tw-text-error"
>
<Trash2 className="tw-mr-1 tw-size-3" />
Clear All ({selectedFiles.length})
</Button>
)}
</div>
<div className="tw-flex tw-min-h-0 tw-flex-1 tw-flex-col tw-space-y-1.5 tw-p-2">
<div className="tw-text-xs">
<SearchBar
value={searchQuery}
onChange={setSearchQuery}
placeholder="Search files..."
/>
</div>
{/* 选择统计 */}
{/*
{selectedFiles.length > 0 && (
<div className="tw-bg-muted tw-flex tw-shrink-0 tw-items-center tw-gap-1 tw-rounded-lg tw-p-1.5">
<span className="tw-text-xs tw-font-medium">Selected:</span>
{stats.markdownCount > 0 && (
<Badge
variant="secondary"
className="tw-bg-blue-100 tw-text-blue-800 dark:tw-bg-blue-900 dark:tw-text-blue-200 tw-h-4 tw-px-1 tw-py-0 tw-text-xs"
>
{stats.markdownCount} MD
</Badge>
)}
{stats.folderCount > 0 && (
<Badge
variant="secondary"
className="tw-bg-yellow-100 tw-text-yellow-800 dark:tw-bg-yellow-900 dark:tw-text-yellow-200 tw-h-4 tw-px-1 tw-py-0 tw-text-xs"
>
{stats.folderCount} Folders
</Badge>
)}
{stats.otherCount > 0 && (
<Badge
variant="secondary"
className="tw-bg-gray-100 tw-text-gray-800 dark:tw-bg-gray-800 dark:tw-text-gray-200 tw-h-4 tw-px-1 tw-py-0 tw-text-xs"
>
{stats.otherCount} Other
</Badge>
)}
</div>
)}
*/}
<Tabs
value={activeTab}
onValueChange={setActiveTab}
className="tw-flex tw-min-h-0 tw-flex-1 tw-flex-col"
>
<TabsList className="tw-grid tw-w-full tw-shrink-0 tw-grid-cols-3 tw-border tw-border-solid tw-border-border">
<TabsTrigger value="markdown" className="tw-px-1 tw-text-xs">
<FileText className="tw-mr-1 tw-size-3" />
MD ({getFilteredCounts.markdown})
</TabsTrigger>
<TabsTrigger value="folder" className="tw-px-1 tw-text-xs">
<div className="tw-flex tw-items-center tw-text-center">
<Folder className="tw-mr-1 tw-size-3" />
Folder ({getFilteredCounts.folder})
<Tooltip>
<TooltipTrigger asChild>
<span className="tw-flex tw-items-center tw-text-center">
&nbsp;
<Info className="tw-size-3" />
</span>
</TooltipTrigger>
<TooltipContent side="bottom" className="tw-w-64">
Adding folders will only include files in the current folder
</TooltipContent>
</Tooltip>
</div>
</TabsTrigger>
<TabsTrigger value="other" className="tw-px-1 tw-text-xs">
<FileText className="tw-mr-1 tw-size-3" />
Other ({getFilteredCounts.other})
</TabsTrigger>
</TabsList>
<TabsContent value={activeTab} className="tw-mt-1.5 tw-min-h-0 tw-flex-1">
<ScrollArea className="tw-h-full tw-pr-1">
<div className="tw-space-y-1">
{filteredFiles.length === 0 ? (
<div className="tw-py-4 tw-text-center tw-text-xs tw-text-muted">
{searchQuery
? "No files found matching your search."
: "No files available."}
</div>
) : (
filteredFiles.map((file) => {
const isSelected = selectedFiles.includes(file.id);
return (
<div
key={file.id}
className={`tw-flex tw-cursor-pointer tw-items-center tw-space-x-1.5 tw-rounded-md tw-border tw-border-solid tw-p-1.5 tw-transition-colors ${
isSelected
? "tw-border-interactive-accent tw-bg-interactive-accent-hover/10 hover:tw-bg-modifier-hover"
: "tw-border-border hover:tw-bg-modifier-hover"
}`}
onClick={() => toggleFileSelection(file.id)}
>
<Checkbox
checked={isSelected}
onCheckedChange={() => toggleFileSelection(file.id)}
onClick={(e) => e.stopPropagation()}
className="tw-size-3"
/>
<div className="tw-flex tw-min-w-0 tw-flex-1 tw-items-center tw-space-x-1.5">
<div className="tw-shrink-0">{getFileIcon(file.type)}</div>
<div className="tw-min-w-0 tw-flex-1">
<div className="tw-flex tw-items-center tw-space-x-1.5">
<TruncatedText className="tw-flex-1 tw-text-xs tw-font-medium">
{file.name}
{file.isActive && (
<span className="tw-ml-1 tw-text-xs tw-text-faint">
(Current)
</span>
)}
{file.extension === "pdf" && (
<span className="tw-ml-1 tw-text-xs tw-text-faint">
(PDF)
</span>
)}
{file.extension === "canvas" && (
<span className="tw-ml-1 tw-text-xs tw-text-faint">
(Canvas)
</span>
)}
</TruncatedText>
</div>
<TruncatedText className="tw-flex-1 tw-text-xs tw-text-muted">
{file.path}
</TruncatedText>
</div>
</div>
{isSelected && (
<div className="tw-shrink-0 tw-text-normal">
<X className="tw-size-3" />
</div>
)}
</div>
);
})
)}
</div>
</ScrollArea>
</TabsContent>
</Tabs>
</div>
<div className="tw-flex tw-shrink-0 tw-items-center tw-justify-between tw-border-t tw-p-2">
<div className="tw-text-xs tw-text-muted">
{selectedFiles.length} file{selectedFiles.length !== 1 ? "s" : ""} selected
</div>
<div className="tw-flex tw-space-x-1.5">
<Button
variant="secondary"
onClick={onClose}
size="sm"
className="tw-h-6 tw-bg-transparent tw-px-2 tw-text-xs"
>
Cancel
</Button>
<Button
onClick={handleAddToContext}
disabled={selectedFiles.length === 0}
size="sm"
className="tw-h-6 tw-px-2 tw-text-xs"
>
Add to Context
</Button>
</div>
</div>
</div>
</PopoverContent>
</Popover>
);
}

View file

@ -36,6 +36,7 @@ import { updateSetting, useSettingsValue } from "@/settings/model";
import { ChatUIState } from "@/state/ChatUIState";
import { FileParserManager } from "@/tools/FileParserManager";
import { err2String, isPlusChain } from "@/utils";
import { getFilesInFolder } from "@/utils/addContextUtils";
import { Buffer } from "buffer";
import { Notice, TFile } from "obsidian";
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
@ -78,6 +79,7 @@ const Chat: React.FC<ChatProps> = ({
const [loading, setLoading] = useState(false);
const [loadingMessage, setLoadingMessage] = useState(LOADING_MESSAGES.DEFAULT);
const [contextNotes, setContextNotes] = useState<TFile[]>([]);
const [contextFolders, setContextFolders] = useState<string[]>([]);
const [includeActiveNote, setIncludeActiveNote] = useState(false);
const [selectedImages, setSelectedImages] = useState<File[]>([]);
const [showChatUI, setShowChatUI] = useState(false);
@ -192,7 +194,15 @@ const Chat: React.FC<ChatProps> = ({
}
// Prepare context notes and deduplicate by path
const allNotes = [...(passedContextNotes || []), ...contextNotes];
// Get files from context folders
const folderFiles: TFile[] = [];
contextFolders.forEach((folderPath) => {
const filesInFolder = getFilesInFolder(app, folderPath, currentChain);
folderFiles.push(...filesInFolder);
});
const allNotes = [...(passedContextNotes || []), ...contextNotes, ...folderFiles];
const notes = allNotes.filter(
(note, index, array) => array.findIndex((n) => n.path === note.path) === index
);
@ -549,6 +559,7 @@ const Chat: React.FC<ChatProps> = ({
// Additional UI state reset specific to this component
safeSet.setCurrentAiMessage("");
setContextNotes([]);
setContextFolders([]);
clearSelectedTextContexts();
// Only modify includeActiveNote if in a non-COPILOT_PLUS_CHAIN mode
// In COPILOT_PLUS_CHAIN mode, respect the settings.includeActiveNoteAsContext value
@ -652,6 +663,8 @@ const Chat: React.FC<ChatProps> = ({
app={app}
contextNotes={contextNotes}
setContextNotes={setContextNotes}
contextFolders={contextFolders}
setContextFolders={setContextFolders}
includeActiveNote={includeActiveNote}
setIncludeActiveNote={setIncludeActiveNote}
mention={mention}

View file

@ -1,4 +1,4 @@
import { AlertCircle, CheckCircle, CircleDashed, Loader2, Plus, X } from "lucide-react";
import { AlertCircle, CheckCircle, CircleDashed, Loader2, Plus, X, Folder } from "lucide-react";
import { TFile } from "obsidian";
import React from "react";
import { Button } from "@/components/ui/button";
@ -12,10 +12,12 @@ import { useProjectContextStatus } from "@/hooks/useProjectContextStatus";
interface ChatContextMenuProps {
activeNote: TFile | null;
contextNotes: TFile[];
contextFolders: string[];
contextUrls: string[];
selectedTextContexts?: SelectedTextContext[];
onAddContext: () => void;
onRemoveContext: (path: string) => void;
onRemoveFolder: (path: string) => void;
onRemoveUrl: (url: string) => void;
onRemoveSelectedText?: (id: string) => void;
showProgressCard: () => void;
@ -70,6 +72,33 @@ function ContextUrl({ url, onRemoveUrl }: { url: string; onRemoveUrl: (url: stri
);
}
function ContextFolder({
folderPath,
onRemoveFolder,
}: {
folderPath: string;
onRemoveFolder: (path: string) => void;
}) {
const folderName = folderPath.split("/").pop() || folderPath;
return (
<Badge className="tw-items-center tw-py-0 tw-pl-2 tw-pr-0.5 tw-text-xs">
<div className="tw-flex tw-items-center tw-gap-1">
<Folder className="tw-size-3" />
<span className="tw-max-w-40 tw-truncate">{folderName}</span>
</div>
<Button
variant="ghost2"
size="fit"
onClick={() => onRemoveFolder(folderPath)}
aria-label="Remove folder from context"
className="tw-text-muted"
>
<X className="tw-size-4" />
</Button>
</Badge>
);
}
function ContextSelection({
selectedText,
onRemoveSelectedText,
@ -104,10 +133,12 @@ function ContextSelection({
export const ChatContextMenu: React.FC<ChatContextMenuProps> = ({
activeNote,
contextNotes,
contextFolders,
contextUrls,
selectedTextContexts = [],
onAddContext,
onRemoveContext,
onRemoveFolder,
onRemoveUrl,
onRemoveSelectedText,
showProgressCard,
@ -133,6 +164,7 @@ export const ChatContextMenu: React.FC<ChatContextMenuProps> = ({
const hasContext =
uniqueNotes.length > 0 ||
contextFolders.length > 0 ||
uniqueUrls.length > 0 ||
selectedTextContexts.length > 0 ||
!!activeNote;
@ -181,6 +213,9 @@ export const ChatContextMenu: React.FC<ChatContextMenuProps> = ({
onRemoveContext={onRemoveContext}
/>
))}
{contextFolders.map((folderPath) => (
<ContextFolder key={folderPath} folderPath={folderPath} onRemoveFolder={onRemoveFolder} />
))}
{uniqueUrls.map((url) => (
<ContextUrl key={url} url={url} onRemoveUrl={onRemoveUrl} />
))}

View file

@ -64,6 +64,8 @@ interface ChatInputProps {
app: App;
contextNotes: TFile[];
setContextNotes: React.Dispatch<React.SetStateAction<TFile[]>>;
contextFolders: string[];
setContextFolders: React.Dispatch<React.SetStateAction<string[]>>;
includeActiveNote: boolean;
setIncludeActiveNote: (include: boolean) => void;
mention: Mention;
@ -87,6 +89,8 @@ const ChatInput = forwardRef<{ focus: () => void }, ChatInputProps>(
app,
contextNotes,
setContextNotes,
contextFolders,
setContextFolders,
includeActiveNote,
setIncludeActiveNote,
mention,
@ -520,6 +524,8 @@ const ChatInput = forwardRef<{ focus: () => void }, ChatInputProps>(
excludeNotePaths={excludeNotePaths}
contextNotes={contextNotes}
setContextNotes={setContextNotes}
contextFolders={contextFolders}
setContextFolders={setContextFolders}
includeActiveNote={includeActiveNote}
setIncludeActiveNote={setIncludeActiveNote}
activeNote={currentActiveNote}

View file

@ -1,10 +1,8 @@
import { App } from "obsidian";
import React from "react";
import { App, TFile } from "obsidian";
import React, { useState } from "react";
import { useChainType } from "@/aiParams";
import { AddContextNoteModal } from "@/components/modals/AddContextNoteModal";
import { AddContextModal } from "@/components/AddContextModal";
import { SelectedTextContext } from "@/types/message";
import { TFile } from "obsidian";
import { ChatContextMenu } from "./ChatContextMenu";
interface ChatControlsProps {
@ -12,6 +10,8 @@ interface ChatControlsProps {
excludeNotePaths: string[];
contextNotes: TFile[];
setContextNotes: React.Dispatch<React.SetStateAction<TFile[]>>;
contextFolders: string[];
setContextFolders: React.Dispatch<React.SetStateAction<string[]>>;
includeActiveNote: boolean;
setIncludeActiveNote: React.Dispatch<React.SetStateAction<boolean>>;
activeNote: TFile | null;
@ -27,6 +27,8 @@ const ContextControl: React.FC<ChatControlsProps> = ({
excludeNotePaths,
contextNotes,
setContextNotes,
contextFolders,
setContextFolders,
includeActiveNote,
setIncludeActiveNote,
activeNote,
@ -37,22 +39,42 @@ const ContextControl: React.FC<ChatControlsProps> = ({
showProgressCard,
}) => {
const [selectedChain] = useChainType();
const [isAddContextModalOpen, setIsAddContextModalOpen] = useState(false);
const handleAddContext = () => {
new AddContextNoteModal({
app,
onNoteSelect: (note) => {
if (activeNote && note.path === activeNote.path) {
setIncludeActiveNote(true);
// Remove the note from contextNotes if it exists there
setContextNotes((prev) => prev.filter((n) => n.path !== note.path));
} else {
// Add wasAddedManually flag to distinguish from reference-added notes
setContextNotes((prev) => [...prev, Object.assign(note, { wasAddedManually: true })]);
}
},
excludeNotePaths,
chainType: selectedChain,
}).open();
setIsAddContextModalOpen(true);
};
// Callback for handling file/folder selection
const handleNoteSelect = (noteOrPath: TFile | string) => {
if (typeof noteOrPath === "string") {
// Handle special removal commands from AddContextModal
if (noteOrPath.startsWith("REMOVE_FOLDER:")) {
const folderPath = noteOrPath.replace("REMOVE_FOLDER:", "");
handleRemoveFolder(folderPath);
return;
} else if (noteOrPath.startsWith("REMOVE_FILE:")) {
const filePath = noteOrPath.replace("REMOVE_FILE:", "");
handleRemoveContext(filePath);
return;
}
// Processing folder paths
const folderPath = noteOrPath;
if (!contextFolders.includes(folderPath)) {
setContextFolders((prev) => [...prev, folderPath]);
}
} else {
// Processing file
const note = noteOrPath;
if (activeNote && note.path === activeNote.path) {
setIncludeActiveNote(true);
// Remove the note from contextNotes if it exists there
setContextNotes((prev) => prev.filter((n) => n.path !== note.path));
} else {
// Add wasAddedManually flag to distinguish from reference-added notes
setContextNotes((prev) => [...prev, Object.assign(note, { wasAddedManually: true })]);
}
}
};
const handleRemoveContext = (path: string) => {
@ -73,20 +95,41 @@ const ContextControl: React.FC<ChatControlsProps> = ({
}
};
const handleRemoveFolder = (folderPath: string) => {
setContextFolders((prev) => prev.filter((path) => path !== folderPath));
};
// Context menu is now available for all chain types
return (
<ChatContextMenu
activeNote={includeActiveNote ? activeNote : null}
contextNotes={contextNotes}
onAddContext={handleAddContext}
onRemoveContext={handleRemoveContext}
contextUrls={contextUrls}
onRemoveUrl={onRemoveUrl}
selectedTextContexts={selectedTextContexts}
onRemoveSelectedText={onRemoveSelectedText}
showProgressCard={showProgressCard}
/>
<>
<ChatContextMenu
activeNote={includeActiveNote ? activeNote : null}
contextNotes={contextNotes}
contextFolders={contextFolders}
onAddContext={handleAddContext}
onRemoveContext={handleRemoveContext}
onRemoveFolder={handleRemoveFolder}
contextUrls={contextUrls}
onRemoveUrl={onRemoveUrl}
selectedTextContexts={selectedTextContexts}
onRemoveSelectedText={onRemoveSelectedText}
showProgressCard={showProgressCard}
/>
<AddContextModal
app={app}
chainType={selectedChain}
excludeNotePaths={excludeNotePaths}
activeNote={activeNote}
contextNotes={contextNotes}
contextFolders={contextFolders}
onNoteSelect={handleNoteSelect}
isOpen={isAddContextModalOpen}
onClose={() => setIsAddContextModalOpen(false)}
>
<div />
</AddContextModal>
</>
);
};

View file

@ -0,0 +1,163 @@
import { ChainType } from "@/chainFactory";
import { App, TFile } from "obsidian";
import { isAllowedFileForChainContext } from "@/utils";
import { extractAppIgnoreSettings, shouldIndexFile } from "@/search/searchUtils";
interface FileItem {
id: string;
name: string;
type: "markdown" | "folder" | "other";
path: string;
extension?: string;
isActive?: boolean;
}
export function getMarkdownFiles(
app: App,
chainType: ChainType,
excludeNotePaths: string[],
activeNote: TFile | null
): FileItem[] {
const recentFiles = app.workspace
.getLastOpenFiles()
.map((filePath) => app.vault.getAbstractFileByPath(filePath))
.filter(
(file): file is TFile =>
file instanceof TFile &&
(file.extension === "md" || file.extension === "canvas") &&
isAllowedFileForChainContext(file, chainType) &&
!excludeNotePaths.includes(file.path) &&
file.path !== activeNote?.path
);
// Get all other files
const allFiles = app.vault
.getFiles()
.filter(
(file) =>
(file.extension === "md" || file.extension === "canvas") &&
isAllowedFileForChainContext(file, chainType)
);
const otherFiles = allFiles.filter(
(file) =>
!recentFiles.some((recent) => recent.path === file.path) &&
!excludeNotePaths.includes(file.path) &&
file.path !== activeNote?.path
);
// Combine active files (if they exist and are permitted)
const activeNoteArray =
activeNote &&
(activeNote.extension === "md" || activeNote.extension === "canvas") &&
isAllowedFileForChainContext(activeNote, chainType)
? [activeNote]
: [];
const allOrderedFiles = [...activeNoteArray, ...recentFiles, ...otherFiles];
return allOrderedFiles.map((file, index) => ({
id: file.path,
name: file.basename,
type: "markdown",
path: file.path,
extension: file.extension,
isActive: file.path === activeNote?.path,
}));
}
export function getFolders(app: App): FileItem[] {
const folderSet = new Set<string>();
const ignoredFolders = extractAppIgnoreSettings(app);
// Get all loaded files
app.vault.getAllLoadedFiles().forEach((file) => {
if (file.parent?.path && file.parent.path !== "/") {
// Check if the folder is ignored
const shouldInclude = !ignoredFolders.some(
(ignored) => file.parent!.path === ignored || file.parent!.path.startsWith(ignored + "/")
);
if (shouldInclude) {
folderSet.add(file.parent.path);
}
}
});
return Array.from(folderSet).map((folderPath) => ({
id: folderPath,
name: folderPath.split("/").pop() || folderPath,
type: "folder",
path: folderPath,
}));
}
export function getOtherFiles(
app: App,
chainType: ChainType,
excludeNotePaths: string[],
activeNote: TFile | null
): FileItem[] {
console.log(excludeNotePaths);
const allFiles = app.vault
.getFiles()
.filter(
(file) =>
file.extension !== "md" &&
file.extension !== "canvas" &&
isAllowedFileForChainContext(file, chainType) &&
!excludeNotePaths.includes(file.path) &&
file.path !== activeNote?.path
);
return allFiles.map((file) => ({
id: file.path,
name: file.basename,
type: "other",
path: file.path,
extension: file.extension,
isActive: false,
}));
}
/**
* Get all files in a specific folder path
* @param app - Obsidian App instance
* @param folderPath - The folder path to search in
* @param chainType - Optional chain type for permission filtering
* @param excludeNotePaths - Optional array of file paths to exclude
* @returns Array of FileItem objects representing files in the folder
*/
export function getFilesInFolder(
app: App,
folderPath: string,
chainType?: ChainType,
excludeNotePaths: string[] = []
): TFile[] {
// Construct PatternCategory object for folder pattern
const folderPattern = {
folderPatterns: [folderPath],
};
// Get all files and apply filtering
const allFiles = app.vault.getFiles().filter((file) => {
// Check if file is in the specified folder
const isInFolder = shouldIndexFile(file, folderPattern, null, true);
if (!isInFolder) return false;
// Check file permissions (if chainType is provided)
if (chainType && !isAllowedFileForChainContext(file, chainType)) {
return false;
}
// Exclude specified file paths
if (excludeNotePaths.includes(file.path)) {
return false;
}
return true;
});
// Convert to FileItem format
return allFiles;
}