├── .github ├── FUNDING.yml └── workflows │ └── main.yaml ├── .gitignore ├── .husky └── commit-msg ├── .vscode ├── extensions.json └── settings.json ├── LICENSE.md ├── README.md ├── apps └── docs │ ├── .gitignore │ ├── .prettierignore │ ├── .prettierrc │ ├── .vscode │ ├── extensions.json │ └── settings.json │ ├── README.md │ ├── app │ ├── (home) │ │ ├── builders.tsx │ │ ├── card.tsx │ │ ├── codes.ts │ │ ├── hero.tsx │ │ ├── highlights.tsx │ │ ├── horizontal-card.tsx │ │ ├── layout.tsx │ │ ├── page.tsx │ │ └── separator.tsx │ ├── api │ │ ├── og │ │ │ ├── Geist-Bold.woff │ │ │ └── route.tsx │ │ └── search │ │ │ └── route.ts │ ├── docs │ │ ├── [[...slug]] │ │ │ └── page.tsx │ │ └── layout.tsx │ ├── global.css │ ├── layout.config.tsx │ ├── layout.tsx │ ├── sitemap.ts │ └── source.ts │ ├── components │ ├── code-block.tsx │ ├── copy-install.tsx │ ├── grid-pattern.tsx │ ├── icon.tsx │ ├── logo.tsx │ ├── meteors.tsx │ └── ui │ │ ├── button.tsx │ │ ├── icon.tsx │ │ └── links.tsx │ ├── content │ └── docs │ │ ├── builders │ │ ├── autocomplete.mdx │ │ ├── button.mdx │ │ ├── context-menu.mdx │ │ ├── group.mdx │ │ ├── modal.mdx │ │ ├── protector.mdx │ │ ├── select-menu.mdx │ │ ├── signal.mdx │ │ └── slash.mdx │ │ ├── getting-started.mdx │ │ ├── guides │ │ ├── implementing-cooldowns.mdx │ │ ├── interactions-handling.mdx │ │ ├── load-modules.mdx │ │ ├── meta.json │ │ ├── middlewares │ │ │ ├── admin-only.mdx │ │ │ ├── index.mdx │ │ │ ├── owner-only.mdx │ │ │ ├── verify-member-permissions.mdx │ │ │ └── verify-member-roles.mdx │ │ ├── registering-commands │ │ │ ├── dynamic.mdx │ │ │ ├── global.mdx │ │ │ ├── guilds.mdx │ │ │ └── index.mdx │ │ └── working-with-signals.mdx │ │ ├── index.mdx │ │ ├── meta.json │ │ ├── mutators │ │ ├── config.mdx │ │ ├── execute.mdx │ │ └── protect.mdx │ │ └── props.ts │ ├── icons │ ├── buymeacoffee.tsx │ ├── cross.tsx │ ├── discord.tsx │ ├── discordjs.tsx │ ├── github.tsx │ ├── index.ts │ ├── ko-fi.tsx │ └── npm.tsx │ ├── middleware.ts │ ├── next.config.mjs │ ├── package.json │ ├── postcss.config.js │ ├── public │ ├── banner.png │ ├── icon.svg │ ├── robots.txt │ └── simple-banner.png │ ├── scripts │ └── generate-docs.mts │ ├── source.config.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ └── utils │ ├── cn.ts │ ├── mdx-components.tsx │ └── metadata.ts ├── package.json ├── packages ├── create-sunar │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── LICENSE.md │ ├── README.md │ ├── biome.json │ ├── configs │ │ ├── static-biome.json │ │ ├── static-eslint-js.cjs │ │ ├── static-eslint-ts.cjs │ │ ├── static-prettier.json │ │ └── static-tsup.ts │ ├── package.json │ ├── src │ │ ├── helpers │ │ │ ├── copy-config.ts │ │ │ ├── copy-template.ts │ │ │ ├── get-dependencies.ts │ │ │ ├── get-features.ts │ │ │ ├── get-scripts.ts │ │ │ ├── is-empty-dir.ts │ │ │ ├── is-writeable.ts │ │ │ ├── node-version.ts │ │ │ ├── setup-features.ts │ │ │ └── validate-name.ts │ │ ├── index.ts │ │ ├── setup.ts │ │ ├── types.ts │ │ └── utils │ │ │ ├── constants.ts │ │ │ ├── dependencies.ts │ │ │ ├── features.ts │ │ │ ├── scripts.ts │ │ │ └── templates.ts │ ├── templates │ │ ├── javascript │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ └── src │ │ │ │ ├── commands │ │ │ │ └── avatar.js │ │ │ │ ├── index.js │ │ │ │ └── signals │ │ │ │ ├── interaction-create.js │ │ │ │ └── ready.js │ │ └── typescript │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── src │ │ │ ├── commands │ │ │ │ └── avatar.ts │ │ │ ├── index.ts │ │ │ └── signals │ │ │ │ ├── interaction-create.ts │ │ │ │ └── ready.ts │ │ │ └── tsconfig.json │ ├── tsconfig.json │ └── tsup.config.ts └── sunar │ ├── .gitignore │ ├── .vscode │ └── settings.json │ ├── LICENSE.md │ ├── README.md │ ├── biome.json │ ├── commitlint.config.ts │ ├── package.json │ ├── src │ ├── builders │ │ ├── autocomplete.ts │ │ ├── button.ts │ │ ├── contextMenu.ts │ │ ├── group.ts │ │ ├── index.ts │ │ ├── modal.ts │ │ ├── protector.ts │ │ ├── selectMenu.ts │ │ ├── signal.ts │ │ └── slash.ts │ ├── client.ts │ ├── handlers │ │ ├── autocomplete │ │ │ ├── autocomplete.ts │ │ │ └── index.ts │ │ ├── button │ │ │ ├── button.ts │ │ │ └── index.ts │ │ ├── contextMenu │ │ │ ├── contextMenu.ts │ │ │ └── index.ts │ │ ├── cooldown │ │ │ ├── cooldown.ts │ │ │ └── index.ts │ │ ├── group │ │ │ ├── group.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── interaction │ │ │ ├── index.ts │ │ │ └── interaction.ts │ │ ├── modal │ │ │ ├── index.ts │ │ │ └── modal.ts │ │ ├── protectors │ │ │ ├── index.ts │ │ │ └── protectors.ts │ │ ├── selectMenu │ │ │ ├── index.ts │ │ │ └── selectMenu.ts │ │ ├── signals │ │ │ ├── index.ts │ │ │ └── signals.ts │ │ └── slash │ │ │ ├── index.ts │ │ │ └── slash.ts │ ├── index.ts │ ├── modules │ │ ├── dirname │ │ │ ├── dirname.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── isESM │ │ │ ├── index.ts │ │ │ └── isESM.ts │ │ ├── load │ │ │ ├── index.ts │ │ │ ├── load.spec.ts │ │ │ └── load.ts │ │ ├── resolve │ │ │ ├── index.ts │ │ │ └── resolve.ts │ │ └── store │ │ │ ├── index.ts │ │ │ └── store.ts │ ├── mutators │ │ ├── config │ │ │ ├── config.spec.ts │ │ │ ├── config.ts │ │ │ └── index.ts │ │ ├── execute │ │ │ ├── execute.spec.ts │ │ │ ├── execute.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ └── protect │ │ │ ├── index.ts │ │ │ ├── protect.spec.ts │ │ │ └── protect.ts │ ├── registry │ │ ├── dynamic │ │ │ ├── dynamic.ts │ │ │ └── index.ts │ │ ├── global │ │ │ ├── global.ts │ │ │ └── index.ts │ │ ├── guild │ │ │ ├── guild.ts │ │ │ └── index.ts │ │ └── index.ts │ ├── stores │ │ ├── collections.ts │ │ ├── context.ts │ │ └── index.ts │ ├── symbols.ts │ ├── types │ │ ├── builder.ts │ │ ├── config.ts │ │ ├── cooldown.ts │ │ ├── index.ts │ │ ├── signals.ts │ │ └── utils.ts │ ├── utils │ │ ├── constants.ts │ │ ├── enums.ts │ │ ├── getApplicationCommands │ │ │ ├── getApplicationCommands.ts │ │ │ └── index.ts │ │ ├── getGroupStoreKey │ │ │ ├── getGroupStoreKey.ts │ │ │ └── index.ts │ │ ├── getSunarApplicationCommands │ │ │ ├── getSunarApplicationCommands.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── isAutocompleteBuilder │ │ │ ├── index.ts │ │ │ ├── isAutocompleteBuilder.spec.ts │ │ │ └── isAutocompleteBuilder.ts │ │ ├── isBuilder │ │ │ ├── index.ts │ │ │ └── isBuilder.ts │ │ ├── isButtonBuilder │ │ │ ├── index.ts │ │ │ ├── isButtonBuilder.spec.ts │ │ │ └── isButtonBuilder.ts │ │ ├── isContextMenuBuilder │ │ │ ├── index.ts │ │ │ ├── isContextMenuBuilder.spec.ts │ │ │ └── isContextMenuBuilder.ts │ │ ├── isGroupBuilder │ │ │ ├── index.ts │ │ │ ├── isGroupBuilder.spec.ts │ │ │ └── isGroupBuilder.ts │ │ ├── isModalBuilder │ │ │ ├── index.ts │ │ │ ├── isModalBuilder.spec.ts │ │ │ └── isModalBuilder.ts │ │ ├── isObject │ │ │ ├── index.ts │ │ │ ├── isObject.spec.ts │ │ │ └── isObject.ts │ │ ├── isRegex │ │ │ ├── index.ts │ │ │ ├── isRegex.spec.ts │ │ │ └── isRegex.ts │ │ ├── isSelectMenuBuilder │ │ │ ├── index.ts │ │ │ └── isSelectMenuBuilder.ts │ │ ├── isSignalBuilder │ │ │ ├── index.ts │ │ │ ├── isSignalBuilder.spec.ts │ │ │ └── isSignalBuilder.ts │ │ ├── isSlashBuilder │ │ │ ├── index.ts │ │ │ ├── isSlashBuilder.spec.ts │ │ │ └── isSlashBuilder.ts │ │ └── resolveCooldown │ │ │ ├── index.ts │ │ │ ├── resolveCooldown.spec.ts │ │ │ └── resolveCooldown.ts │ └── vitest │ │ ├── commands │ │ ├── _ignored.ts │ │ └── ping.ts │ │ └── signals │ │ └── ready.ts │ ├── tsconfig.json │ ├── tsup.config.ts │ └── vitest.config.ts ├── pnpm-lock.yaml ├── pnpm-workspace.yaml └── turbo.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: 4doist 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: 4doist 14 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 15 | -------------------------------------------------------------------------------- /.github/workflows/main.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - "**" 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: pnpm/action-setup@v4 13 | 14 | - run: pnpm install 15 | - run: pnpm run lint 16 | - run: pnpm run build -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Packages 2 | node_modules 3 | 4 | # Log files 5 | logs 6 | *.log 7 | npm-debug.log* 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | 14 | # Env 15 | .env 16 | 17 | # Dist 18 | dist 19 | dist-docs 20 | 21 | # Miscellaneous 22 | .tmp 23 | .vscode/* 24 | !.vscode/extensions.json 25 | !.vscode/settings.json 26 | .idea 27 | .DS_Store 28 | .turbo 29 | tsconfig.tsbuildinfo 30 | coverage 31 | out 32 | package.tgz 33 | tsup.config.bundled* 34 | vitest.config.ts.timestamp* 35 | 36 | # Deno 37 | deno.lock 38 | 39 | # Bun 40 | bun.lockb 41 | 42 | # yarn 43 | .pnp.* 44 | .yarn/* 45 | !.yarn/patches 46 | !.yarn/plugins 47 | !.yarn/releases 48 | !.yarn/sdks 49 | !.yarn/versions 50 | 51 | # Cache 52 | .prettiercache 53 | .eslintcache 54 | .vercel -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | pnpm exec commitlint --edit $1 -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "biomejs.biome", 4 | "bradlc.vscode-tailwindcss", 5 | "esbenp.prettier-vscode" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.defaultFormatter": "biomejs.biome", 3 | "typescript.tsdk": "node_modules\\typescript\\lib", 4 | 5 | "conventionalCommits.scopes": ["readme", "vscode", "jsdoc", "biome"], 6 | "cSpell.words": [ 7 | "autocompletes", 8 | "biomejs", 9 | "commitlint", 10 | "cooldowns", 11 | "Protectable", 12 | "Repliable", 13 | "sunar", 14 | "treeshake" 15 | ], 16 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Sunar JavaScript Community 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | Sunar simple banner 3 | Visit website 4 |
5 | 6 | # About 7 | Sunar emerges as a finely-tuned [discord.js](https://discord.js.org) framework, meticulously engineered to prioritize ease of use and efficiency. 8 | 9 | # Credits 10 | Special thanks to [discord.js](https://discord.js.org) for their incredible library that powers Sunar, to [Valibot](https://github.com/fabian-hiller/valibot) for inspiring our code structure, and to [Fumadocs](https://fumadocs.vercel.app/) for being an excellent framework for creating documentations. 11 | 12 | # License 13 | Completely free and licensed under the [MIT license](https://github.com/sunarjs/sunar/blob/main/README.md). But if you want, you can give me a star on [GitHub](https://github.com/sunarjs/sunar). -------------------------------------------------------------------------------- /apps/docs/.gitignore: -------------------------------------------------------------------------------- 1 | # deps 2 | /node_modules 3 | 4 | # generated content 5 | .map.ts 6 | .contentlayer 7 | 8 | # test & build 9 | /coverage 10 | /.next/ 11 | /out/ 12 | /build 13 | *.tsbuildinfo 14 | 15 | # misc 16 | .DS_Store 17 | *.pem 18 | /.pnp 19 | .pnp.js 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | 24 | # others 25 | .env*.local 26 | .vercel 27 | next-env.d.ts 28 | .vercel 29 | .source -------------------------------------------------------------------------------- /apps/docs/.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .next 3 | .turbo 4 | content/docs -------------------------------------------------------------------------------- /apps/docs/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all", 4 | "endOfLine": "auto", 5 | "semi": true, 6 | "tabWidth": 2, 7 | "printWidth": 80, 8 | "useTabs": true, 9 | "plugins": ["prettier-plugin-tailwindcss"] 10 | } 11 | -------------------------------------------------------------------------------- /apps/docs/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["bradlc.vscode-tailwindcss", "esbenp.prettier-vscode"] 3 | } 4 | -------------------------------------------------------------------------------- /apps/docs/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.wordWrap": "on", 3 | "editor.defaultFormatter": "esbenp.prettier-vscode", 4 | "cSpell.words": [ 5 | "docgen", 6 | "fumadocs", 7 | "jsxs", 8 | "Novatrix", 9 | "rehype", 10 | "uvcanvas" 11 | ], 12 | "typescript.tsdk": "node_modules\\typescript\\lib" 13 | } 14 | -------------------------------------------------------------------------------- /apps/docs/README.md: -------------------------------------------------------------------------------- 1 | # Sunar documentation 2 | 3 | This is a Next.js application. 4 | 5 | Run development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | pnpm dev 11 | # or 12 | yarn dev 13 | ``` 14 | 15 | Open http://localhost:3000 with your browser to see the result. 16 | 17 | ## Learn More 18 | 19 | To learn more about Next.js, take a look at the following 20 | resources: 21 | 22 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js 23 | features and API. 24 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 25 | -------------------------------------------------------------------------------- /apps/docs/app/(home)/codes.ts: -------------------------------------------------------------------------------- 1 | export const slashCommandCode = `import { Slash, execute } from 'sunar'; 2 | 3 | const slash = new Slash({ 4 | name: 'ping', 5 | description: 'Ping command', 6 | }); 7 | 8 | execute(slash, (interaction) => { 9 | interaction.reply({ content: 'Pong!' }); 10 | }); 11 | 12 | export { slash };`; 13 | 14 | export const contextMenuCode = `import { ContextMenu, execute } from 'sunar'; 15 | 16 | const contextMenu = new ContextMenu({ 17 | name: 'Ping', 18 | type: 2, 19 | }); 20 | 21 | execute(contextMenu, (interaction) => { 22 | interaction.reply({ content: 'Pong!' }); 23 | }); 24 | 25 | export { contextMenu };`; 26 | 27 | export const signalCode = `import { Signal, execute } from 'sunar'; 28 | 29 | const signal = new Signal('ready', { once: true }); 30 | 31 | execute(signal, () => { 32 | console.log('Logged in!'); 33 | }); 34 | 35 | export { signal };`; 36 | 37 | export const buttonCode = `import { Button, execute } from 'sunar'; 38 | 39 | const button = new Button({ id: 'my-button' }); 40 | 41 | execute(button, (interaction) => { 42 | interaction.reply({ content: 'Pong!' }); 43 | }); 44 | 45 | export { button };`; 46 | 47 | export const modalCode = `import { Modal, execute } from 'sunar'; 48 | 49 | const modal = new Modal({ id: 'my-modal' }); 50 | 51 | execute(modal, (interaction) => { 52 | interaction.reply({ content: 'Pong!' }); 53 | }); 54 | 55 | export { modal };`; 56 | 57 | export const selectMenuCode = `import { SelectMenu, execute } from 'sunar'; 58 | 59 | const selectMenu = new SelectMenu({ id: 'my-select-menu', type: 3 }); 60 | 61 | execute(selectMenu, (interaction) => { 62 | interaction.reply({ content: 'Pong!' }); 63 | }); 64 | 65 | export { selectMenu };`; 66 | -------------------------------------------------------------------------------- /apps/docs/app/(home)/hero.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link'; 2 | 3 | import { CopyInstall } from '@/components/copy-install'; 4 | import { GridPattern } from '@/components/grid-pattern'; 5 | import { Button } from '@/components/ui/button'; 6 | 7 | import { cn } from '@/utils/cn'; 8 | 9 | export function HomeHero() { 10 | return ( 11 |
12 | 22 |

span]:dark:from-muted-foreground [&>span]:dark:to-foreground [&>span]:dark:bg-gradient-to-t', 26 | '[&>span]:dark:to-40% [&>span]:dark:bg-clip-text [&>span]:dark:text-transparent', 27 | )} 28 | > 29 | Make Overpowered 30 | Discord Bots. 31 |

32 |

33 | Sunar emerges as a finely-tuned discord.js framework, meticulously 34 | engineered to prioritize{' '} 35 | 36 | ease of use and efficiency 37 | 38 | . 39 |

40 |
41 | 42 | 43 | 44 | 45 | 46 |
47 |
48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /apps/docs/app/(home)/highlights.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | FolderTreeIcon, 3 | GlobeIcon, 4 | type LucideIcon, 5 | MousePointerClickIcon, 6 | RabbitIcon, 7 | ScaleIcon, 8 | TypeIcon, 9 | } from 'lucide-react'; 10 | 11 | import { Cross } from '@/icons/cross'; 12 | 13 | export function HomeHighlights() { 14 | return ( 15 |
16 |
17 | 18 | Offers a simple and readable API, making Discord bot development 19 | straightforward for all users. 20 | 21 | 22 |
23 |
24 | 25 | Provides robust type definitions, ensuring developers in JavaScript 26 | and TypeScript enjoy a seamless experience. 27 | 28 | 29 |
30 |
31 | 32 | Prioritizes efficiency and performance while delivering essential 33 | Discord bot functionalities. 34 | 35 | 36 |
37 | 38 | 39 | Supports essential features like slash commands, context menu commands, 40 | and more for dynamic bot interactions. 41 | 42 | 43 | Sunar is open-source, allowing community contributions and customization 44 | to meet diverse bot development needs. 45 | 46 | 47 | Encourages clean and organized project architecture for easier 48 | maintenance and scalability. 49 | 50 |
51 | ); 52 | } 53 | 54 | interface HighlightPros extends React.PropsWithChildren { 55 | title: string; 56 | icon: LucideIcon; 57 | } 58 | 59 | export function Highlight({ icon: IconComp, title, children }: HighlightPros) { 60 | return ( 61 |
62 |
63 | 64 |

65 | {title} 66 |

67 |
68 |

{children}

69 |
70 | ); 71 | } 72 | -------------------------------------------------------------------------------- /apps/docs/app/(home)/horizontal-card.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link'; 2 | import type { PropsWithChildren } from 'react'; 3 | 4 | import Meteors from '@/components/meteors'; 5 | import { cn } from '@/utils/cn'; 6 | 7 | interface HorizontalSectionProps extends PropsWithChildren { 8 | title: string; 9 | description: React.ReactNode; 10 | docsLink: string; 11 | otherLink?: { name: string; link: string; external?: boolean }; 12 | withMeteors?: boolean; 13 | titleClass?: string; 14 | } 15 | 16 | export function HorizontalSection({ 17 | title, 18 | description, 19 | docsLink, 20 | otherLink, 21 | withMeteors, 22 | titleClass, 23 | children, 24 | }: HorizontalSectionProps) { 25 | return ( 26 |
32 | {withMeteors && } 33 |
34 |

40 | {title} 41 |

42 |

43 | {description} 44 |

45 |
46 | 53 | Read more 54 | 55 | {otherLink && ( 56 | 62 | {otherLink.name} 63 | 64 | )} 65 |
66 |
67 | {children} 68 |
69 | ); 70 | } 71 | -------------------------------------------------------------------------------- /apps/docs/app/(home)/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { ReactNode } from 'react'; 2 | import { HomeLayout as Layout } from 'fumadocs-ui/home-layout'; 3 | 4 | import { baseOptions } from '@/app/layout.config'; 5 | 6 | export default function HomeLayout({ 7 | children, 8 | }: { 9 | children: ReactNode; 10 | }): React.ReactElement { 11 | return ( 12 |
13 | 14 | {children} 15 |
18 | ); 19 | } 20 | 21 | function Footer(): React.ReactElement { 22 | return ( 23 | 49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /apps/docs/app/(home)/page.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link'; 2 | 3 | import { HomeHero } from '@/app/(home)/hero'; 4 | import { HomeBuilders } from '@/app/(home)/builders'; 5 | 6 | import { HomeHighlights } from './highlights'; 7 | import { Separator } from './separator'; 8 | 9 | import { CopyInstall } from '@/components/copy-install'; 10 | import { Button } from '@/components/ui/button'; 11 | 12 | export default function HomePage() { 13 | return ( 14 |
15 | 16 | 17 |
18 |

19 | Build your bot at 20 |
21 | the speed of thought. 22 |

23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |

33 | Start creating your bot now. 34 |

35 |
36 | 37 | 38 | 39 | 40 | 41 |
42 |
43 |
44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /apps/docs/app/(home)/separator.tsx: -------------------------------------------------------------------------------- 1 | export function Separator() { 2 | return
; 3 | } 4 | 5 | export function SectionTitle({ content }: { content: React.ReactNode }) { 6 | return ( 7 |
8 |

9 | {content} 10 |

11 |
12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /apps/docs/app/api/og/Geist-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunarjs/sunar/293cf286523fec49f009f5585cef425cf3e31a2d/apps/docs/app/api/og/Geist-Bold.woff -------------------------------------------------------------------------------- /apps/docs/app/api/search/route.ts: -------------------------------------------------------------------------------- 1 | import { source } from '@/app/source'; 2 | import { createSearchAPI } from 'fumadocs-core/search/server'; 3 | 4 | export const { GET } = createSearchAPI('advanced', { 5 | indexes: source.getPages().map((page) => ({ 6 | title: page.data.title, 7 | structuredData: page.data.structuredData, 8 | id: page.url, 9 | url: page.url, 10 | })), 11 | }); 12 | -------------------------------------------------------------------------------- /apps/docs/app/docs/[[...slug]]/page.tsx: -------------------------------------------------------------------------------- 1 | import { notFound } from 'next/navigation'; 2 | import { 3 | DocsBody, 4 | DocsCategory, 5 | DocsDescription, 6 | DocsPage, 7 | DocsTitle, 8 | } from 'fumadocs-ui/page'; 9 | 10 | import { GitHubIcon } from '@/icons'; 11 | import { source } from '@/app/source'; 12 | import { createMetadata } from '@/utils/metadata'; 13 | import { mdxComponents } from '@/utils/mdx-components'; 14 | 15 | interface Params { 16 | slug: string[]; 17 | } 18 | 19 | export const dynamicParams = false; 20 | 21 | export default async function Page({ params }: { params: Params }) { 22 | const page = source.getPage(params.slug); 23 | 24 | if (page == null) notFound(); 25 | 26 | const path = `apps/docs/content/docs/${page.file.path}`; 27 | 28 | const footer = ( 29 | 35 | 36 | Edit on Github ↗ 37 | 38 | ); 39 | 40 | return ( 41 | 51 | {page.data.title} 52 | {page.data.description} 53 | 54 | 55 | {page.data.index ? ( 56 | 57 | ) : null} 58 | 59 | 60 | ); 61 | } 62 | 63 | export function generateStaticParams(): Params[] { 64 | return source.generateParams(); 65 | } 66 | 67 | export async function generateMetadata({ params }: { params: Params }) { 68 | const page = source.getPage(params.slug); 69 | 70 | if (page == null) notFound(); 71 | 72 | const description = 73 | page.data.description ?? 74 | 'The library for building overpowered discord bots with discord.js.'; 75 | 76 | const imageParams = new URLSearchParams(); 77 | imageParams.set('title', page.data.title); 78 | imageParams.set('description', description); 79 | 80 | const image = { 81 | alt: 'Banner', 82 | url: `/api/og?${imageParams.toString()}`, 83 | width: 1200, 84 | height: 630, 85 | }; 86 | 87 | return createMetadata({ 88 | title: page.data.title, 89 | description, 90 | openGraph: { 91 | url: `/docs/${page.slugs.join('/')}`, 92 | images: image, 93 | }, 94 | twitter: { 95 | images: image, 96 | }, 97 | }); 98 | } 99 | -------------------------------------------------------------------------------- /apps/docs/app/docs/layout.tsx: -------------------------------------------------------------------------------- 1 | import { DocsLayout } from 'fumadocs-ui/layout'; 2 | import type { ReactNode } from 'react'; 3 | 4 | import { source } from '@/app/source'; 5 | import { baseOptions } from '@/app/layout.config'; 6 | 7 | export default function Layout({ children }: { children: ReactNode }) { 8 | return ( 9 | 14 | {children} 15 | 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /apps/docs/app/global.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --primary: 10 85% 59%; 7 | } 8 | -------------------------------------------------------------------------------- /apps/docs/app/layout.config.tsx: -------------------------------------------------------------------------------- 1 | import type { B as BaseLayoutProps } from 'fumadocs-ui/dist/layout.shared-DEQFTB9M'; 2 | 3 | import { Logo } from '@/components/logo'; 4 | import { SunarIcon } from '@/components/icon'; 5 | 6 | export const baseOptions: BaseLayoutProps = { 7 | githubUrl: 'https://github.com/sunarjs/sunar', 8 | nav: { 9 | title: , 10 | transparentMode: 'top', 11 | }, 12 | links: [ 13 | { 14 | text: 'Documentation', 15 | icon: , 16 | url: '/docs', 17 | active: 'nested-url', 18 | }, 19 | ], 20 | }; 21 | -------------------------------------------------------------------------------- /apps/docs/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import '@/app/global.css'; 2 | 3 | import type { ReactNode } from 'react'; 4 | import { GeistSans } from 'geist/font/sans'; 5 | import { GeistMono } from 'geist/font/mono'; 6 | import { RootProvider } from 'fumadocs-ui/provider'; 7 | import { Analytics } from '@vercel/analytics/react'; 8 | 9 | import { cn } from '@/utils/cn'; 10 | import { baseUrl, createMetadata } from '@/utils/metadata'; 11 | 12 | export const metadata = createMetadata({ 13 | title: { 14 | template: '%s | Sunar', 15 | default: 'Sunar', 16 | }, 17 | description: 'The discord.js framework for building discord bots.', 18 | metadataBase: baseUrl, 19 | }); 20 | 21 | export default function Layout({ children }: { children: ReactNode }) { 22 | return ( 23 | 28 | 29 | {children} 30 | 31 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /apps/docs/app/sitemap.ts: -------------------------------------------------------------------------------- 1 | import type { MetadataRoute } from 'next'; 2 | import { baseUrl } from '@/utils/metadata'; 3 | import { source } from '@/app/source'; 4 | 5 | export default function sitemap(): MetadataRoute.Sitemap { 6 | const url = (path: string): string => new URL(path, baseUrl).toString(); 7 | 8 | return [ 9 | { 10 | url: url('/'), 11 | changeFrequency: 'monthly', 12 | priority: 1, 13 | }, 14 | { 15 | url: url('/docs'), 16 | changeFrequency: 'monthly', 17 | priority: 0.8, 18 | }, 19 | ...source.getPages().map((page) => ({ 20 | url: url(page.url), 21 | lastModified: page.data.lastModified 22 | ? new Date(page.data.lastModified) 23 | : undefined, 24 | changeFrequency: 'weekly', 25 | priority: 0.5, 26 | })), 27 | ]; 28 | } 29 | -------------------------------------------------------------------------------- /apps/docs/app/source.ts: -------------------------------------------------------------------------------- 1 | import { icons } from 'lucide-react'; 2 | import { createElement } from 'react'; 3 | import { 4 | type InferMetaType, 5 | type InferPageType, 6 | loader, 7 | } from 'fumadocs-core/source'; 8 | import { createMDXSource } from 'fumadocs-mdx'; 9 | 10 | import { meta, docs } from '@/.source'; 11 | import { IconContainer } from '@/components/ui/icon'; 12 | 13 | export const source = loader({ 14 | baseUrl: '/docs', 15 | icon(icon) { 16 | if (icon && icon in icons) 17 | return createElement(IconContainer, { 18 | icon: icons[icon as keyof typeof icons], 19 | }); 20 | }, 21 | source: createMDXSource(docs, meta), 22 | }); 23 | 24 | export type Page = InferPageType; 25 | export type Meta = InferMetaType; 26 | -------------------------------------------------------------------------------- /apps/docs/components/code-block.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from '@/utils/cn'; 2 | import * as Base from 'fumadocs-ui/components/codeblock'; 3 | import { Jsx, toJsxRuntime } from 'hast-util-to-jsx-runtime'; 4 | import { Fragment, type HTMLAttributes } from 'react'; 5 | import { 6 | BundledLanguage, 7 | LanguageInput, 8 | SpecialLanguage, 9 | StringLiteralUnion, 10 | codeToHast, 11 | } from 'shiki'; 12 | import { jsx, jsxs } from 'react/jsx-runtime'; 13 | 14 | const langs = ['js'] satisfies ( 15 | | LanguageInput 16 | | SpecialLanguage 17 | | StringLiteralUnion 18 | )[]; 19 | 20 | export type CodeBlockProps = HTMLAttributes & { 21 | code: string; 22 | wrapper?: Base.CodeBlockProps; 23 | lang: (typeof langs)[number]; 24 | }; 25 | 26 | export async function CodeBlock({ 27 | code, 28 | lang, 29 | wrapper, 30 | ...props 31 | }: CodeBlockProps) { 32 | const hast = await codeToHast(code, { 33 | lang, 34 | defaultColor: false, 35 | themes: { 36 | light: 'min-light', 37 | dark: 'github-dark-default', 38 | }, 39 | }); 40 | 41 | const rendered = toJsxRuntime(hast, { 42 | jsx: jsx as Jsx, 43 | jsxs: jsxs as Jsx, 44 | Fragment, 45 | development: false, 46 | components: { 47 | // @ts-expect-error -- JSX component 48 | pre: Base.Pre, 49 | }, 50 | }); 51 | 52 | return ( 53 | 58 | {rendered} 59 | 60 | ); 61 | } 62 | 63 | interface PrettyCodeBlockOptions extends CodeBlockProps { 64 | background: React.ReactNode; 65 | container?: React.DetailedHTMLProps< 66 | React.HTMLAttributes, 67 | HTMLDivElement 68 | >; 69 | } 70 | 71 | export function PrettyCodeBlock({ 72 | background, 73 | container, 74 | ...props 75 | }: PrettyCodeBlockOptions) { 76 | return ( 77 |
div>canvas]:hidden [&>div>canvas]:sm:block', 81 | container?.className, 82 | )} 83 | > 84 | {background} 85 | 94 |
95 | ); 96 | } 97 | -------------------------------------------------------------------------------- /apps/docs/components/copy-install.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { CheckIcon, CopyIcon, TerminalIcon } from 'lucide-react'; 4 | import { useState } from 'react'; 5 | 6 | import { cn } from '@/utils/cn'; 7 | 8 | const content = 'npm create sunar'; 9 | 10 | export function CopyInstall( 11 | props: React.DetailedHTMLProps< 12 | React.HTMLAttributes, 13 | HTMLButtonElement 14 | >, 15 | ) { 16 | const [copied, setCopied] = useState(false); 17 | 18 | const handleClick = async () => { 19 | try { 20 | await navigator.clipboard.writeText(content); 21 | 22 | setCopied(true); 23 | 24 | setTimeout(() => { 25 | setCopied(false); 26 | }, 3000); 27 | } catch (error) { 28 | setCopied(false); 29 | console.error(error); 30 | } 31 | }; 32 | 33 | return ( 34 | 61 | ); 62 | } 63 | -------------------------------------------------------------------------------- /apps/docs/components/grid-pattern.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from '@/utils/cn'; 2 | import { useId } from 'react'; 3 | 4 | interface GridPatternProps { 5 | width?: any; 6 | height?: any; 7 | x?: any; 8 | y?: any; 9 | squares?: Array<[x: number, y: number]>; 10 | strokeDasharray?: any; 11 | className?: string; 12 | [key: string]: any; 13 | } 14 | 15 | export function GridPattern({ 16 | width = 40, 17 | height = 40, 18 | x = -1, 19 | y = -1, 20 | strokeDasharray = 0, 21 | squares, 22 | className, 23 | ...props 24 | }: GridPatternProps) { 25 | const id = useId(); 26 | 27 | return ( 28 | 68 | ); 69 | } 70 | -------------------------------------------------------------------------------- /apps/docs/components/icon.tsx: -------------------------------------------------------------------------------- 1 | export function SunarIcon(props: React.SVGProps) { 2 | return ( 3 | 11 | 17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /apps/docs/components/logo.tsx: -------------------------------------------------------------------------------- 1 | import { SunarIcon } from './icon'; 2 | 3 | export function Logo(props: React.SVGProps) { 4 | return ( 5 |
9 | 10 | Sunar 11 |
12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /apps/docs/components/meteors.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { cn } from '@/utils/cn'; 4 | import { useEffect, useState } from 'react'; 5 | 6 | interface MeteorsProps { 7 | number?: number; 8 | } 9 | export const Meteors = ({ number = 20 }: MeteorsProps) => { 10 | const [meteorStyles, setMeteorStyles] = useState>( 11 | [], 12 | ); 13 | 14 | useEffect(() => { 15 | const styles = [...new Array(number)].map(() => ({ 16 | top: -5, 17 | left: Math.floor(Math.random() * window.innerWidth) + 'px', 18 | animationDelay: Math.random() * 1 + 0.2 + 's', 19 | animationDuration: Math.floor(Math.random() * 8 + 2) + 's', 20 | })); 21 | setMeteorStyles(styles); 22 | }, [number]); 23 | 24 | return ( 25 | <> 26 | {[...meteorStyles].map((style, idx) => ( 27 | // Meteor Head 28 | 35 | {/* Meteor Tail */} 36 |
37 | 38 | ))} 39 | 40 | ); 41 | }; 42 | 43 | export default Meteors; 44 | -------------------------------------------------------------------------------- /apps/docs/components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from '@/utils/cn'; 2 | 3 | export interface ButtonProps 4 | extends React.DetailedHTMLProps< 5 | React.HTMLAttributes, 6 | HTMLButtonElement 7 | > {} 8 | 9 | export function Button(props?: ButtonProps) { 10 | return ( 11 |