@ardly/bunext 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/.eslintrc.json +8 -0
  2. package/.prettierignore +4 -0
  3. package/README.md +121 -0
  4. package/STRUCTURE.md +77 -0
  5. package/bin/cli.js +67 -0
  6. package/components.json +21 -0
  7. package/next.config.ts +22 -0
  8. package/package.json +59 -0
  9. package/postcss.config.mjs +8 -0
  10. package/prettier.config.js +7 -0
  11. package/public/android-chrome-192x192.png +0 -0
  12. package/public/android-chrome-512x512.png +0 -0
  13. package/public/favicon.svg +1 -0
  14. package/public/loading-dots.gif +0 -0
  15. package/public/logo.svg +1 -0
  16. package/public/ogImage.webp +0 -0
  17. package/public/site.webmanifest +19 -0
  18. package/src/actions/placeholder.ts +30 -0
  19. package/src/actions/sampleAction.ts +39 -0
  20. package/src/app/(index)/intr/TestCard.tsx +31 -0
  21. package/src/app/(index)/intr/page.tsx +17 -0
  22. package/src/app/(index)/page.tsx +156 -0
  23. package/src/app/(index)/pagetr/page.tsx +37 -0
  24. package/src/app/error-wrapper.tsx +32 -0
  25. package/src/app/global-error.tsx +53 -0
  26. package/src/app/layout.tsx +56 -0
  27. package/src/app/loading.tsx +11 -0
  28. package/src/app/not-found.tsx +45 -0
  29. package/src/app/sitemap.ts +14 -0
  30. package/src/components/Providers/root-provider.tsx +22 -0
  31. package/src/components/Providers/theme-provider.tsx +27 -0
  32. package/src/components/TestComp.tsx +11 -0
  33. package/src/components/brand.tsx +35 -0
  34. package/src/components/navigation/footer.tsx +32 -0
  35. package/src/components/navigation/main-nav.tsx +55 -0
  36. package/src/components/navigation/mobile-nav.tsx +154 -0
  37. package/src/components/navigation/site-header.tsx +67 -0
  38. package/src/components/ui/avatar.tsx +50 -0
  39. package/src/components/ui/badge.tsx +36 -0
  40. package/src/components/ui/button.tsx +56 -0
  41. package/src/components/ui/card.tsx +79 -0
  42. package/src/components/ui/command.tsx +153 -0
  43. package/src/components/ui/dialog.tsx +122 -0
  44. package/src/components/ui/drawer.tsx +118 -0
  45. package/src/components/ui/dropdown-menu.tsx +200 -0
  46. package/src/components/ui/input.tsx +22 -0
  47. package/src/components/ui/label.tsx +26 -0
  48. package/src/components/ui/multi-select.tsx +380 -0
  49. package/src/components/ui/origin/multiselect.tsx +645 -0
  50. package/src/components/ui/popover.tsx +31 -0
  51. package/src/components/ui/radio-group.tsx +44 -0
  52. package/src/components/ui/separator.tsx +31 -0
  53. package/src/components/ui/skeleton.tsx +15 -0
  54. package/src/components/ui/themeSelector.tsx +157 -0
  55. package/src/components/ui/toast.tsx +129 -0
  56. package/src/components/ui/toaster.tsx +31 -0
  57. package/src/components/ui/tooltip.tsx +39 -0
  58. package/src/components/utils/ConditionalLink.tsx +46 -0
  59. package/src/components/utils/Image.tsx +57 -0
  60. package/src/components/utils/Img.tsx +104 -0
  61. package/src/components/utils/Spinner.tsx +29 -0
  62. package/src/components/utils/TopButton.tsx +67 -0
  63. package/src/components/utils/TransitionLink.tsx +67 -0
  64. package/src/components/utils/copy.tsx +98 -0
  65. package/src/components/utils/featureFlag.tsx +22 -0
  66. package/src/components/utils/icons.tsx +155 -0
  67. package/src/components/utils/share-modal.tsx +159 -0
  68. package/src/hooks/use-intersection.ts +52 -0
  69. package/src/hooks/use-lazy-load.ts +33 -0
  70. package/src/hooks/use-meta-color.ts +25 -0
  71. package/src/hooks/use-toast.ts +191 -0
  72. package/src/lib/config/featureflags.ts +63 -0
  73. package/src/lib/config/siteConfig.ts +172 -0
  74. package/src/lib/config/user.ts +9 -0
  75. package/src/lib/data/footer-data.ts +85 -0
  76. package/src/lib/data/nav-data.ts +30 -0
  77. package/src/lib/data/siteData.ts +52 -0
  78. package/src/lib/utils/index.ts +190 -0
  79. package/src/styles/customGlobal.css +141 -0
  80. package/src/styles/globals.css +72 -0
  81. package/src/styles/tailwind/base.ts +46 -0
  82. package/src/styles/tailwind/fonts/ClashDisplay-Bold.eot +0 -0
  83. package/src/styles/tailwind/fonts/ClashDisplay-Bold.ttf +0 -0
  84. package/src/styles/tailwind/fonts/ClashDisplay-Bold.woff +0 -0
  85. package/src/styles/tailwind/fonts/ClashDisplay-Bold.woff2 +0 -0
  86. package/src/styles/tailwind/fonts/ClashDisplay-Extralight.eot +0 -0
  87. package/src/styles/tailwind/fonts/ClashDisplay-Extralight.ttf +0 -0
  88. package/src/styles/tailwind/fonts/ClashDisplay-Extralight.woff +0 -0
  89. package/src/styles/tailwind/fonts/ClashDisplay-Extralight.woff2 +0 -0
  90. package/src/styles/tailwind/fonts/ClashDisplay-Light.eot +0 -0
  91. package/src/styles/tailwind/fonts/ClashDisplay-Light.ttf +0 -0
  92. package/src/styles/tailwind/fonts/ClashDisplay-Light.woff +0 -0
  93. package/src/styles/tailwind/fonts/ClashDisplay-Light.woff2 +0 -0
  94. package/src/styles/tailwind/fonts/ClashDisplay-Medium.eot +0 -0
  95. package/src/styles/tailwind/fonts/ClashDisplay-Medium.ttf +0 -0
  96. package/src/styles/tailwind/fonts/ClashDisplay-Medium.woff +0 -0
  97. package/src/styles/tailwind/fonts/ClashDisplay-Medium.woff2 +0 -0
  98. package/src/styles/tailwind/fonts/ClashDisplay-Regular.eot +0 -0
  99. package/src/styles/tailwind/fonts/ClashDisplay-Regular.ttf +0 -0
  100. package/src/styles/tailwind/fonts/ClashDisplay-Regular.woff +0 -0
  101. package/src/styles/tailwind/fonts/ClashDisplay-Regular.woff2 +0 -0
  102. package/src/styles/tailwind/fonts/ClashDisplay-Semibold.eot +0 -0
  103. package/src/styles/tailwind/fonts/ClashDisplay-Semibold.ttf +0 -0
  104. package/src/styles/tailwind/fonts/ClashDisplay-Semibold.woff +0 -0
  105. package/src/styles/tailwind/fonts/ClashDisplay-Semibold.woff2 +0 -0
  106. package/src/styles/tailwind/fonts/ClashDisplay-Variable.eot +0 -0
  107. package/src/styles/tailwind/fonts/ClashDisplay-Variable.ttf +0 -0
  108. package/src/styles/tailwind/fonts/ClashDisplay-Variable.woff +0 -0
  109. package/src/styles/tailwind/fonts/ClashDisplay-Variable.woff2 +0 -0
  110. package/src/styles/tailwind/fonts/GeistMonoVF.woff +0 -0
  111. package/src/styles/tailwind/fonts/GeistVF.woff +0 -0
  112. package/src/styles/tailwind/fonts.ts +51 -0
  113. package/src/styles/tailwind/tailwindUtils.ts +29 -0
  114. package/src/types/index.ts +80 -0
  115. package/tailwind.config.ts +104 -0
  116. package/tsconfig.json +28 -0
  117. package/vercel.json +6 -0
package/.eslintrc.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": ["next/core-web-vitals", "next/typescript", "prettier"],
3
+ "rules": {
4
+ "@typescript-eslint/no-unused-vars": "off",
5
+ "@next/next/no-img-element": "off",
6
+ "@typescript-eslint/no-explicit-any": "off"
7
+ }
8
+ }
@@ -0,0 +1,4 @@
1
+ node_modules
2
+ .next
3
+ build
4
+ dist
package/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # Bunext
2
+
3
+ A Next.js 15 app with Tailwind CSS template. [Live deployment](https://bunext.ardastroid.com/)
4
+
5
+ ## Usage (run locally)
6
+
7
+ > required `bun` or `nodejs` installed and make sure they're up to date
8
+
9
+ Go to the `root` folder where `package.json` exists.
10
+
11
+ ```bash
12
+ bun install
13
+ ```
14
+
15
+ ```bash
16
+ npm install
17
+ ```
18
+
19
+ ### Then
20
+
21
+ ```bash
22
+ bun --bun run dev
23
+ ```
24
+
25
+ ```bash
26
+ bun run dev
27
+ ```
28
+
29
+ ```bash
30
+ npm run dev
31
+ ```
32
+
33
+ ## Features
34
+
35
+ - Next.js 15 App Directory
36
+ - Tailwind CSS
37
+ - [Shadcn](https://ui.shadcn.com/) components
38
+ - Custom util components like `share modal, multi-select(no library), Img, Icons, etc`
39
+ - CustomFont Optimization using [Next font](https://nextjs.org/docs/pages/building-your-application/optimizing/fonts)
40
+ - Icons using [lucide-react](https://lucide.dev/)
41
+ - Next theme provider (dark and light mode)
42
+ - Url stage management using [nuqs](https://nuqs.47ng.com/)
43
+ - Tailwind css only animations using [tailwindcss-motion](https://docs.rombo.co/tailwind)
44
+ - Feature flags
45
+ - Metadata generator for SEO (including apple-touch-icon)
46
+ - [zod](https://zod.dev/) validation
47
+ - Per Link page transition (without any library)
48
+ - Custom Image components with lazy loading and auto generated placeholder (worsk with or without `next/image`)
49
+ - [Biome](https://biomejs.dev/) for linting and formatting
50
+ - [Fluid Tailwind](https://fluid.tw/) for easier responsive design (disabled by default, to enable go to `tailwind.config.ts` and uncomment the fluid plugin variables, Note: the `min-*` and `max-*` variants don't work while using fluid-tailwind)
51
+ - Utilities like `qrCode gen, string shortner, uniqueCode gen, img placeholder, email validation, hashing etc`
52
+
53
+ ## Config
54
+
55
+ - for generating colors use [realtime-colors](https://www.realtimecolors.com/) shadcn template and pase it on `src/styles/globals.css`
56
+ - add fonts on `src/styles/tailwind/fonts.ts`
57
+ - to configure feature flags got to `src/lib/config/featureflags.ts`
58
+ - to configure Metadata got to `src/lib/data/siteData.ts`
59
+ - advance Metadata config in `src/lib/config/siteConfig.ts`
60
+ - for base styles (scrollbar style, selection highlighting etc) go to `src/styles/tailwind/base.ts`
61
+
62
+ ## Roadmap
63
+
64
+ - [x] add next themes
65
+ - [x] feature flags
66
+ - [x] add sample responsive nav
67
+ - [ ] add sample footer
68
+ - [ ] add sample server actions
69
+ - [ ] add syntax highlighting for code blocks
70
+ - [ ] add a branch with animation features using motion
71
+ - [ ] add a feature full branch with drizzle orm, analytics, auth
72
+
73
+ ### Multi-select sample code
74
+
75
+ ```tsx
76
+ 'use client'
77
+ import React, { useState } from 'react'
78
+ import { MultiSelect } from '@/components/ui/multi-select'
79
+
80
+ const catsList = [
81
+ { value: 'persian', label: 'Persian Cat' },
82
+ { value: 'siamese', label: 'Siamese Cat' },
83
+ { value: 'maine-coon', label: 'Maine Coon' },
84
+ { value: 'ragdoll', label: 'Ragdoll' },
85
+ { value: 'bengal', label: 'Bengal Cat' },
86
+ ]
87
+
88
+ function Home() {
89
+ const [selectedCats, setSelectedCats] = useState<string[]>([
90
+ 'persian',
91
+ 'siamese',
92
+ ])
93
+
94
+ return (
95
+ <div className="max-w-xl p-4">
96
+ <h1 className="mb-4 text-2xl font-bold">Multi-Select Component</h1>
97
+ <MultiSelect
98
+ options={catsList}
99
+ onValueChange={setSelectedCats}
100
+ defaultValue={selectedCats}
101
+ placeholder="Select cats"
102
+ variant="inverted"
103
+ animation={2}
104
+ maxCount={3}
105
+ />
106
+ <div className="mt-4">
107
+ <h2 className="text-xl font-semibold">Selected Cats:</h2>
108
+ <ul className="list-inside list-disc">
109
+ {selectedCats.map((cat) => (
110
+ <li key={cat}>{cat}</li>
111
+ ))}
112
+ </ul>
113
+ </div>
114
+ </div>
115
+ )
116
+ }
117
+ ```
118
+
119
+ <!-- ### [Conventions](./CONVENTION.md) -->
120
+ <!-- ## License
121
+ Licensed under the [MIT license](./LICENSE). -->
package/STRUCTURE.md ADDED
@@ -0,0 +1,77 @@
1
+ ## Bunext Folder Structure
2
+
3
+ ```sh
4
+ ├── .env
5
+ ├── .env.example
6
+ ├── .eslintrc.json
7
+ ├── .gitignore
8
+ ├── CONVENTION.md
9
+ ├── README.md
10
+ ├── biome.json
11
+ ├── bun.lockb
12
+ ├── components.json
13
+ ├── next-env.d.ts
14
+ ├── next.config.ts
15
+ ├── package.json
16
+ ├── postcss.config.mjs
17
+ ├── tailwind.config.ts
18
+ ├── tsconfig.json
19
+ ├── public/
20
+ └── src/
21
+ ├── actions/
22
+ ├── app/
23
+ │ ├── (index)/
24
+ │ │ ├── intr/
25
+ │ │ ├── pagetr/
26
+ │ │ └── page.tsx
27
+ │ ├── layout.tsx
28
+ │ ├── not-found.tsx
29
+ │ └── sitemap.ts
30
+ ├── components/
31
+ │ ├── Providers/ # providers for the app
32
+ │ │ ├── root-provider.tsx
33
+ │ │ └── theme-provider.tsx
34
+ │ ├── navigation/ # navigation components
35
+ │ │ ├── footer.tsx
36
+ │ │ ├── main-nav.tsx
37
+ │ │ ├── mobile-nav.tsx
38
+ │ │ └── site-header.tsx
39
+ │ ├── ui/ # shadcn-ui components
40
+ │ ├── utils/ # custom utility components
41
+ │ │ ├── ConditionalLink.tsx
42
+ │ │ ├── Image.tsx
43
+ │ │ ├── Img.tsx
44
+ │ │ ├── Spinner.tsx
45
+ │ │ ├── TopButton.tsx
46
+ │ │ ├── TransitionLink.tsx
47
+ │ │ ├── featureFlag.tsx
48
+ │ │ ├── icons.tsx
49
+ │ │ └── share-modal.tsx
50
+ │ ├── brand.tsx
51
+ │ └── TestComp.tsx
52
+ ├── hooks/
53
+ │ ├── use-intersection.ts
54
+ │ ├── use-lazy-load.ts
55
+ │ ├── use-meta-color.ts
56
+ │ └── use-toast.ts
57
+ ├── lib/
58
+ │ ├── config/
59
+ │ │ ├── featureflags.ts
60
+ │ │ ├── siteConfig.ts
61
+ │ │ └── user.ts
62
+ │ ├── data/ # data files for core site config & seo
63
+ │ │ ├── footer-data.ts
64
+ │ │ ├── nav-data.ts
65
+ │ │ └── siteData.ts
66
+ │ └── utils.ts # utility functions
67
+ ├── styles/
68
+ │ ├── customGlobal.css
69
+ │ ├── globals.css
70
+ │ └── tailwind/
71
+ │ ├── base.ts
72
+ │ ├── fonts.ts
73
+ │ ├── tailwindUtils.ts
74
+ │ └── fonts/
75
+ └── types/
76
+ └── index.ts
77
+ ```
package/bin/cli.js ADDED
@@ -0,0 +1,67 @@
1
+ #! /usr/bin/env node
2
+
3
+ import { execSync } from 'child_process'
4
+ import readline from 'readline'
5
+
6
+ const runCommand = (command) => {
7
+ try {
8
+ execSync(`${command}`, { stdio: 'inherit' })
9
+ } catch (error) {
10
+ console.error(`Failed to run command: ${command}`, error)
11
+ return false
12
+ }
13
+ return true
14
+ }
15
+
16
+ const repoName = process.argv[2]
17
+ const gitCheckout = `git clone --depth 1 https://github.com/DarkidOP/Bunext.git ${repoName}`
18
+ const installDeps = `cd ${repoName} && bun install`
19
+ const removeGit = `rm -rf .git`
20
+ const initGit = `git init && git add . && git commit -m "Initial commit"`
21
+
22
+ const rl = readline.createInterface({
23
+ input: process.stdin,
24
+ output: process.stdout,
25
+ })
26
+
27
+ console.log(`Creating project template in ./${repoName}`)
28
+ const checkedOut = runCommand(gitCheckout)
29
+ if (!checkedOut) {
30
+ console.error(
31
+ 'Failed to clone template repository "https://github.com/DarkidOP/Bunext.git"'
32
+ )
33
+ process.exit(1)
34
+ }
35
+
36
+ console.log('Removing Git history...')
37
+ const removedGit = runCommand(removeGit)
38
+ if (!removedGit) {
39
+ console.error('Failed to remove Git history')
40
+ process.exit(1)
41
+ }
42
+
43
+ console.log('Installing dependencies...')
44
+ const installedDeps = runCommand(installDeps)
45
+ if (!installedDeps) {
46
+ console.error('Failed to install dependencies')
47
+ process.exit(1)
48
+ }
49
+
50
+ // Add Git initialization prompt
51
+ rl.question(
52
+ 'Would you like to initialize a new git repository? (y/n) ',
53
+ (answer) => {
54
+ if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
55
+ console.log('Initializing Git repository...')
56
+ const initializedGit = runCommand(initGit)
57
+ if (!initializedGit) {
58
+ console.error('Failed to initialize Git repository')
59
+ process.exit(1)
60
+ }
61
+ console.log('Git repository initialized successfully!')
62
+ }
63
+
64
+ console.log('\nHappy coding! 🎉')
65
+ rl.close()
66
+ }
67
+ )
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "default",
4
+ "rsc": true,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "tailwind.config.ts",
8
+ "css": "src/styles/globals.css",
9
+ "baseColor": "neutral",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "aliases": {
14
+ "components": "@/components",
15
+ "utils": "@/lib/utils",
16
+ "ui": "@/components/ui",
17
+ "lib": "@/lib",
18
+ "hooks": "@/hooks"
19
+ },
20
+ "iconLibrary": "lucide"
21
+ }
package/next.config.ts ADDED
@@ -0,0 +1,22 @@
1
+ import type { NextConfig } from 'next'
2
+
3
+ const nextConfig: NextConfig = {
4
+ /* config options here */
5
+ eslint: {
6
+ ignoreDuringBuilds: false,
7
+ },
8
+ images: {
9
+ remotePatterns: [
10
+ { protocol: 'https', hostname: 'v0.dev', pathname: '/placeholder.svg' },
11
+ ],
12
+ },
13
+ logging: {
14
+ fetches: {
15
+ fullUrl: true,
16
+ },
17
+ },
18
+ compiler:
19
+ process.env.NODE_ENV === 'production' ? { removeConsole: true } : {},
20
+ }
21
+
22
+ export default nextConfig
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@ardly/bunext",
3
+ "description": "Bunext - A Next.js 15 template with Tailwind CSS, shadcn ui and Bun with some utilities built in",
4
+ "author": "Ard Astroid <ardastroid@gmail.com>",
5
+ "version": "1.0.0",
6
+ "bin": "./bin/cli.js",
7
+ "scripts": {
8
+ "dev": "next dev --turbopack",
9
+ "build": "next build",
10
+ "start": "next start",
11
+ "preview": "next build && next start",
12
+ "lint": "next lint",
13
+ "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,css,scss,md}\""
14
+ },
15
+ "dependencies": {
16
+ "@radix-ui/react-avatar": "^1.1.2",
17
+ "@radix-ui/react-dialog": "^1.1.4",
18
+ "@radix-ui/react-dropdown-menu": "^2.1.4",
19
+ "@radix-ui/react-label": "^2.1.1",
20
+ "@radix-ui/react-popover": "^1.1.4",
21
+ "@radix-ui/react-radio-group": "^1.2.2",
22
+ "@radix-ui/react-separator": "^1.1.1",
23
+ "@radix-ui/react-slot": "^1.1.1",
24
+ "@radix-ui/react-toast": "^1.2.4",
25
+ "@radix-ui/react-tooltip": "^1.1.6",
26
+ "class-variance-authority": "^0.7.1",
27
+ "clsx": "^2.1.1",
28
+ "cmdk": "1.0.0",
29
+ "lucide-react": "^0.460.0",
30
+ "next": "15.1.1",
31
+ "next-themes": "^0.4.4",
32
+ "nuqs": "^2.2.3",
33
+ "react": "19.0.0",
34
+ "react-dom": "19.0.0",
35
+ "tailwind-merge": "^2.5.5",
36
+ "tailwindcss-animate": "^1.0.7",
37
+ "vaul": "^1.1.2",
38
+ "zod": "^3.24.1"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^20.17.10",
42
+ "@types/react": "19.0.1",
43
+ "@types/react-dom": "19.0.2",
44
+ "eslint": "^8.57.1",
45
+ "eslint-config-next": "15.1.1",
46
+ "eslint-config-prettier": "^9.1.0",
47
+ "fluid-tailwind": "^1.0.4",
48
+ "postcss": "^8.4.49",
49
+ "prettier": "^3.4.2",
50
+ "prettier-plugin-tailwindcss": "^0.6.9",
51
+ "tailwindcss": "^3.4.17",
52
+ "tailwindcss-motion": "^1.0.0",
53
+ "typescript": "^5.7.2"
54
+ },
55
+ "overrides": {
56
+ "@types/react": "19.0.1",
57
+ "@types/react-dom": "19.0.2"
58
+ }
59
+ }
@@ -0,0 +1,8 @@
1
+ /** @type {import('postcss-load-config').Config} */
2
+ const config = {
3
+ plugins: {
4
+ tailwindcss: {},
5
+ },
6
+ };
7
+
8
+ export default config;
@@ -0,0 +1,7 @@
1
+ module.exports = {
2
+ plugins: ['prettier-plugin-tailwindcss'],
3
+ semi: false,
4
+ singleQuote: true,
5
+ tabWidth: 2,
6
+ trailingComma: 'es5',
7
+ }
@@ -0,0 +1 @@
1
+ <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 80 80"><defs><style>.cls-1{fill:url(#linear-gradient);}.cls-2,.cls-3,.cls-4{fill:#070707;}.cls-3{opacity:0.14;}.cls-4{opacity:0.3;}.cls-5{fill:#08090a;}.cls-6{fill:#f18a5c;}</style><linearGradient id="linear-gradient" x1="41.94" y1="17.58" x2="38.61" y2="71.97" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#f9f1e1"/><stop offset="1" stop-color="#faf8fa"/></linearGradient></defs><path class="cls-1" d="M74.11,34.38C70.67,27,64.91,23,59,19.35h0a89.47,89.47,0,0,1-11.58-9c-.21-.2-.48-.33-.71-.51a.69.69,0,0,0-1,0C44,11.28,42.48,10.91,41,9.25c-.15-.17-.37-.26-.51-.44-.82-1-1.47-.87-2.31.12-1.82,2.13-2,2.16-4.16.68-1.09-.74-1.81-.53-2.63.47a64.85,64.85,0,0,1-10.08,9.63c-2.71,2.15-5.4,4.34-7.94,6.78A38.59,38.59,0,0,0,4.66,38.16a25,25,0,0,0-1.4,17.2h0c1.37,5.8,4.16,10,9,12A70.48,70.48,0,0,0,31.6,71.92a94.32,94.32,0,0,0,30.29-2.31,37.85,37.85,0,0,0,9.37-3.37A9.56,9.56,0,0,0,76.12,60,35.27,35.27,0,0,0,74.11,34.38Z"/><path class="cls-2" d="M39.34,74.87a81.43,81.43,0,0,1-26.26-4.5C5.59,67.83,1.76,61.53.54,52.52S2.14,36.33,7.05,30C11,25,15.72,21.14,20.46,17.36A69,69,0,0,0,30.19,8c1.67-2,2.49-2.13,4.54-.77a1.49,1.49,0,0,0,2.32-.37c2-2.28,2.27-2.25,4.44-.38a3,3,0,0,1,.42.37C43,8.1,44,8.49,45.27,7.22a1.3,1.3,0,0,1,1.5-.12,8.46,8.46,0,0,1,2.62,1.78c3.76,3.69,8,6.39,12.21,9.19,3.88,2.59,7.68,5.32,10.76,9.27,4.47,5.72,6.78,12.62,7.3,20.46a36.52,36.52,0,0,1-2,14.27,10.57,10.57,0,0,1-4.43,5.81c-3.84,2.43-8,3.58-12.19,4.53A105.83,105.83,0,0,1,39.34,74.87ZM59,19.35a89.47,89.47,0,0,1-11.58-9c-.21-.2-.48-.33-.71-.51a.69.69,0,0,0-1,0C44,11.28,42.48,10.91,41,9.25c-.15-.17-.37-.26-.51-.44-.82-1-1.47-.87-2.31.12-1.82,2.13-2,2.16-4.16.68-1.09-.74-1.81-.53-2.63.47a64.85,64.85,0,0,1-10.08,9.63c-2.71,2.15-5.4,4.34-7.94,6.78A38.59,38.59,0,0,0,4.66,38.16a25,25,0,0,0-1.4,17.2c1.37,5.8,4.15,10,9,12A70.48,70.48,0,0,0,31.6,71.92a94.32,94.32,0,0,0,30.29-2.31,37.85,37.85,0,0,0,9.37-3.37A9.56,9.56,0,0,0,76.12,60a35.27,35.27,0,0,0-2-25.64C70.67,27,64.91,23,59,19.35Z"/><path class="cls-3" d="M3.26,55.36c1.18.51,1.76,1.86,2.57,2.84A25.48,25.48,0,0,0,20.9,67.13a66.14,66.14,0,0,0,16.18.94,77.8,77.8,0,0,0,11.21-1.52,82.55,82.55,0,0,0,14.39-4.33,26.23,26.23,0,0,0,3.77-1.84c3-1.74,4.49-4.73,5.26-8.5a45.47,45.47,0,0,0,.77-8.55,22.15,22.15,0,0,0-4.21-13.81c-2.58-3.49-5.66-6.21-8.64-9.1-.3-.3-.64-.52-.64-1.07C64.91,23,70.67,27,74.11,34.38a35.27,35.27,0,0,1,2,25.64,9.56,9.56,0,0,1-4.86,6.22,37.85,37.85,0,0,1-9.37,3.37A94.32,94.32,0,0,1,31.6,71.92a70.48,70.48,0,0,1-19.38-4.61C7.41,65.38,4.63,61.16,3.26,55.36Z"/><path class="cls-4" d="M37.53,13.92a14.62,14.62,0,0,1,1.83,8.89,5.31,5.31,0,0,1-.69,2.11,14,14,0,0,1-8.9,6.35,5.76,5.76,0,0,1,.91-1.86,36.31,36.31,0,0,1,3.86-5.16,8.82,8.82,0,0,0,2.11-5.09A39.25,39.25,0,0,1,37.53,13.92Z"/><path class="cls-4" d="M41.16,33.28a27.11,27.11,0,0,1,.35-7.78,27.36,27.36,0,0,0-.14-9.34c-.1-.66-.13-1.34-.2-2.05,2.29,1.21,4.32,5.16,4.19,8.39A18.11,18.11,0,0,1,41.16,33.28Z"/><path class="cls-4" d="M51,30.28a18.68,18.68,0,0,1-1.75-6A19.33,19.33,0,0,0,47,17.37c-.46-.84-.81-1.77-1.22-2.68,1.63-.33,4.38,1.37,5.51,3.64C53.24,22.24,52.4,26.14,51,30.28Z"/><path class="cls-4" d="M33.66,13.9C34.9,18.62,32.88,23,29.21,24a8.64,8.64,0,0,1-5.92-.35A8.41,8.41,0,0,1,26.54,21a11.64,11.64,0,0,0,6-5.57A11.68,11.68,0,0,1,33.66,13.9Z"/><path class="cls-5" d="M50.14,50.89a4.48,4.48,0,0,1-4.25-4.74,4.34,4.34,0,1,1,8.64.18A4.44,4.44,0,0,1,50.14,50.89Z"/><path class="cls-5" d="M35.11,58C32.28,57.75,30,56,27.9,53.82a1.15,1.15,0,0,1-.07-1.49.76.76,0,0,1,1.22-.2c.57.45,1.09,1,1.66,1.45,3.64,2.94,6.58,2.74,9.94-.66a2.2,2.2,0,0,0,.2-.23c.49-.64,1-1,1.61-.22a1.36,1.36,0,0,1-.33,1.86A11.49,11.49,0,0,1,35.11,58Z"/><path class="cls-5" d="M21.93,42a4.56,4.56,0,0,1,4.12,4.91,4.51,4.51,0,0,1-4.5,4.46,4.61,4.61,0,0,1-4.14-4.76A4.52,4.52,0,0,1,21.93,42Z"/><path class="cls-6" d="M13.72,53.48a2.42,2.42,0,0,1-2.65-2.2,2.47,2.47,0,0,1,2.63-2.05c1.45,0,2.74,1,2.74,2.12S15.22,53.48,13.72,53.48Z"/><path class="cls-6" d="M57.16,53.57c-1.51,0-2.78-1-2.73-2.11s1.27-2,2.7-2,2.73.84,2.74,2S58.73,53.58,57.16,53.57Z"/></svg>
Binary file
@@ -0,0 +1 @@
1
+ <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 192 192"><defs><style>.cls-1{fill:url(#linear-gradient);}.cls-2,.cls-3{fill:#070707;}.cls-2{opacity:0.14;}.cls-3{opacity:0.3;}.cls-4{fill:#08090a;}.cls-5{fill:#f18a5c;}</style><linearGradient id="linear-gradient" x1="100.54" y1="42.93" x2="92.74" y2="170.51" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#f9f1e1"/><stop offset="1" stop-color="#faf8fa"/></linearGradient></defs><path class="cls-1" d="M176,82.34c-8.07-17.28-21.58-26.61-35.47-35.26h0c-9.37-6.43-18.67-13-27.16-21.12a15.65,15.65,0,0,0-1.68-1.22,1.63,1.63,0,0,0-2.27,0c-4,3.39-7.61,2.52-11-1.37-.35-.4-.85-.62-1.19-1-1.91-2.37-3.44-2-5.43.29-4.25,5-4.61,5.07-9.75,1.59C79.5,22.5,77.81,23,75.9,25.33,68.78,34,60.68,41.23,52.25,47.92,45.9,53,39.58,58.09,33.63,63.83,25.57,71.59,18.2,80.12,13.1,91.19c-5.93,12.86-6.9,26.33-3.28,40.36h0c3.21,13.59,9.75,23.5,21,28A164.25,164.25,0,0,0,76.3,170.37c23.87,2.35,47.55,0,71-5.4a89.21,89.21,0,0,0,22-7.9c5.53-2.91,9.33-7.5,11.41-14.6C186.88,121.45,184.92,101.44,176,82.34Z"/><path class="cls-2" d="M9.82,131.55c2.76,1.18,4.13,4.36,6,6.65,9.89,11.9,21.81,18.18,35.34,21,12.61,2.59,25.27,2.71,37.94,2.19,8.81-.36,17.58-1.9,26.31-3.56a194.37,194.37,0,0,0,33.75-10.16,59.46,59.46,0,0,0,8.83-4.31c6.93-4.08,10.54-11.1,12.34-19.94a106.48,106.48,0,0,0,1.82-20.06c.14-12.21-3-23.09-9.89-32.39-6.05-8.18-13.26-14.58-20.25-21.35-.72-.69-1.52-1.2-1.51-2.5,13.89,8.65,27.4,18,35.47,35.26,8.91,19.1,10.87,39.11,4.72,60.13-2.08,7.1-5.88,11.69-11.41,14.6a89.21,89.21,0,0,1-22,7.9c-23.49,5.37-47.17,7.75-71,5.41a164.62,164.62,0,0,1-45.48-10.82C19.57,155.05,13,145.14,9.82,131.55Z"/><path class="cls-3" d="M90.21,34.35c3.55,6.71,5.21,13.39,4.29,20.85a12.59,12.59,0,0,1-1.63,4.95C87.72,68.6,80.53,72.72,72,75a13.88,13.88,0,0,1,2.13-4.35,85.47,85.47,0,0,1,9.06-12.11,20.79,20.79,0,0,0,5-11.94A86.45,86.45,0,0,1,90.21,34.35Z"/><path class="cls-3" d="M98.71,79.76a63.7,63.7,0,0,1,.84-18.26c1.4-7.41.77-14.62-.34-21.9-.23-1.55-.31-3.14-.47-4.82,5.38,2.85,10.15,12.1,9.82,19.69C108.15,64.22,104.6,72.31,98.71,79.76Z"/><path class="cls-3" d="M121.7,72.71a43.74,43.74,0,0,1-4.09-14,45.43,45.43,0,0,0-5.21-16.24c-1.08-2-1.9-4.14-2.86-6.29,3.81-.77,10.26,3.22,12.92,8.54C127.05,53.86,125.09,63,121.7,72.71Z"/><path class="cls-3" d="M81.13,34.29C84,45.36,79.3,55.69,70.69,58c-5,1.33-10,1.07-13.89-.83A19.68,19.68,0,0,1,64.43,51a27.38,27.38,0,0,0,14-13.09A30.63,30.63,0,0,1,81.13,34.29Z"/><path class="cls-4" d="M119.78,121.06c-5.62-.07-10.11-5.07-10-11.12S114.6,99,120.13,99.15s10.06,5.17,10,11.21S125.47,121.13,119.78,121.06Z"/><path class="cls-4" d="M84.54,137.83c-6.65-.67-12-4.86-16.93-9.89a2.68,2.68,0,0,1-.15-3.5,1.79,1.79,0,0,1,2.86-.48c1.33,1.06,2.56,2.33,3.88,3.4,8.54,6.91,15.44,6.44,23.32-1.54a5.82,5.82,0,0,0,.46-.53c1.15-1.5,2.45-2.4,3.79-.52,1.2,1.69.35,3.18-.77,4.37A27,27,0,0,1,84.54,137.83Z"/><path class="cls-4" d="M53.61,100.24c5.61.29,10,5.46,9.68,11.51s-5.12,10.79-10.57,10.45-9.8-5.31-9.7-11.15C43.12,104.87,48,100,53.61,100.24Z"/><path class="cls-5" d="M34.35,127.13c-3.5,0-6.33-2.34-6.22-5.15.12-2.65,2.86-4.78,6.19-4.82s6.41,2.29,6.42,5S37.87,127.12,34.35,127.13Z"/><path class="cls-5" d="M136.24,127.36c-3.54,0-6.51-2.33-6.39-5s3-4.63,6.32-4.69c3.67-.06,6.41,2,6.43,4.8S139.93,127.37,136.24,127.36Z"/></svg>
Binary file
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "Bunext",
3
+ "short_name": "Bunext",
4
+ "icons": [
5
+ {
6
+ "src": "/android-chrome-192x192.png",
7
+ "sizes": "192x192",
8
+ "type": "image/png"
9
+ },
10
+ {
11
+ "src": "/android-chrome-512x512.png",
12
+ "sizes": "512x512",
13
+ "type": "image/png"
14
+ }
15
+ ],
16
+ "theme_color": "#fbf0df",
17
+ "background_color": "#2d2b2a",
18
+ "display": "standalone"
19
+ }
@@ -0,0 +1,30 @@
1
+ 'use server';
2
+ import { promises as fs } from 'node:fs';
3
+ import path from 'node:path';
4
+ import sharp from 'sharp';
5
+ function bufferToBase64(buffer: Buffer): string {
6
+ return `data:image/png;base64,${buffer.toString('base64')}`;
7
+ }
8
+ async function getFileBufferLocal(src: string) {
9
+ const realsrc = path.join(process.cwd(), 'public', src);
10
+ return fs.readFile(realsrc);
11
+ }
12
+ async function getFileBufferRemote(url: string) {
13
+ const response = await fetch(url);
14
+ return Buffer.from(await response.arrayBuffer());
15
+ }
16
+ function getFileBuffer(src: string) {
17
+ const isRemote = src.startsWith('http');
18
+ return isRemote ? getFileBufferRemote(src) : getFileBufferLocal(src);
19
+ }
20
+ export async function getPlaceholderImage(src: string) {
21
+ try {
22
+ const originalBuffer = await (src.startsWith('http')
23
+ ? getFileBuffer(src)
24
+ : getFileBufferLocal(src));
25
+ const resizedBuffer = await sharp(originalBuffer).resize(20).toBuffer();
26
+ return bufferToBase64(resizedBuffer);
27
+ } catch {
28
+ return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mOsa2yqBwAFCAICLICSyQAAAABJRU5ErkJggg==';
29
+ }
30
+ }
@@ -0,0 +1,39 @@
1
+ 'use server'
2
+ // https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations
3
+ import { revalidatePath } from 'next/cache'
4
+ import { redirect } from 'next/navigation'
5
+
6
+ export async function createSampleItem(formData: FormData) {
7
+ try {
8
+ const rawFormData = {
9
+ title: formData.get('title'),
10
+ description: formData.get('description'),
11
+ }
12
+
13
+ // Sample validation
14
+ if (!rawFormData.title || !rawFormData.description) {
15
+ throw new Error('Title and description are required')
16
+ }
17
+
18
+ // Here you would typically:
19
+ // 1. Validate the data
20
+ // 2. Insert into database
21
+ // 3. Handle any errors
22
+
23
+ // Sample success path
24
+ revalidatePath('/items') // Revalidate the items list
25
+ redirect('/items') // Redirect to items page
26
+ } catch (error) {
27
+ // Handle errors appropriately
28
+ if (error instanceof Error) {
29
+ return {
30
+ success: false,
31
+ error: error.message,
32
+ }
33
+ }
34
+ return {
35
+ success: false,
36
+ error: 'Something went wrong',
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,31 @@
1
+ 'use client'
2
+ import { useIntersection } from '@/hooks/use-intersection'
3
+ import { cn } from '@/lib/utils'
4
+
5
+ type TTestCard = {
6
+ className?: string
7
+ index: number
8
+ }
9
+
10
+ export function TestCard({ className, index }: TTestCard) {
11
+ const { ref, isInView } = useIntersection({ threshold: 0.2, once: true })
12
+ return (
13
+ <div
14
+ ref={ref}
15
+ className={cn(
16
+ 'w-full max-w-md rounded-lg bg-muted/45 p-6 shadow-lg transition-opacity duration-500',
17
+ isInView
18
+ ? 'motion-scale-in-[0.41] motion-translate-x-in-[2%] motion-translate-y-in-[111%] motion-blur-in-[60px] motion-delay-75'
19
+ : 'opacity-0',
20
+ className
21
+ )}
22
+ >
23
+ <h2 className="mb-4 text-2xl font-semibold">Card {index + 1}</h2>
24
+ <p className="text-muted-foreground">
25
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod
26
+ tempfwfor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
27
+ veniam.
28
+ </p>
29
+ </div>
30
+ )
31
+ }
@@ -0,0 +1,17 @@
1
+ import { TestCard } from './TestCard'
2
+
3
+ export default function Page() {
4
+ // Simulate a production error
5
+
6
+ return (
7
+ <main className="container mx-auto">
8
+ <div className="flex flex-col items-center gap-8 py-12">
9
+ <h1 className="text-4xl font-bold">Scroll Intersection Example</h1>
10
+
11
+ {Array.from({ length: 20 }).map((_, i) => (
12
+ <TestCard key={i} index={i} />
13
+ ))}
14
+ </div>
15
+ </main>
16
+ )
17
+ }