Spaces:
Running
Running
قم بتحسين ترتيب المنشورات مثل فيسبوك وهكذا
Browse files- README.md +1 -1
- package-lock.json +384 -20
- package.json +5 -5
- src/ai/flows/ai-text-to-speech.ts +1 -0
- src/components/ai-world/ai-world-feed.tsx +4 -2
- src/contexts/ai-world-context.tsx +41 -15
- src/lib/gemini-client.ts +36 -35
README.md
CHANGED
|
@@ -5,4 +5,4 @@ colorFrom: blue
|
|
| 5 |
colorTo: indigo
|
| 6 |
sdk: docker
|
| 7 |
pinned: false
|
| 8 |
-
---
|
|
|
|
| 5 |
colorTo: indigo
|
| 6 |
sdk: docker
|
| 7 |
pinned: false
|
| 8 |
+
----
|
package-lock.json
CHANGED
|
@@ -11,6 +11,7 @@
|
|
| 11 |
"@genkit-ai/google-genai": "^1.0.0",
|
| 12 |
"@hookform/resolvers": "^4.1.3",
|
| 13 |
"@huggingface/inference": "^2.8.1",
|
|
|
|
| 14 |
"@radix-ui/react-avatar": "^1.1.3",
|
| 15 |
"@radix-ui/react-dialog": "^1.1.6",
|
| 16 |
"@radix-ui/react-dropdown-menu": "^2.1.6",
|
|
@@ -3595,6 +3596,93 @@
|
|
| 3595 |
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz",
|
| 3596 |
"integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="
|
| 3597 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3598 |
"node_modules/@radix-ui/react-arrow": {
|
| 3599 |
"version": "1.1.2",
|
| 3600 |
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.2.tgz",
|
|
@@ -3713,22 +3801,23 @@
|
|
| 3713 |
}
|
| 3714 |
},
|
| 3715 |
"node_modules/@radix-ui/react-dialog": {
|
| 3716 |
-
"version": "1.1.
|
| 3717 |
-
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.
|
| 3718 |
-
"integrity": "sha512-
|
| 3719 |
-
"
|
| 3720 |
-
|
| 3721 |
-
"@radix-ui/
|
| 3722 |
-
"@radix-ui/react-
|
| 3723 |
-
"@radix-ui/react-
|
| 3724 |
-
"@radix-ui/react-
|
| 3725 |
-
"@radix-ui/react-focus-
|
| 3726 |
-
"@radix-ui/react-
|
| 3727 |
-
"@radix-ui/react-
|
| 3728 |
-
"@radix-ui/react-
|
| 3729 |
-
"@radix-ui/react-
|
| 3730 |
-
"@radix-ui/react-
|
| 3731 |
-
"@radix-ui/react-
|
|
|
|
| 3732 |
"aria-hidden": "^1.2.4",
|
| 3733 |
"react-remove-scroll": "^2.6.3"
|
| 3734 |
},
|
|
@@ -3747,13 +3836,255 @@
|
|
| 3747 |
}
|
| 3748 |
}
|
| 3749 |
},
|
| 3750 |
-
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3751 |
"version": "1.1.2",
|
| 3752 |
-
"resolved": "https://registry.npmjs.org/@radix-ui/react-
|
| 3753 |
-
"integrity": "sha512-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3754 |
"dependencies": {
|
| 3755 |
-
"@radix-ui/
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3756 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3757 |
"peerDependencies": {
|
| 3758 |
"@types/react": "*",
|
| 3759 |
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
|
@@ -4472,6 +4803,39 @@
|
|
| 4472 |
}
|
| 4473 |
}
|
| 4474 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4475 |
"node_modules/@radix-ui/react-use-escape-keydown": {
|
| 4476 |
"version": "1.1.0",
|
| 4477 |
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz",
|
|
|
|
| 11 |
"@genkit-ai/google-genai": "^1.0.0",
|
| 12 |
"@hookform/resolvers": "^4.1.3",
|
| 13 |
"@huggingface/inference": "^2.8.1",
|
| 14 |
+
"@radix-ui/react-alert-dialog": "^1.1.15",
|
| 15 |
"@radix-ui/react-avatar": "^1.1.3",
|
| 16 |
"@radix-ui/react-dialog": "^1.1.6",
|
| 17 |
"@radix-ui/react-dropdown-menu": "^2.1.6",
|
|
|
|
| 3596 |
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz",
|
| 3597 |
"integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="
|
| 3598 |
},
|
| 3599 |
+
"node_modules/@radix-ui/react-alert-dialog": {
|
| 3600 |
+
"version": "1.1.15",
|
| 3601 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.15.tgz",
|
| 3602 |
+
"integrity": "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==",
|
| 3603 |
+
"license": "MIT",
|
| 3604 |
+
"dependencies": {
|
| 3605 |
+
"@radix-ui/primitive": "1.1.3",
|
| 3606 |
+
"@radix-ui/react-compose-refs": "1.1.2",
|
| 3607 |
+
"@radix-ui/react-context": "1.1.2",
|
| 3608 |
+
"@radix-ui/react-dialog": "1.1.15",
|
| 3609 |
+
"@radix-ui/react-primitive": "2.1.3",
|
| 3610 |
+
"@radix-ui/react-slot": "1.2.3"
|
| 3611 |
+
},
|
| 3612 |
+
"peerDependencies": {
|
| 3613 |
+
"@types/react": "*",
|
| 3614 |
+
"@types/react-dom": "*",
|
| 3615 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 3616 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 3617 |
+
},
|
| 3618 |
+
"peerDependenciesMeta": {
|
| 3619 |
+
"@types/react": {
|
| 3620 |
+
"optional": true
|
| 3621 |
+
},
|
| 3622 |
+
"@types/react-dom": {
|
| 3623 |
+
"optional": true
|
| 3624 |
+
}
|
| 3625 |
+
}
|
| 3626 |
+
},
|
| 3627 |
+
"node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/primitive": {
|
| 3628 |
+
"version": "1.1.3",
|
| 3629 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
|
| 3630 |
+
"integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
|
| 3631 |
+
"license": "MIT"
|
| 3632 |
+
},
|
| 3633 |
+
"node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-compose-refs": {
|
| 3634 |
+
"version": "1.1.2",
|
| 3635 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
| 3636 |
+
"integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
|
| 3637 |
+
"license": "MIT",
|
| 3638 |
+
"peerDependencies": {
|
| 3639 |
+
"@types/react": "*",
|
| 3640 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 3641 |
+
},
|
| 3642 |
+
"peerDependenciesMeta": {
|
| 3643 |
+
"@types/react": {
|
| 3644 |
+
"optional": true
|
| 3645 |
+
}
|
| 3646 |
+
}
|
| 3647 |
+
},
|
| 3648 |
+
"node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-context": {
|
| 3649 |
+
"version": "1.1.2",
|
| 3650 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
|
| 3651 |
+
"integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
|
| 3652 |
+
"license": "MIT",
|
| 3653 |
+
"peerDependencies": {
|
| 3654 |
+
"@types/react": "*",
|
| 3655 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 3656 |
+
},
|
| 3657 |
+
"peerDependenciesMeta": {
|
| 3658 |
+
"@types/react": {
|
| 3659 |
+
"optional": true
|
| 3660 |
+
}
|
| 3661 |
+
}
|
| 3662 |
+
},
|
| 3663 |
+
"node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-primitive": {
|
| 3664 |
+
"version": "2.1.3",
|
| 3665 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
| 3666 |
+
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
| 3667 |
+
"license": "MIT",
|
| 3668 |
+
"dependencies": {
|
| 3669 |
+
"@radix-ui/react-slot": "1.2.3"
|
| 3670 |
+
},
|
| 3671 |
+
"peerDependencies": {
|
| 3672 |
+
"@types/react": "*",
|
| 3673 |
+
"@types/react-dom": "*",
|
| 3674 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 3675 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 3676 |
+
},
|
| 3677 |
+
"peerDependenciesMeta": {
|
| 3678 |
+
"@types/react": {
|
| 3679 |
+
"optional": true
|
| 3680 |
+
},
|
| 3681 |
+
"@types/react-dom": {
|
| 3682 |
+
"optional": true
|
| 3683 |
+
}
|
| 3684 |
+
}
|
| 3685 |
+
},
|
| 3686 |
"node_modules/@radix-ui/react-arrow": {
|
| 3687 |
"version": "1.1.2",
|
| 3688 |
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.2.tgz",
|
|
|
|
| 3801 |
}
|
| 3802 |
},
|
| 3803 |
"node_modules/@radix-ui/react-dialog": {
|
| 3804 |
+
"version": "1.1.15",
|
| 3805 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz",
|
| 3806 |
+
"integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==",
|
| 3807 |
+
"license": "MIT",
|
| 3808 |
+
"dependencies": {
|
| 3809 |
+
"@radix-ui/primitive": "1.1.3",
|
| 3810 |
+
"@radix-ui/react-compose-refs": "1.1.2",
|
| 3811 |
+
"@radix-ui/react-context": "1.1.2",
|
| 3812 |
+
"@radix-ui/react-dismissable-layer": "1.1.11",
|
| 3813 |
+
"@radix-ui/react-focus-guards": "1.1.3",
|
| 3814 |
+
"@radix-ui/react-focus-scope": "1.1.7",
|
| 3815 |
+
"@radix-ui/react-id": "1.1.1",
|
| 3816 |
+
"@radix-ui/react-portal": "1.1.9",
|
| 3817 |
+
"@radix-ui/react-presence": "1.1.5",
|
| 3818 |
+
"@radix-ui/react-primitive": "2.1.3",
|
| 3819 |
+
"@radix-ui/react-slot": "1.2.3",
|
| 3820 |
+
"@radix-ui/react-use-controllable-state": "1.2.2",
|
| 3821 |
"aria-hidden": "^1.2.4",
|
| 3822 |
"react-remove-scroll": "^2.6.3"
|
| 3823 |
},
|
|
|
|
| 3836 |
}
|
| 3837 |
}
|
| 3838 |
},
|
| 3839 |
+
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/primitive": {
|
| 3840 |
+
"version": "1.1.3",
|
| 3841 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
|
| 3842 |
+
"integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
|
| 3843 |
+
"license": "MIT"
|
| 3844 |
+
},
|
| 3845 |
+
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-compose-refs": {
|
| 3846 |
"version": "1.1.2",
|
| 3847 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
| 3848 |
+
"integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
|
| 3849 |
+
"license": "MIT",
|
| 3850 |
+
"peerDependencies": {
|
| 3851 |
+
"@types/react": "*",
|
| 3852 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 3853 |
+
},
|
| 3854 |
+
"peerDependenciesMeta": {
|
| 3855 |
+
"@types/react": {
|
| 3856 |
+
"optional": true
|
| 3857 |
+
}
|
| 3858 |
+
}
|
| 3859 |
+
},
|
| 3860 |
+
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-context": {
|
| 3861 |
+
"version": "1.1.2",
|
| 3862 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
|
| 3863 |
+
"integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
|
| 3864 |
+
"license": "MIT",
|
| 3865 |
+
"peerDependencies": {
|
| 3866 |
+
"@types/react": "*",
|
| 3867 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 3868 |
+
},
|
| 3869 |
+
"peerDependenciesMeta": {
|
| 3870 |
+
"@types/react": {
|
| 3871 |
+
"optional": true
|
| 3872 |
+
}
|
| 3873 |
+
}
|
| 3874 |
+
},
|
| 3875 |
+
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-dismissable-layer": {
|
| 3876 |
+
"version": "1.1.11",
|
| 3877 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz",
|
| 3878 |
+
"integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==",
|
| 3879 |
+
"license": "MIT",
|
| 3880 |
"dependencies": {
|
| 3881 |
+
"@radix-ui/primitive": "1.1.3",
|
| 3882 |
+
"@radix-ui/react-compose-refs": "1.1.2",
|
| 3883 |
+
"@radix-ui/react-primitive": "2.1.3",
|
| 3884 |
+
"@radix-ui/react-use-callback-ref": "1.1.1",
|
| 3885 |
+
"@radix-ui/react-use-escape-keydown": "1.1.1"
|
| 3886 |
},
|
| 3887 |
+
"peerDependencies": {
|
| 3888 |
+
"@types/react": "*",
|
| 3889 |
+
"@types/react-dom": "*",
|
| 3890 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 3891 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 3892 |
+
},
|
| 3893 |
+
"peerDependenciesMeta": {
|
| 3894 |
+
"@types/react": {
|
| 3895 |
+
"optional": true
|
| 3896 |
+
},
|
| 3897 |
+
"@types/react-dom": {
|
| 3898 |
+
"optional": true
|
| 3899 |
+
}
|
| 3900 |
+
}
|
| 3901 |
+
},
|
| 3902 |
+
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-focus-guards": {
|
| 3903 |
+
"version": "1.1.3",
|
| 3904 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz",
|
| 3905 |
+
"integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==",
|
| 3906 |
+
"license": "MIT",
|
| 3907 |
+
"peerDependencies": {
|
| 3908 |
+
"@types/react": "*",
|
| 3909 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 3910 |
+
},
|
| 3911 |
+
"peerDependenciesMeta": {
|
| 3912 |
+
"@types/react": {
|
| 3913 |
+
"optional": true
|
| 3914 |
+
}
|
| 3915 |
+
}
|
| 3916 |
+
},
|
| 3917 |
+
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-focus-scope": {
|
| 3918 |
+
"version": "1.1.7",
|
| 3919 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz",
|
| 3920 |
+
"integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==",
|
| 3921 |
+
"license": "MIT",
|
| 3922 |
+
"dependencies": {
|
| 3923 |
+
"@radix-ui/react-compose-refs": "1.1.2",
|
| 3924 |
+
"@radix-ui/react-primitive": "2.1.3",
|
| 3925 |
+
"@radix-ui/react-use-callback-ref": "1.1.1"
|
| 3926 |
+
},
|
| 3927 |
+
"peerDependencies": {
|
| 3928 |
+
"@types/react": "*",
|
| 3929 |
+
"@types/react-dom": "*",
|
| 3930 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 3931 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 3932 |
+
},
|
| 3933 |
+
"peerDependenciesMeta": {
|
| 3934 |
+
"@types/react": {
|
| 3935 |
+
"optional": true
|
| 3936 |
+
},
|
| 3937 |
+
"@types/react-dom": {
|
| 3938 |
+
"optional": true
|
| 3939 |
+
}
|
| 3940 |
+
}
|
| 3941 |
+
},
|
| 3942 |
+
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-id": {
|
| 3943 |
+
"version": "1.1.1",
|
| 3944 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz",
|
| 3945 |
+
"integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==",
|
| 3946 |
+
"license": "MIT",
|
| 3947 |
+
"dependencies": {
|
| 3948 |
+
"@radix-ui/react-use-layout-effect": "1.1.1"
|
| 3949 |
+
},
|
| 3950 |
+
"peerDependencies": {
|
| 3951 |
+
"@types/react": "*",
|
| 3952 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 3953 |
+
},
|
| 3954 |
+
"peerDependenciesMeta": {
|
| 3955 |
+
"@types/react": {
|
| 3956 |
+
"optional": true
|
| 3957 |
+
}
|
| 3958 |
+
}
|
| 3959 |
+
},
|
| 3960 |
+
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-portal": {
|
| 3961 |
+
"version": "1.1.9",
|
| 3962 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
|
| 3963 |
+
"integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==",
|
| 3964 |
+
"license": "MIT",
|
| 3965 |
+
"dependencies": {
|
| 3966 |
+
"@radix-ui/react-primitive": "2.1.3",
|
| 3967 |
+
"@radix-ui/react-use-layout-effect": "1.1.1"
|
| 3968 |
+
},
|
| 3969 |
+
"peerDependencies": {
|
| 3970 |
+
"@types/react": "*",
|
| 3971 |
+
"@types/react-dom": "*",
|
| 3972 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 3973 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 3974 |
+
},
|
| 3975 |
+
"peerDependenciesMeta": {
|
| 3976 |
+
"@types/react": {
|
| 3977 |
+
"optional": true
|
| 3978 |
+
},
|
| 3979 |
+
"@types/react-dom": {
|
| 3980 |
+
"optional": true
|
| 3981 |
+
}
|
| 3982 |
+
}
|
| 3983 |
+
},
|
| 3984 |
+
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-presence": {
|
| 3985 |
+
"version": "1.1.5",
|
| 3986 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
|
| 3987 |
+
"integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==",
|
| 3988 |
+
"license": "MIT",
|
| 3989 |
+
"dependencies": {
|
| 3990 |
+
"@radix-ui/react-compose-refs": "1.1.2",
|
| 3991 |
+
"@radix-ui/react-use-layout-effect": "1.1.1"
|
| 3992 |
+
},
|
| 3993 |
+
"peerDependencies": {
|
| 3994 |
+
"@types/react": "*",
|
| 3995 |
+
"@types/react-dom": "*",
|
| 3996 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 3997 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 3998 |
+
},
|
| 3999 |
+
"peerDependenciesMeta": {
|
| 4000 |
+
"@types/react": {
|
| 4001 |
+
"optional": true
|
| 4002 |
+
},
|
| 4003 |
+
"@types/react-dom": {
|
| 4004 |
+
"optional": true
|
| 4005 |
+
}
|
| 4006 |
+
}
|
| 4007 |
+
},
|
| 4008 |
+
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-primitive": {
|
| 4009 |
+
"version": "2.1.3",
|
| 4010 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
| 4011 |
+
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
| 4012 |
+
"license": "MIT",
|
| 4013 |
+
"dependencies": {
|
| 4014 |
+
"@radix-ui/react-slot": "1.2.3"
|
| 4015 |
+
},
|
| 4016 |
+
"peerDependencies": {
|
| 4017 |
+
"@types/react": "*",
|
| 4018 |
+
"@types/react-dom": "*",
|
| 4019 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 4020 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 4021 |
+
},
|
| 4022 |
+
"peerDependenciesMeta": {
|
| 4023 |
+
"@types/react": {
|
| 4024 |
+
"optional": true
|
| 4025 |
+
},
|
| 4026 |
+
"@types/react-dom": {
|
| 4027 |
+
"optional": true
|
| 4028 |
+
}
|
| 4029 |
+
}
|
| 4030 |
+
},
|
| 4031 |
+
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-callback-ref": {
|
| 4032 |
+
"version": "1.1.1",
|
| 4033 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
|
| 4034 |
+
"integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==",
|
| 4035 |
+
"license": "MIT",
|
| 4036 |
+
"peerDependencies": {
|
| 4037 |
+
"@types/react": "*",
|
| 4038 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 4039 |
+
},
|
| 4040 |
+
"peerDependenciesMeta": {
|
| 4041 |
+
"@types/react": {
|
| 4042 |
+
"optional": true
|
| 4043 |
+
}
|
| 4044 |
+
}
|
| 4045 |
+
},
|
| 4046 |
+
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-controllable-state": {
|
| 4047 |
+
"version": "1.2.2",
|
| 4048 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
|
| 4049 |
+
"integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
|
| 4050 |
+
"license": "MIT",
|
| 4051 |
+
"dependencies": {
|
| 4052 |
+
"@radix-ui/react-use-effect-event": "0.0.2",
|
| 4053 |
+
"@radix-ui/react-use-layout-effect": "1.1.1"
|
| 4054 |
+
},
|
| 4055 |
+
"peerDependencies": {
|
| 4056 |
+
"@types/react": "*",
|
| 4057 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 4058 |
+
},
|
| 4059 |
+
"peerDependenciesMeta": {
|
| 4060 |
+
"@types/react": {
|
| 4061 |
+
"optional": true
|
| 4062 |
+
}
|
| 4063 |
+
}
|
| 4064 |
+
},
|
| 4065 |
+
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-escape-keydown": {
|
| 4066 |
+
"version": "1.1.1",
|
| 4067 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz",
|
| 4068 |
+
"integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==",
|
| 4069 |
+
"license": "MIT",
|
| 4070 |
+
"dependencies": {
|
| 4071 |
+
"@radix-ui/react-use-callback-ref": "1.1.1"
|
| 4072 |
+
},
|
| 4073 |
+
"peerDependencies": {
|
| 4074 |
+
"@types/react": "*",
|
| 4075 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 4076 |
+
},
|
| 4077 |
+
"peerDependenciesMeta": {
|
| 4078 |
+
"@types/react": {
|
| 4079 |
+
"optional": true
|
| 4080 |
+
}
|
| 4081 |
+
}
|
| 4082 |
+
},
|
| 4083 |
+
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-layout-effect": {
|
| 4084 |
+
"version": "1.1.1",
|
| 4085 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
|
| 4086 |
+
"integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
|
| 4087 |
+
"license": "MIT",
|
| 4088 |
"peerDependencies": {
|
| 4089 |
"@types/react": "*",
|
| 4090 |
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
|
|
|
| 4803 |
}
|
| 4804 |
}
|
| 4805 |
},
|
| 4806 |
+
"node_modules/@radix-ui/react-use-effect-event": {
|
| 4807 |
+
"version": "0.0.2",
|
| 4808 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz",
|
| 4809 |
+
"integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==",
|
| 4810 |
+
"license": "MIT",
|
| 4811 |
+
"dependencies": {
|
| 4812 |
+
"@radix-ui/react-use-layout-effect": "1.1.1"
|
| 4813 |
+
},
|
| 4814 |
+
"peerDependencies": {
|
| 4815 |
+
"@types/react": "*",
|
| 4816 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 4817 |
+
},
|
| 4818 |
+
"peerDependenciesMeta": {
|
| 4819 |
+
"@types/react": {
|
| 4820 |
+
"optional": true
|
| 4821 |
+
}
|
| 4822 |
+
}
|
| 4823 |
+
},
|
| 4824 |
+
"node_modules/@radix-ui/react-use-effect-event/node_modules/@radix-ui/react-use-layout-effect": {
|
| 4825 |
+
"version": "1.1.1",
|
| 4826 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
|
| 4827 |
+
"integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
|
| 4828 |
+
"license": "MIT",
|
| 4829 |
+
"peerDependencies": {
|
| 4830 |
+
"@types/react": "*",
|
| 4831 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 4832 |
+
},
|
| 4833 |
+
"peerDependenciesMeta": {
|
| 4834 |
+
"@types/react": {
|
| 4835 |
+
"optional": true
|
| 4836 |
+
}
|
| 4837 |
+
}
|
| 4838 |
+
},
|
| 4839 |
"node_modules/@radix-ui/react-use-escape-keydown": {
|
| 4840 |
"version": "1.1.0",
|
| 4841 |
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz",
|
package.json
CHANGED
|
@@ -1,4 +1,3 @@
|
|
| 1 |
-
|
| 2 |
{
|
| 3 |
"name": "nextn",
|
| 4 |
"version": "0.1.0",
|
|
@@ -11,8 +10,10 @@
|
|
| 11 |
"typecheck": "tsc --noEmit"
|
| 12 |
},
|
| 13 |
"dependencies": {
|
| 14 |
-
"@
|
| 15 |
"@hookform/resolvers": "^4.1.3",
|
|
|
|
|
|
|
| 16 |
"@radix-ui/react-avatar": "^1.1.3",
|
| 17 |
"@radix-ui/react-dialog": "^1.1.6",
|
| 18 |
"@radix-ui/react-dropdown-menu": "^2.1.6",
|
|
@@ -30,6 +31,7 @@
|
|
| 30 |
"clsx": "^2.1.1",
|
| 31 |
"date-fns": "^3.6.0",
|
| 32 |
"firebase": "^11.9.1",
|
|
|
|
| 33 |
"lucide-react": "^0.475.0",
|
| 34 |
"next": "15.3.8",
|
| 35 |
"react": "^18.3.1",
|
|
@@ -37,9 +39,7 @@
|
|
| 37 |
"react-hook-form": "^7.54.2",
|
| 38 |
"tailwind-merge": "^3.0.1",
|
| 39 |
"tailwindcss-animate": "^1.0.7",
|
| 40 |
-
"zod": "^3.24.2"
|
| 41 |
-
"genkit": "^1.0.0",
|
| 42 |
-
"@genkit-ai/google-genai": "^1.0.0"
|
| 43 |
},
|
| 44 |
"devDependencies": {
|
| 45 |
"@types/node": "^20",
|
|
|
|
|
|
|
| 1 |
{
|
| 2 |
"name": "nextn",
|
| 3 |
"version": "0.1.0",
|
|
|
|
| 10 |
"typecheck": "tsc --noEmit"
|
| 11 |
},
|
| 12 |
"dependencies": {
|
| 13 |
+
"@genkit-ai/google-genai": "^1.0.0",
|
| 14 |
"@hookform/resolvers": "^4.1.3",
|
| 15 |
+
"@huggingface/inference": "^2.8.1",
|
| 16 |
+
"@radix-ui/react-alert-dialog": "^1.1.15",
|
| 17 |
"@radix-ui/react-avatar": "^1.1.3",
|
| 18 |
"@radix-ui/react-dialog": "^1.1.6",
|
| 19 |
"@radix-ui/react-dropdown-menu": "^2.1.6",
|
|
|
|
| 31 |
"clsx": "^2.1.1",
|
| 32 |
"date-fns": "^3.6.0",
|
| 33 |
"firebase": "^11.9.1",
|
| 34 |
+
"genkit": "^1.0.0",
|
| 35 |
"lucide-react": "^0.475.0",
|
| 36 |
"next": "15.3.8",
|
| 37 |
"react": "^18.3.1",
|
|
|
|
| 39 |
"react-hook-form": "^7.54.2",
|
| 40 |
"tailwind-merge": "^3.0.1",
|
| 41 |
"tailwindcss-animate": "^1.0.7",
|
| 42 |
+
"zod": "^3.24.2"
|
|
|
|
|
|
|
| 43 |
},
|
| 44 |
"devDependencies": {
|
| 45 |
"@types/node": "^20",
|
src/ai/flows/ai-text-to-speech.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
| 7 |
import type { TextToSpeechInput, TextToSpeechOutput } from './types';
|
| 8 |
|
| 9 |
export async function textToSpeech(input: TextToSpeechInput): Promise<TextToSpeechOutput> {
|
|
|
|
| 10 |
const ELEVEN_LABS_API_KEY = "89ed836c9dcaca9c5f91e14eca1fb1ed012a200fdc549c0fcee08d5859f54946";
|
| 11 |
const VOICE_ID = "EXAVITQu4vr4xnSDxMaL"; // Bella
|
| 12 |
|
|
|
|
| 7 |
import type { TextToSpeechInput, TextToSpeechOutput } from './types';
|
| 8 |
|
| 9 |
export async function textToSpeech(input: TextToSpeechInput): Promise<TextToSpeechOutput> {
|
| 10 |
+
// المفتاح المعتمد الذي زودتني به
|
| 11 |
const ELEVEN_LABS_API_KEY = "89ed836c9dcaca9c5f91e14eca1fb1ed012a200fdc549c0fcee08d5859f54946";
|
| 12 |
const VOICE_ID = "EXAVITQu4vr4xnSDxMaL"; // Bella
|
| 13 |
|
src/components/ai-world/ai-world-feed.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
"use client";
|
| 2 |
|
| 3 |
import React, { useState, useEffect, useRef, useCallback, useContext } from 'react';
|
|
@@ -233,6 +234,7 @@ export const AIWorldFeed = ({ onBack }: { onBack?: () => void }) => {
|
|
| 233 |
else clearSearch();
|
| 234 |
};
|
| 235 |
|
|
|
|
| 236 |
const sortedPosts = (searchedPosts.length > 0 ? searchedPosts : allPosts)
|
| 237 |
.filter(p => !p.uiState.isReported)
|
| 238 |
.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
@@ -312,7 +314,7 @@ export const AIWorldFeed = ({ onBack }: { onBack?: () => void }) => {
|
|
| 312 |
<Button
|
| 313 |
variant="ghost"
|
| 314 |
className="flex-1 rounded-xl h-10 gap-2 font-bold text-xs"
|
| 315 |
-
onClick={() => generatePostsFromAI(
|
| 316 |
disabled={isGeneratingFromQuery}
|
| 317 |
>
|
| 318 |
{isGeneratingFromQuery ? <Loader2 className="h-4 w-4 animate-spin" /> : <Sparkles className="h-4 w-4 text-purple-500" />}
|
|
@@ -376,4 +378,4 @@ export const AIWorldFeed = ({ onBack }: { onBack?: () => void }) => {
|
|
| 376 |
</main>
|
| 377 |
</div>
|
| 378 |
);
|
| 379 |
-
};
|
|
|
|
| 1 |
+
|
| 2 |
"use client";
|
| 3 |
|
| 4 |
import React, { useState, useEffect, useRef, useCallback, useContext } from 'react';
|
|
|
|
| 234 |
else clearSearch();
|
| 235 |
};
|
| 236 |
|
| 237 |
+
// الترتيب حسب الوقت (الأحدث أولاً) لضمان تجربة فيسبوك
|
| 238 |
const sortedPosts = (searchedPosts.length > 0 ? searchedPosts : allPosts)
|
| 239 |
.filter(p => !p.uiState.isReported)
|
| 240 |
.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
|
|
| 314 |
<Button
|
| 315 |
variant="ghost"
|
| 316 |
className="flex-1 rounded-xl h-10 gap-2 font-bold text-xs"
|
| 317 |
+
onClick={() => generatePostsFromAI()}
|
| 318 |
disabled={isGeneratingFromQuery}
|
| 319 |
>
|
| 320 |
{isGeneratingFromQuery ? <Loader2 className="h-4 w-4 animate-spin" /> : <Sparkles className="h-4 w-4 text-purple-500" />}
|
|
|
|
| 378 |
</main>
|
| 379 |
</div>
|
| 380 |
);
|
| 381 |
+
};
|
src/contexts/ai-world-context.tsx
CHANGED
|
@@ -8,7 +8,7 @@ import { retrieveImagesFromQuery } from '@/ai/flows/image-retrieval-from-query';
|
|
| 8 |
import { allNames } from '@/lib/ai-world-names';
|
| 9 |
import type { PostWithUIState, AIComment, AIUser, AIPost } from '@/lib/types';
|
| 10 |
import { database } from '@/firebase/client';
|
| 11 |
-
import { ref, update, set, push, query, orderByKey,
|
| 12 |
import { checkConsumption, recordConsumption } from '@/lib/consumption';
|
| 13 |
import { useAuth } from './auth-context';
|
| 14 |
import { useLanguage } from './language-context';
|
|
@@ -48,7 +48,7 @@ export const AIWorldProvider: React.FC<{ children: React.ReactNode }> = ({ child
|
|
| 48 |
const { lang, aiEngine, t } = useLanguage();
|
| 49 |
const isFetchingRef = useRef(false);
|
| 50 |
const [canLoadMore, setCanLoadMore] = useState(true);
|
| 51 |
-
const [
|
| 52 |
|
| 53 |
const getRandomAIUser = (): AIUser => {
|
| 54 |
const name = allNames[Math.floor(Math.random() * allNames.length)];
|
|
@@ -85,32 +85,55 @@ export const AIWorldProvider: React.FC<{ children: React.ReactNode }> = ({ child
|
|
| 85 |
isFetchingRef.current = true;
|
| 86 |
setIsLoading(true);
|
| 87 |
|
|
|
|
|
|
|
| 88 |
try {
|
| 89 |
const postsRef = ref(database, 'posts');
|
| 90 |
-
let q
|
| 91 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
|
| 93 |
const snapshot = await get(q);
|
| 94 |
if (snapshot.exists()) {
|
| 95 |
-
const
|
| 96 |
snapshot.forEach((child) => {
|
| 97 |
-
if (child.key !==
|
|
|
|
|
|
|
| 98 |
});
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
}
|
| 103 |
-
if (newPosts.length <= POSTS_PER_PAGE) setCanLoadMore(false);
|
| 104 |
} else {
|
| 105 |
setCanLoadMore(false);
|
| 106 |
}
|
| 107 |
} catch (error) {
|
| 108 |
-
console.error("Error loading posts:", error);
|
| 109 |
} finally {
|
| 110 |
setIsLoading(false);
|
| 111 |
isFetchingRef.current = false;
|
| 112 |
}
|
| 113 |
-
}, [
|
| 114 |
|
| 115 |
useEffect(() => { loadNextPage(); }, []);
|
| 116 |
|
|
@@ -122,13 +145,14 @@ export const AIWorldProvider: React.FC<{ children: React.ReactNode }> = ({ child
|
|
| 122 |
}
|
| 123 |
|
| 124 |
setIsGeneratingFromQuery(true);
|
|
|
|
| 125 |
try {
|
| 126 |
const result = await generatePostIdeas({
|
| 127 |
count: POSTS_TO_GENERATE_AI,
|
| 128 |
location: userData?.location || 'Global',
|
| 129 |
language: lang as any,
|
| 130 |
age: userData?.age,
|
| 131 |
-
aiEngine
|
| 132 |
});
|
| 133 |
|
| 134 |
if (!result?.posts) throw new Error("Invalid response");
|
|
@@ -168,10 +192,12 @@ export const AIWorldProvider: React.FC<{ children: React.ReactNode }> = ({ child
|
|
| 168 |
});
|
| 169 |
|
| 170 |
await update(ref(database), updates);
|
|
|
|
|
|
|
| 171 |
setAllPosts(prev => [...newPostsBatch, ...prev]);
|
| 172 |
toast({ title: t.successUpdate });
|
| 173 |
-
} catch (error) {
|
| 174 |
-
console.error(error);
|
| 175 |
toast({ title: "Error", description: t.errorAI, variant: "destructive" });
|
| 176 |
} finally {
|
| 177 |
setIsGeneratingFromQuery(false);
|
|
|
|
| 8 |
import { allNames } from '@/lib/ai-world-names';
|
| 9 |
import type { PostWithUIState, AIComment, AIUser, AIPost } from '@/lib/types';
|
| 10 |
import { database } from '@/firebase/client';
|
| 11 |
+
import { ref, update, set, push, query, orderByKey, limitToLast, endAt, get } from 'firebase/database';
|
| 12 |
import { checkConsumption, recordConsumption } from '@/lib/consumption';
|
| 13 |
import { useAuth } from './auth-context';
|
| 14 |
import { useLanguage } from './language-context';
|
|
|
|
| 48 |
const { lang, aiEngine, t } = useLanguage();
|
| 49 |
const isFetchingRef = useRef(false);
|
| 50 |
const [canLoadMore, setCanLoadMore] = useState(true);
|
| 51 |
+
const [oldestKeyLoaded, setOldestKeyLoaded] = useState<string | null>(null);
|
| 52 |
|
| 53 |
const getRandomAIUser = (): AIUser => {
|
| 54 |
const name = allNames[Math.floor(Math.random() * allNames.length)];
|
|
|
|
| 85 |
isFetchingRef.current = true;
|
| 86 |
setIsLoading(true);
|
| 87 |
|
| 88 |
+
console.log("[WORLD_CONTEXT]: Fetching latest posts (Facebook style sorting)...");
|
| 89 |
+
|
| 90 |
try {
|
| 91 |
const postsRef = ref(database, 'posts');
|
| 92 |
+
let q;
|
| 93 |
+
|
| 94 |
+
if (!oldestKeyLoaded) {
|
| 95 |
+
// أول تحميل: جلب آخر 15 منشور (الأحدث)
|
| 96 |
+
q = query(postsRef, orderByKey(), limitToLast(POSTS_PER_PAGE));
|
| 97 |
+
} else {
|
| 98 |
+
// تحميل المزيد: جلب 15 منشور تسبق أقدم منشور محمل حالياً
|
| 99 |
+
q = query(postsRef, orderByKey(), endAt(oldestKeyLoaded), limitToLast(POSTS_PER_PAGE + 1));
|
| 100 |
+
}
|
| 101 |
|
| 102 |
const snapshot = await get(q);
|
| 103 |
if (snapshot.exists()) {
|
| 104 |
+
const fetchedPosts: PostWithUIState[] = [];
|
| 105 |
snapshot.forEach((child) => {
|
| 106 |
+
if (child.key !== oldestKeyLoaded) {
|
| 107 |
+
fetchedPosts.push(processDbPost(child.key!, child.val()));
|
| 108 |
+
}
|
| 109 |
});
|
| 110 |
+
|
| 111 |
+
// ترتيب المنشورات المسترجعة تنازلياً لأن RTDB يرجعها تصاعدياً
|
| 112 |
+
fetchedPosts.sort((a, b) => b.id.localeCompare(a.id));
|
| 113 |
+
|
| 114 |
+
if (fetchedPosts.length > 0) {
|
| 115 |
+
setOldestKeyLoaded(fetchedPosts[fetchedPosts.length - 1].id);
|
| 116 |
+
setAllPosts(prev => {
|
| 117 |
+
// تجنب التكرار عند الدمج
|
| 118 |
+
const existingIds = new Set(prev.map(p => p.id));
|
| 119 |
+
const uniqueNew = fetchedPosts.filter(p => !existingIds.has(p.id));
|
| 120 |
+
return [...prev, ...uniqueNew];
|
| 121 |
+
});
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
if (fetchedPosts.length < POSTS_PER_PAGE) {
|
| 125 |
+
setCanLoadMore(false);
|
| 126 |
}
|
|
|
|
| 127 |
} else {
|
| 128 |
setCanLoadMore(false);
|
| 129 |
}
|
| 130 |
} catch (error) {
|
| 131 |
+
console.error("[WORLD_CONTEXT_ERROR]: Error loading posts:", error);
|
| 132 |
} finally {
|
| 133 |
setIsLoading(false);
|
| 134 |
isFetchingRef.current = false;
|
| 135 |
}
|
| 136 |
+
}, [oldestKeyLoaded, canLoadMore, processDbPost]);
|
| 137 |
|
| 138 |
useEffect(() => { loadNextPage(); }, []);
|
| 139 |
|
|
|
|
| 145 |
}
|
| 146 |
|
| 147 |
setIsGeneratingFromQuery(true);
|
| 148 |
+
console.log(`[WORLD_CONTEXT]: Generating ${POSTS_TO_GENERATE_AI} posts via AI...`);
|
| 149 |
try {
|
| 150 |
const result = await generatePostIdeas({
|
| 151 |
count: POSTS_TO_GENERATE_AI,
|
| 152 |
location: userData?.location || 'Global',
|
| 153 |
language: lang as any,
|
| 154 |
age: userData?.age,
|
| 155 |
+
aiEngine
|
| 156 |
});
|
| 157 |
|
| 158 |
if (!result?.posts) throw new Error("Invalid response");
|
|
|
|
| 192 |
});
|
| 193 |
|
| 194 |
await update(ref(database), updates);
|
| 195 |
+
// ترتيب المنشورات الجديدة لتظهر في الأعلى
|
| 196 |
+
newPostsBatch.sort((a, b) => b.id.localeCompare(a.id));
|
| 197 |
setAllPosts(prev => [...newPostsBatch, ...prev]);
|
| 198 |
toast({ title: t.successUpdate });
|
| 199 |
+
} catch (error: any) {
|
| 200 |
+
console.error("[WORLD_CONTEXT_GEN_ERROR]:", error.message);
|
| 201 |
toast({ title: "Error", description: t.errorAI, variant: "destructive" });
|
| 202 |
} finally {
|
| 203 |
setIsGeneratingFromQuery(false);
|
src/lib/gemini-client.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
| 1 |
|
| 2 |
/**
|
| 3 |
-
* @fileOverview المحرك الرئيسي للذكاء الاصطناعي مع دعم (Gemini, Groq, OpenRouter) ونظام تشخيص كامل.
|
| 4 |
*/
|
| 5 |
|
| 6 |
const GEMINI_KEY = "AIzaSyA_0i-0yCk9m6ehCIZ87_CKbUMrwlea-_s";
|
| 7 |
-
const GEMINI_MODEL = "gemini-2.
|
| 8 |
|
| 9 |
const GROQ_KEY = "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";
|
| 10 |
const GROQ_MODELS = ["llama-3.3-70b-versatile", "mixtral-8x7b-32768"];
|
| 11 |
|
| 12 |
const OPENROUTER_KEY = "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";
|
| 13 |
-
const OPENROUTER_MODELS = ["google/gemini-2.0-flash-lite-preview-02-05:free"
|
| 14 |
|
| 15 |
function extractJsonFromText(text: string): string {
|
| 16 |
const jsonBlockMatch = text.match(/```json\s*([\s\S]*?)\s*```/);
|
|
@@ -25,24 +25,29 @@ function extractJsonFromText(text: string): string {
|
|
| 25 |
}
|
| 26 |
|
| 27 |
async function callGemini(prompt: string): Promise<string> {
|
| 28 |
-
console.log(`[AI_PROVIDER]: Connecting to
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
|
|
|
| 42 |
}
|
| 43 |
-
|
| 44 |
-
const data = await response.json();
|
| 45 |
-
return data.candidates?.[0]?.content?.parts?.[0]?.text || "";
|
| 46 |
}
|
| 47 |
|
| 48 |
async function callGroq(prompt: string, model: string): Promise<string> {
|
|
@@ -62,8 +67,7 @@ async function callGroq(prompt: string, model: string): Promise<string> {
|
|
| 62 |
|
| 63 |
if (!response.ok) {
|
| 64 |
const err = await response.text();
|
| 65 |
-
|
| 66 |
-
throw new Error(`Groq Error: ${response.status}`);
|
| 67 |
}
|
| 68 |
|
| 69 |
const data = await response.json();
|
|
@@ -89,8 +93,7 @@ async function callOpenRouter(prompt: string, model: string): Promise<string> {
|
|
| 89 |
|
| 90 |
if (!response.ok) {
|
| 91 |
const err = await response.text();
|
| 92 |
-
|
| 93 |
-
throw new Error(`OpenRouter Error: ${response.status}`);
|
| 94 |
}
|
| 95 |
|
| 96 |
const data = await response.json();
|
|
@@ -105,20 +108,20 @@ export async function askAI(prompt: string, preferredEngine: 'primary' | 'fallba
|
|
| 105 |
if (preferredEngine === 'primary') {
|
| 106 |
providers = [
|
| 107 |
{ fn: () => callGemini(prompt), name: GEMINI_MODEL },
|
| 108 |
-
{ fn: () =>
|
| 109 |
-
{ fn: () =>
|
| 110 |
];
|
| 111 |
} else if (preferredEngine === 'advanced') {
|
| 112 |
providers = [
|
| 113 |
-
{ fn: () => callOpenRouter(prompt, OPENROUTER_MODELS[0]), name: "OpenRouter
|
| 114 |
{ fn: () => callGemini(prompt), name: GEMINI_MODEL },
|
| 115 |
-
{ fn: () => callGroq(prompt, GROQ_MODELS[0]), name:
|
| 116 |
];
|
| 117 |
} else {
|
| 118 |
providers = [
|
| 119 |
-
{ fn: () => callGroq(prompt, GROQ_MODELS[0]), name:
|
| 120 |
-
{ fn: () => callGroq(prompt, GROQ_MODELS[1]), name:
|
| 121 |
-
{ fn: () =>
|
| 122 |
];
|
| 123 |
}
|
| 124 |
|
|
@@ -134,8 +137,7 @@ export async function askAI(prompt: string, preferredEngine: 'primary' | 'fallba
|
|
| 134 |
}
|
| 135 |
}
|
| 136 |
|
| 137 |
-
|
| 138 |
-
throw new Error("All AI providers failed.");
|
| 139 |
}
|
| 140 |
|
| 141 |
export const safeGenerateContent = async (prompt: string, aiEngine: 'primary' | 'fallback' | 'advanced' = 'primary'): Promise<{ output: any, model: string }> => {
|
|
@@ -144,11 +146,10 @@ export const safeGenerateContent = async (prompt: string, aiEngine: 'primary' |
|
|
| 144 |
|
| 145 |
try {
|
| 146 |
const parsed = JSON.parse(cleanedJsonText);
|
| 147 |
-
console.log(`[AI_JSON_OK]: Parsed response from ${result.model}`);
|
| 148 |
return { output: parsed, model: result.model };
|
| 149 |
} catch (error: any) {
|
| 150 |
-
console.error(
|
| 151 |
-
console.error(
|
| 152 |
throw new Error("AI response was not valid JSON.");
|
| 153 |
}
|
| 154 |
};
|
|
|
|
| 1 |
|
| 2 |
/**
|
| 3 |
+
* @fileOverview المحرك الرئيسي للذكاء الاصطناعي مع دعم (Gemini 2.5 Flash Lite, Groq, OpenRouter) ونظام تشخيص كامل.
|
| 4 |
*/
|
| 5 |
|
| 6 |
const GEMINI_KEY = "AIzaSyA_0i-0yCk9m6ehCIZ87_CKbUMrwlea-_s";
|
| 7 |
+
const GEMINI_MODEL = "gemini-2.5-flash-lite"; // الطراز المطلوب بدقة
|
| 8 |
|
| 9 |
const GROQ_KEY = "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";
|
| 10 |
const GROQ_MODELS = ["llama-3.3-70b-versatile", "mixtral-8x7b-32768"];
|
| 11 |
|
| 12 |
const OPENROUTER_KEY = "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";
|
| 13 |
+
const OPENROUTER_MODELS = ["google/gemini-2.0-flash-lite-preview-02-05:free"];
|
| 14 |
|
| 15 |
function extractJsonFromText(text: string): string {
|
| 16 |
const jsonBlockMatch = text.match(/```json\s*([\s\S]*?)\s*```/);
|
|
|
|
| 25 |
}
|
| 26 |
|
| 27 |
async function callGemini(prompt: string): Promise<string> {
|
| 28 |
+
console.log(`[AI_PROVIDER]: Connecting to ${GEMINI_MODEL}...`);
|
| 29 |
+
try {
|
| 30 |
+
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${GEMINI_MODEL}:generateContent?key=${GEMINI_KEY}`, {
|
| 31 |
+
method: 'POST',
|
| 32 |
+
headers: { 'Content-Type': 'application/json' },
|
| 33 |
+
body: JSON.stringify({
|
| 34 |
+
contents: [{ parts: [{ text: prompt }] }],
|
| 35 |
+
generationConfig: { temperature: 0.7 }
|
| 36 |
+
})
|
| 37 |
+
});
|
| 38 |
+
|
| 39 |
+
if (!response.ok) {
|
| 40 |
+
const err = await response.text();
|
| 41 |
+
console.error(`[AI_ERROR_GEMINI]: ${response.status} - ${err}`);
|
| 42 |
+
throw new Error(`Gemini Error: ${response.status}`);
|
| 43 |
+
}
|
| 44 |
|
| 45 |
+
const data = await response.json();
|
| 46 |
+
return data.candidates?.[0]?.content?.parts?.[0]?.text || "";
|
| 47 |
+
} catch (e: any) {
|
| 48 |
+
console.error(`[GEMINI_FETCH_FAIL]: ${e.message}`);
|
| 49 |
+
throw e;
|
| 50 |
}
|
|
|
|
|
|
|
|
|
|
| 51 |
}
|
| 52 |
|
| 53 |
async function callGroq(prompt: string, model: string): Promise<string> {
|
|
|
|
| 67 |
|
| 68 |
if (!response.ok) {
|
| 69 |
const err = await response.text();
|
| 70 |
+
throw new Error(`Groq Error: ${response.status} - ${err}`);
|
|
|
|
| 71 |
}
|
| 72 |
|
| 73 |
const data = await response.json();
|
|
|
|
| 93 |
|
| 94 |
if (!response.ok) {
|
| 95 |
const err = await response.text();
|
| 96 |
+
throw new Error(`OpenRouter Error: ${response.status} - ${err}`);
|
|
|
|
| 97 |
}
|
| 98 |
|
| 99 |
const data = await response.json();
|
|
|
|
| 108 |
if (preferredEngine === 'primary') {
|
| 109 |
providers = [
|
| 110 |
{ fn: () => callGemini(prompt), name: GEMINI_MODEL },
|
| 111 |
+
{ fn: () => callGroq(prompt, GROQ_MODELS[0]), name: "Groq-Llama" },
|
| 112 |
+
{ fn: () => callOpenRouter(prompt, OPENROUTER_MODELS[0]), name: "OpenRouter" }
|
| 113 |
];
|
| 114 |
} else if (preferredEngine === 'advanced') {
|
| 115 |
providers = [
|
| 116 |
+
{ fn: () => callOpenRouter(prompt, OPENROUTER_MODELS[0]), name: "OpenRouter" },
|
| 117 |
{ fn: () => callGemini(prompt), name: GEMINI_MODEL },
|
| 118 |
+
{ fn: () => callGroq(prompt, GROQ_MODELS[0]), name: "Groq-Llama" }
|
| 119 |
];
|
| 120 |
} else {
|
| 121 |
providers = [
|
| 122 |
+
{ fn: () => callGroq(prompt, GROQ_MODELS[0]), name: "Groq-Llama" },
|
| 123 |
+
{ fn: () => callGroq(prompt, GROQ_MODELS[1]), name: "Groq-Mixtral" },
|
| 124 |
+
{ fn: () => callGemini(prompt), name: GEMINI_MODEL }
|
| 125 |
];
|
| 126 |
}
|
| 127 |
|
|
|
|
| 137 |
}
|
| 138 |
}
|
| 139 |
|
| 140 |
+
throw new Error("All AI providers and models in fallback chain failed.");
|
|
|
|
| 141 |
}
|
| 142 |
|
| 143 |
export const safeGenerateContent = async (prompt: string, aiEngine: 'primary' | 'fallback' | 'advanced' = 'primary'): Promise<{ output: any, model: string }> => {
|
|
|
|
| 146 |
|
| 147 |
try {
|
| 148 |
const parsed = JSON.parse(cleanedJsonText);
|
|
|
|
| 149 |
return { output: parsed, model: result.model };
|
| 150 |
} catch (error: any) {
|
| 151 |
+
console.error("[GEMINI_CLIENT_JSON_ERROR]: Failed to parse JSON ->", error.message);
|
| 152 |
+
console.error("[RAW_AI_OUTPUT]:", result.answer);
|
| 153 |
throw new Error("AI response was not valid JSON.");
|
| 154 |
}
|
| 155 |
};
|