diff --git a/package-lock.json b/package-lock.json
index 1f22463..c59f896 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -22,9 +22,11 @@
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-toast": "^1.1.5",
"@tanstack/react-query": "^4.36.1",
+ "avvvatars-react": "^0.4.2",
"axios": "^1.5.1",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
+ "kmenu": "^2.0.0",
"lucide-react": "^0.376.0",
"next": "^13.5.6",
"next-auth": "^4.23.2",
@@ -3034,6 +3036,19 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/avvvatars-react": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/avvvatars-react/-/avvvatars-react-0.4.2.tgz",
+ "integrity": "sha512-D/bnSM+P6pQi71dFeFVqQsqmv+ct5XG7uO2lvJoiksALpXLd6kPgpRR1SUQxFZJkJNrkmg0NPgbhIgJgOsDYRQ==",
+ "license": "MIT",
+ "dependencies": {
+ "goober": "^2.1.8"
+ },
+ "peerDependencies": {
+ "react": ">= 16",
+ "react-dom": ">= 16"
+ }
+ },
"node_modules/axe-core": {
"version": "4.10.0",
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.0.tgz",
@@ -3657,7 +3672,6 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
- "devOptional": true,
"license": "MIT"
},
"node_modules/damerau-levenshtein": {
@@ -5328,6 +5342,31 @@
"url": "https://github.com/sponsors/rawify"
}
},
+ "node_modules/framer-motion": {
+ "version": "11.9.0",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.9.0.tgz",
+ "integrity": "sha512-nCfGxvsQecVLjjYDu35G2F5ls+ArE3FBfhxV0RSiisMaUKqteq5DMBFNRKwMyVj+VqKTNhawt+BV480YCHKFlQ==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "@emotion/is-prop-valid": "*",
+ "react": "^18.0.0",
+ "react-dom": "^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/is-prop-valid": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/fs-extra": {
"version": "11.2.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
@@ -5646,6 +5685,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/goober": {
+ "version": "2.1.14",
+ "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.14.tgz",
+ "integrity": "sha512-4UpC0NdGyAFqLNPnhCT2iHpza2q+RAY3GV85a/mRPdzyPQMsj0KmMMuetdIkzWRbJ+Hgau1EZztq8ImmiMGhsg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "csstype": "^3.0.10"
+ }
+ },
"node_modules/gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
@@ -6634,6 +6682,22 @@
"node": ">=0.10.0"
}
},
+ "node_modules/kmenu": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/kmenu/-/kmenu-2.0.0.tgz",
+ "integrity": "sha512-ZuNZDFrQZIP/C7GK7xd+CxX9J5UEkXK20X7OhjcH/yBa/zDQEklo5Wk36iHJoSMeHvJ3uW0q/VBKjXk0SV2v7w==",
+ "license": "MIT",
+ "dependencies": {
+ "framer-motion": "^11.0.3",
+ "react-scrollbar-size": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0"
+ }
+ },
"node_modules/language-subtag-registry": {
"version": "0.3.23",
"resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
@@ -8019,6 +8083,18 @@
}
}
},
+ "node_modules/react-scrollbar-size": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/react-scrollbar-size/-/react-scrollbar-size-5.0.0.tgz",
+ "integrity": "sha512-Ly3OuRMz4yDFViTh+ANH6TrG8EqrgjC1uxxm2a/95+2Ijy3XT+bWtzm4QmgZUcUVg+8BCKzmPMM7z39ZtucDIQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.1 || ^18.0.0"
+ }
+ },
"node_modules/react-share": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/react-share/-/react-share-5.1.0.tgz",
diff --git a/package.json b/package.json
index f4bb98d..c5bd6b2 100644
--- a/package.json
+++ b/package.json
@@ -34,6 +34,7 @@
"axios": "^1.5.1",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
+ "kmenu": "^2.0.0",
"lucide-react": "^0.376.0",
"next": "^13.5.6",
"next-auth": "^4.23.2",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 1987dd3..c35f0d3 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -62,6 +62,9 @@ importers:
clsx:
specifier: ^2.0.0
version: 2.1.1
+ kmenu:
+ specifier: ^2.0.0
+ version: 2.0.0(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
lucide-react:
specifier: ^0.376.0
version: 0.376.0(react@18.3.1)
@@ -1859,6 +1862,20 @@ packages:
fraction.js@4.3.7:
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
+ framer-motion@11.9.0:
+ resolution: {integrity: sha512-nCfGxvsQecVLjjYDu35G2F5ls+ArE3FBfhxV0RSiisMaUKqteq5DMBFNRKwMyVj+VqKTNhawt+BV480YCHKFlQ==}
+ peerDependencies:
+ '@emotion/is-prop-valid': '*'
+ react: ^18.0.0
+ react-dom: ^18.0.0
+ peerDependenciesMeta:
+ '@emotion/is-prop-valid':
+ optional: true
+ react:
+ optional: true
+ react-dom:
+ optional: true
+
fs-extra@11.2.0:
resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==}
engines: {node: '>=14.14'}
@@ -2280,6 +2297,12 @@ packages:
resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
engines: {node: '>=0.10.0'}
+ kmenu@2.0.0:
+ resolution: {integrity: sha512-ZuNZDFrQZIP/C7GK7xd+CxX9J5UEkXK20X7OhjcH/yBa/zDQEklo5Wk36iHJoSMeHvJ3uW0q/VBKjXk0SV2v7w==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ react: '>=16.0.0'
+
language-subtag-registry@0.3.23:
resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==}
@@ -2776,6 +2799,12 @@ packages:
'@types/react':
optional: true
+ react-scrollbar-size@5.0.0:
+ resolution: {integrity: sha512-Ly3OuRMz4yDFViTh+ANH6TrG8EqrgjC1uxxm2a/95+2Ijy3XT+bWtzm4QmgZUcUVg+8BCKzmPMM7z39ZtucDIQ==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.1 || ^18.0.0
+
react-share@5.1.0:
resolution: {integrity: sha512-OvyfMtj/0UzH1wi90OdHhZVJ6WUC/+IeWvBwppeZozwIGyAjQgyR0QXlHOrxVHVECqnGvcpBaFTXVrqouTieaw==}
peerDependencies:
@@ -3532,7 +3561,7 @@ snapshots:
'@types/node': 20.5.1
chalk: 4.1.2
cosmiconfig: 8.3.6(typescript@5.5.4)
- cosmiconfig-typescript-loader: 4.4.0(@types/node@20.5.1)(cosmiconfig@8.3.6(typescript@5.5.4))(ts-node@10.9.2(@types/node@20.16.2)(typescript@5.5.4))(typescript@5.5.4)
+ cosmiconfig-typescript-loader: 4.4.0(@types/node@20.5.1)(cosmiconfig@8.3.6(typescript@5.5.4))(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.5.4))(typescript@5.5.4)
lodash.isplainobject: 4.0.6
lodash.merge: 4.6.2
lodash.uniq: 4.5.0
@@ -4629,7 +4658,7 @@ snapshots:
cookie@0.5.0: {}
- cosmiconfig-typescript-loader@4.4.0(@types/node@20.5.1)(cosmiconfig@8.3.6(typescript@5.5.4))(ts-node@10.9.2(@types/node@20.16.2)(typescript@5.5.4))(typescript@5.5.4):
+ cosmiconfig-typescript-loader@4.4.0(@types/node@20.5.1)(cosmiconfig@8.3.6(typescript@5.5.4))(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.5.4))(typescript@5.5.4):
dependencies:
'@types/node': 20.5.1
cosmiconfig: 8.3.6(typescript@5.5.4)
@@ -5324,6 +5353,14 @@ snapshots:
fraction.js@4.3.7: {}
+ framer-motion@11.9.0(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ dependencies:
+ tslib: 2.7.0
+ optionalDependencies:
+ '@emotion/is-prop-valid': 1.3.0
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+
fs-extra@11.2.0:
dependencies:
graceful-fs: 4.2.11
@@ -5721,6 +5758,15 @@ snapshots:
kind-of@6.0.3: {}
+ kmenu@2.0.0(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ dependencies:
+ framer-motion: 11.9.0(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ react: 18.3.1
+ react-scrollbar-size: 5.0.0(react@18.3.1)
+ transitivePeerDependencies:
+ - '@emotion/is-prop-valid'
+ - react-dom
+
language-subtag-registry@0.3.23: {}
language-tags@1.0.9:
@@ -6186,6 +6232,10 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.5
+ react-scrollbar-size@5.0.0(react@18.3.1):
+ dependencies:
+ react: 18.3.1
+
react-share@5.1.0(react@18.3.1):
dependencies:
classnames: 2.5.1
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index c5f993a..937f4d7 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,3 +1,5 @@
+import "@/styles/variables/kmenu.css";
+import "kmenu/dist/cmdk.css";
import "./globals.css";
import type { Metadata } from "next";
diff --git a/src/app/provider.tsx b/src/app/provider.tsx
index 81c35a7..eede66d 100644
--- a/src/app/provider.tsx
+++ b/src/app/provider.tsx
@@ -1,3 +1,7 @@
+"use client";
+import { MenuProvider } from "kmenu";
+
+import CommandPalette from "@/components/layout/header/command-palette";
import AuthProvider from "@/providers/auth-provider";
import ReactQueryProvider from "@/providers/react-query-provider";
@@ -8,7 +12,12 @@ interface Properties {
export default function Providers({ children }: Properties): JSX.Element {
return (
- {children}
+
+
+ {children}
+
+
+
);
}
diff --git a/src/components/layout/header/command-palette.tsx b/src/components/layout/header/command-palette.tsx
new file mode 100644
index 0000000..8fdfc1e
--- /dev/null
+++ b/src/components/layout/header/command-palette.tsx
@@ -0,0 +1,101 @@
+"use client";
+/* eslint-disable @typescript-eslint/strict-boolean-expressions */
+import {
+ type Command,
+ CommandMenu,
+ CommandWrapper,
+ type InnerCommand,
+ useCommands,
+ useKmenu,
+} from "kmenu";
+import { Github, Instagram, Search, Twitter, Youtube } from "lucide-react";
+import { type FC, useState } from "react";
+
+import { defaultPostsConstants } from "../../../constants/post.constants.ts";
+
+const CommandPalette: FC = () => {
+ const [postSearchResults, setPostSearchResults] = useState(
+ [],
+ );
+ const { setOpen, open } = useKmenu();
+
+ const getPostsWithQuery = (query?: string): InnerCommand[] => {
+
+ const filteredPosts = defaultPostsConstants.filter(
+ (defaultPostsConstant) =>
+ defaultPostsConstant.header
+ .toLowerCase()
+ .includes(query?.toLowerCase() ?? "") ||
+ defaultPostsConstant.content
+ .toLowerCase()
+ .includes(query?.toLowerCase() ?? ""),
+ );
+ const commands = filteredPosts.map((post) => ({
+ icon: ,
+ text: post.header,
+ perform: () => {
+ // handleSearch(post.header);
+ },
+ }));
+ setPostSearchResults(commands);
+ return commands;
+ };
+
+ const main: Command[] = [
+ {
+ category: "Search",
+ commands: [
+ {
+ icon: ,
+ text: "Search",
+ perform: () => {
+ setOpen(2);
+ },
+ },
+ ],
+ },
+ {
+ category: "Social",
+ commands: [
+ {
+ icon: ,
+ text: "Twitter",
+ perform: () => window.open('https://x.com/gelecekbilimde', '_blank'),
+ },
+ {
+ icon: ,
+ text: "Instagram",
+ perform: () => window.open('https://www.instagram.com/gelecekbilimde/', '_blank'),
+ },
+ {
+ icon: ,
+ text: "Youtube",
+ perform: () => window.open('https://www.youtube.com/gelecekbilimde', '_blank'),
+ },
+ ],
+ },
+ ];
+
+ const posts: Command[] = [
+ {
+ category: "Posts",
+ commands: postSearchResults,
+ },
+ ];
+
+ const [mainCommands] = useCommands(main);
+ const [postsCommands] = useCommands(posts);
+
+ return (
+
+
+
+
+ );
+};
+
+export default CommandPalette;
diff --git a/src/components/layout/header/index.tsx b/src/components/layout/header/index.tsx
index dfc551f..fa489e3 100644
--- a/src/components/layout/header/index.tsx
+++ b/src/components/layout/header/index.tsx
@@ -2,6 +2,7 @@
import "react-modern-drawer/dist/index.css";
+import { useKmenu } from "kmenu";
import { MenuIcon, SearchIcon } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
diff --git a/src/styles/variables/kmenu.css b/src/styles/variables/kmenu.css
new file mode 100644
index 0000000..2cff6e7
--- /dev/null
+++ b/src/styles/variables/kmenu.css
@@ -0,0 +1,99 @@
+.kmenu {
+ --backdrop-background: hsla(0, 0%, 100%, 0.565);
+ --backdrop-blur: blur(2px);
+
+ --menu-background: hsla(0, 0%, 100%, 1);
+ --menu-shadow: 0px 0px 60px 10px hsla(0, 0%, 0%, 0.05);
+ --menu-border: 1px solid #e2e2e2;
+ --menu-border-radius: 10px;
+ --menu-padding: 0.5rem;
+ --menu-transition: 100ms ease;
+ --menu-width: 600px;
+
+ --search-border: 1px solid #e2e2e2;
+ --search-color: hsla(0, 0%, 7%, 1);
+ --search-font-size: 16px;
+
+ --breadcrumb-font-size: 12px;
+ --breadcrumb-background: #fafafa;
+ --breadcrumb-color: #666666;
+ --breadcrumb-radius: 4px;
+ --breadcrumb-margin: 5px 0px 0px 5px;
+ --breadcrumb-padding: 5px 10px;
+ --breadcrumb-border: 1px solid #e2e2e2;
+
+ --command-font-size: 14px;
+ --command-icon-size: 16px;
+ --command-icon-margin: 8px;
+ --command-transition: 100ms linear;
+ --command-inactive-color: hsla(0, 0%, 51%, 1);
+ --command-active-color: hsla(0, 0%, 20%, 1);
+ --command-margin: 0px 0px 0px 14px;
+
+ --command-heading-font-size: 12px;
+ --command-heading-color: #666666;
+ --command-heading-margin: 5px 0 10px 10px;
+
+ --shortcut-inactive-color: hsla(0, 0%, 51%, 1);
+ --shortcut-active-color: hsla(0, 0%, 20%, 1);
+ --shortcut-inactive-background: hsla(0, 0%, 100%, 0);
+ --shortcut-active-background: hsla(0, 0%, 50%, 0.075);
+ --shortcut-border: 1px solid hsla(0, 0%, 89%, 1);
+ --shortcut-border-radius: 3px;
+ --shortcut-font-size: 12px;
+ --shortcut-size: 26px;
+
+ --checkbox-width: 20px;
+ --checkbox-height: 20px;
+ --checkbox-border-radius: 6px;
+ --checkbox-inactive-background: hsla(0, 0%, 100%, 0);
+ --checkbox-active-background: hsla(0, 0%, 100%, 0);
+ --checkbox-checked-background: hsla(210, 100%, 72%, 1);
+ --checkbox-checked-border: 1px solid hsla(210, 100%, 72%, 1);
+ --checkbox-inactive-border: 1px solid hsla(0, 0%, 89%, 1);
+ --checkbox-active-border: 1px solid #d5d5d5;
+ --checkbox-check-color: hsla(0, 0%, 100%, 1);
+ --checkbox-transition: 0.05s linear;
+
+ --selected-background: rgba(0, 0, 0, 0.05);
+ --selected-border-radius: 8px;
+ --selected-height: 50px;
+
+ --opening-animation-duration: 100ms;
+}
+
+html.dark .kmenu {
+ --backdrop-background: hsla(0, 0%, 0%, 0.565);
+
+ --menu-background: rgb(19, 19, 19);
+ --menu-shadow: none;
+ --menu-border: 1px solid hsla(0, 0%, 15%, 1);
+
+ --search-border: 1px solid hsla(0, 0%, 15%, 1);
+ --search-color: hsla(0, 0%, 100%, 1);
+
+ --breadcrumb-background: hsla(0, 0%, 14%, 1);
+ --breadcrumb-color: #666666;
+ --breadcrumb-border: 1px solid #242424;
+
+ --command-inactive-color: hsla(0, 0%, 40%, 1);
+ --command-active-color: hsla(0, 0%, 80%, 1);
+
+ --command-heading-color: #666666;
+
+ --shortcut-inactive-color: hsla(0, 0%, 51%, 1);
+ --shortcut-active-color: hsla(0, 0%, 80%, 1);
+ --shortcut-inactive-background: hsla(0, 0%, 100%, 0);
+ --shortcut-active-background: hsla(0, 0%, 10%, 1);
+ --shortcut-border: 1px solid hsla(0, 0%, 15%, 1);
+
+ --checkbox-inactive-background: hsla(0, 0%, 100%, 0);
+ --checkbox-active-background: hsla(0, 0%, 100%, 0);
+ --checkbox-checked-background: hsla(210, 100%, 72%, 1);
+ --checkbox-checked-border: 1px solid hsla(210, 100%, 72%, 1);
+ --checkbox-inactive-border: 1px solid hsla(0, 0%, 89%, 1);
+ --checkbox-active-border: 1px solid #d5d5d5;
+ --checkbox-check-color: hsla(0, 0%, 100%, 1);
+
+ --selected-background: hsla(0, 0%, 100%, 0.05);
+}