arcanajs 0.2.2 → 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.
- package/.env.example +0 -0
- package/.prettierrc +4 -0
- package/README.md +85 -254
- package/arcanajs +0 -0
- package/arcanajs-cli.json +8 -0
- package/arcanajs.config.mjs +0 -0
- package/eslint.config.mjs +35 -0
- package/jest.config.mjs +8 -0
- package/package.json +5 -55
- package/src/app/Http/Controllers/Controller.ts +0 -0
- package/src/app/Http/Controllers/UserController.ts +0 -0
- package/src/app/Http/Middleware/AuthMiddleware.ts +0 -0
- package/src/app/Http/Middleware/ValidationMiddleware.ts +0 -0
- package/src/app/Models/User.ts +0 -0
- package/src/app/Providers/AppServiceProvider.ts +0 -0
- package/src/app/Providers/DatabaseProvider.ts +0 -0
- package/src/app/Repositories/UserRepository.ts +0 -0
- package/src/app/Services/UserService.ts +0 -0
- package/src/bootstrap/app.ts +0 -0
- package/src/config/app.ts +0 -0
- package/src/config/cache.ts +0 -0
- package/src/config/database.ts +0 -0
- package/src/config/mail.ts +0 -0
- package/src/config/server.ts +0 -0
- package/src/core/App.ts +0 -0
- package/src/core/Cache.ts +0 -0
- package/src/core/Container.ts +0 -0
- package/src/core/Controller.ts +0 -0
- package/src/core/Kernel.ts +0 -0
- package/src/core/Logger.ts +0 -0
- package/src/core/Repository.ts +0 -0
- package/src/core/Response.ts +0 -0
- package/src/core/Service.ts +0 -0
- package/src/core/Validator.ts +0 -0
- package/src/resources/css/style.css +0 -0
- package/src/resources/views/back-office/components/common/Footer/main.tsx +161 -0
- package/src/resources/views/back-office/components/common/Header/main.tsx +151 -0
- package/src/resources/views/back-office/components/common/Layout/main.tsx +15 -0
- package/src/resources/views/back-office/components/ui/CodeBlock/index.tsx +350 -0
- package/src/resources/views/back-office/pages/About/index.tsx +0 -0
- package/src/resources/views/back-office/pages/Contact/index.tsx +0 -0
- package/src/resources/views/back-office/pages/Home/index.tsx +0 -0
- package/src/resources/views/back-office/pages/NotFound/index.tsx +0 -0
- package/src/resources/views/back-office/types/global.d.ts +0 -0
- package/src/resources/views/front-office/components/common/Footer/main.tsx +161 -0
- package/src/resources/views/front-office/components/common/Header/main.tsx +151 -0
- package/src/resources/views/front-office/components/common/Layout/main.tsx +15 -0
- package/src/resources/views/front-office/components/ui/CodeBlock/index.tsx +350 -0
- package/src/resources/views/front-office/pages/About/index.tsx +0 -0
- package/src/resources/views/front-office/pages/Contact/index.tsx +0 -0
- package/src/resources/views/front-office/pages/Home/index.tsx +0 -0
- package/src/resources/views/front-office/pages/NotFound/index.tsx +0 -0
- package/src/resources/views/front-office/types/global.d.ts +0 -0
- package/src/resources/views/main.tsx +0 -0
- package/src/routes/api.routes.ts +0 -0
- package/src/routes/web.routes.ts +0 -0
- package/tests/integration/jest.ts +0 -0
- package/tests/unit/jest.ts +0 -0
- package/tsconfig.build.json +4 -0
- package/tsconfig.json +27 -0
- package/LICENSE +0 -21
- package/bin/arcana.ts +0 -19
- package/dist/bin/arcana.d.ts +0 -4
- package/dist/bin/arcana.d.ts.map +0 -1
- package/dist/bin/arcana.js +0 -17
- package/dist/bin/arcana.js.map +0 -1
- package/dist/config/default.d.ts +0 -7
- package/dist/config/default.d.ts.map +0 -1
- package/dist/config/default.js +0 -8
- package/dist/config/default.js.map +0 -1
- package/dist/src/controllers/HomeController.d.ts +0 -6
- package/dist/src/controllers/HomeController.d.ts.map +0 -1
- package/dist/src/controllers/HomeController.js +0 -9
- package/dist/src/controllers/HomeController.js.map +0 -1
- package/dist/src/middleware/auth.d.ts +0 -3
- package/dist/src/middleware/auth.d.ts.map +0 -1
- package/dist/src/middleware/auth.js +0 -9
- package/dist/src/middleware/auth.js.map +0 -1
- package/dist/src/models/User.d.ts +0 -11
- package/dist/src/models/User.d.ts.map +0 -1
- package/dist/src/models/User.js +0 -45
- package/dist/src/models/User.js.map +0 -1
- package/dist/src/repositories/UserRepository.d.ts +0 -9
- package/dist/src/repositories/UserRepository.d.ts.map +0 -1
- package/dist/src/repositories/UserRepository.js +0 -19
- package/dist/src/repositories/UserRepository.js.map +0 -1
- package/dist/src/routes/index.d.ts +0 -3
- package/dist/src/routes/index.d.ts.map +0 -1
- package/dist/src/routes/index.js +0 -14
- package/dist/src/routes/index.js.map +0 -1
- package/dist/src/server.d.ts +0 -2
- package/dist/src/server.d.ts.map +0 -1
- package/dist/src/server.js +0 -34
- package/dist/src/server.js.map +0 -1
- package/dist/src/services/UserService.d.ts +0 -8
- package/dist/src/services/UserService.d.ts.map +0 -1
- package/dist/src/services/UserService.js +0 -16
- package/dist/src/services/UserService.js.map +0 -1
- package/dist/src/views/pages/home.d.ts +0 -2
- package/dist/src/views/pages/home.d.ts.map +0 -1
- package/dist/src/views/pages/home.js +0 -13
- package/dist/src/views/pages/home.js.map +0 -1
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { Link } from "router-kit";
|
|
2
|
+
|
|
3
|
+
const MainFooter = () => {
|
|
4
|
+
return (
|
|
5
|
+
<footer className="bg-primary-500 border-t border-white/10">
|
|
6
|
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 py-8 sm:py-10 lg:py-12">
|
|
7
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 sm:gap-8">
|
|
8
|
+
{/* Company Info */}
|
|
9
|
+
<div className="col-span-1 sm:col-span-2">
|
|
10
|
+
<div className="flex items-center space-x-3 mb-3 sm:mb-4">
|
|
11
|
+
<span className="text-xl sm:text-2xl font-bold text-gradient">
|
|
12
|
+
Router-Kit
|
|
13
|
+
</span>
|
|
14
|
+
</div>
|
|
15
|
+
<p className="text-white/70 text-xs sm:text-sm mb-3 sm:mb-4 max-w-md">
|
|
16
|
+
A lightweight, minimal, and powerful client-side routing library
|
|
17
|
+
for React applications. Simple yet flexible routing without the
|
|
18
|
+
overhead.
|
|
19
|
+
</p>
|
|
20
|
+
<div className="flex space-x-3 sm:space-x-4">
|
|
21
|
+
<a
|
|
22
|
+
href="https://github.com/Mohammed-Ben-Cheikh/router-kit"
|
|
23
|
+
target="_blank"
|
|
24
|
+
rel="noopener noreferrer"
|
|
25
|
+
className="text-white/70 hover:text-accent-300 transition-colors"
|
|
26
|
+
aria-label="GitHub"
|
|
27
|
+
>
|
|
28
|
+
<svg
|
|
29
|
+
className="w-5 h-5 sm:w-6 sm:h-6"
|
|
30
|
+
fill="currentColor"
|
|
31
|
+
viewBox="0 0 24 24"
|
|
32
|
+
>
|
|
33
|
+
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
|
|
34
|
+
</svg>
|
|
35
|
+
</a>
|
|
36
|
+
<a
|
|
37
|
+
href="https://www.npmjs.com/package/router-kit"
|
|
38
|
+
target="_blank"
|
|
39
|
+
rel="noopener noreferrer"
|
|
40
|
+
className="text-white/70 hover:text-accent-300 transition-colors"
|
|
41
|
+
aria-label="NPM"
|
|
42
|
+
>
|
|
43
|
+
<svg
|
|
44
|
+
className="w-5 h-5 sm:w-6 sm:h-6"
|
|
45
|
+
fill="currentColor"
|
|
46
|
+
viewBox="0 0 24 24"
|
|
47
|
+
>
|
|
48
|
+
<path d="M0 7.334v8h6.666v1.332H12v-1.332h12v-8H0zm6.666 6.664H5.334v-4H3.999v4H1.335V8.667h5.331v5.331zm4 0v1.336H8.001V8.667h5.334v5.332h-2.669v-.001zm12.001 0h-1.33v-4h-1.336v4h-1.335v-4h-1.33v4h-2.671V8.667h8.002v5.331zM10.665 10H12v2.667h-1.335V10z" />
|
|
49
|
+
</svg>
|
|
50
|
+
</a>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
{/* Quick Links */}
|
|
55
|
+
<div>
|
|
56
|
+
<h4 className="text-white text-base sm:text-lg font-semibold mb-3 sm:mb-4">
|
|
57
|
+
Quick Links
|
|
58
|
+
</h4>
|
|
59
|
+
<ul className="space-y-2">
|
|
60
|
+
<li>
|
|
61
|
+
<Link
|
|
62
|
+
to="/"
|
|
63
|
+
className="text-sm text-white/70 hover:text-accent-300 transition-colors"
|
|
64
|
+
>
|
|
65
|
+
Home
|
|
66
|
+
</Link>
|
|
67
|
+
</li>
|
|
68
|
+
<li>
|
|
69
|
+
<Link
|
|
70
|
+
to="/docs"
|
|
71
|
+
className="text-sm text-white/70 hover:text-accent-300 transition-colors"
|
|
72
|
+
>
|
|
73
|
+
Documentation
|
|
74
|
+
</Link>
|
|
75
|
+
</li>
|
|
76
|
+
<li>
|
|
77
|
+
<Link
|
|
78
|
+
to="/about"
|
|
79
|
+
className="text-sm text-white/70 hover:text-accent-300 transition-colors"
|
|
80
|
+
>
|
|
81
|
+
About
|
|
82
|
+
</Link>
|
|
83
|
+
</li>
|
|
84
|
+
<li>
|
|
85
|
+
<Link
|
|
86
|
+
to="/contact"
|
|
87
|
+
className="text-sm text-white/70 hover:text-accent-300 transition-colors"
|
|
88
|
+
>
|
|
89
|
+
Contact
|
|
90
|
+
</Link>
|
|
91
|
+
</li>
|
|
92
|
+
</ul>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
{/* Resources */}
|
|
96
|
+
<div>
|
|
97
|
+
<h4 className="text-white text-base sm:text-lg font-semibold mb-3 sm:mb-4">
|
|
98
|
+
Resources
|
|
99
|
+
</h4>
|
|
100
|
+
<ul className="space-y-2">
|
|
101
|
+
<li>
|
|
102
|
+
<a
|
|
103
|
+
href="https://github.com/Mohammed-Ben-Cheikh/router-kit"
|
|
104
|
+
target="_blank"
|
|
105
|
+
rel="noopener noreferrer"
|
|
106
|
+
className="text-sm text-white/70 hover:text-accent-300 transition-colors"
|
|
107
|
+
>
|
|
108
|
+
GitHub
|
|
109
|
+
</a>
|
|
110
|
+
</li>
|
|
111
|
+
<li>
|
|
112
|
+
<a
|
|
113
|
+
href="https://www.npmjs.com/package/router-kit"
|
|
114
|
+
target="_blank"
|
|
115
|
+
rel="noopener noreferrer"
|
|
116
|
+
className="text-sm text-white/70 hover:text-accent-300 transition-colors"
|
|
117
|
+
>
|
|
118
|
+
NPM Package
|
|
119
|
+
</a>
|
|
120
|
+
</li>
|
|
121
|
+
<li>
|
|
122
|
+
<a
|
|
123
|
+
href="#"
|
|
124
|
+
className="text-sm text-white/70 hover:text-accent-300 transition-colors"
|
|
125
|
+
>
|
|
126
|
+
API Reference
|
|
127
|
+
</a>
|
|
128
|
+
</li>
|
|
129
|
+
<li>
|
|
130
|
+
<a
|
|
131
|
+
href="#"
|
|
132
|
+
className="text-sm text-white/70 hover:text-accent-300 transition-colors"
|
|
133
|
+
>
|
|
134
|
+
Examples
|
|
135
|
+
</a>
|
|
136
|
+
</li>
|
|
137
|
+
</ul>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
{/* Copyright */}
|
|
142
|
+
<div className="border-t border-white/10 mt-6 sm:mt-8 pt-6 sm:pt-8 text-xs sm:text-sm text-center text-white/60">
|
|
143
|
+
<p className="px-4">
|
|
144
|
+
© {new Date().getFullYear()} Router-Kit by{" "}
|
|
145
|
+
<a
|
|
146
|
+
href="https://mohammedbencheikh.com"
|
|
147
|
+
target="_blank"
|
|
148
|
+
rel="noopener noreferrer"
|
|
149
|
+
className="text-accent-300 hover:underline"
|
|
150
|
+
>
|
|
151
|
+
Mohammed Ben Cheikh
|
|
152
|
+
</a>
|
|
153
|
+
. All rights reserved.
|
|
154
|
+
</p>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
</footer>
|
|
158
|
+
);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export default MainFooter;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { Link, NavLink } from "router-kit";
|
|
3
|
+
|
|
4
|
+
const MainHeader = () => {
|
|
5
|
+
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
|
6
|
+
|
|
7
|
+
const linkClasses =
|
|
8
|
+
"px-4 py-2 text-white/80 hover:text-accent-300 transition-all duration-300 rounded-lg hover:bg-white/10 font-medium";
|
|
9
|
+
const activeLinkClasses = "!text-accent-300 bg-white/10";
|
|
10
|
+
|
|
11
|
+
const mobileLinkClasses =
|
|
12
|
+
"block px-4 py-3 text-white/80 hover:text-accent-300 transition-all duration-300 rounded-lg hover:bg-white/10 font-medium";
|
|
13
|
+
|
|
14
|
+
const toggleMenu = () => {
|
|
15
|
+
setIsMenuOpen(!isMenuOpen);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const closeMenu = () => {
|
|
19
|
+
setIsMenuOpen(false);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<nav className="fixed top-0 left-0 right-0 bg-primary-500/95 backdrop-blur-md border-b border-white/10 z-50">
|
|
24
|
+
<div className="max-w-7xl mx-auto px-4">
|
|
25
|
+
<div className="flex items-center justify-between h-16">
|
|
26
|
+
<Link to="/" className="flex items-center space-x-3 group">
|
|
27
|
+
<span className="text-2xl font-bold text-gradient">Router-Kit</span>
|
|
28
|
+
</Link>
|
|
29
|
+
|
|
30
|
+
{/* Desktop Menu */}
|
|
31
|
+
<div className="hidden md:flex items-center space-x-2">
|
|
32
|
+
<NavLink
|
|
33
|
+
className={linkClasses}
|
|
34
|
+
activeClassName={activeLinkClasses}
|
|
35
|
+
to="/"
|
|
36
|
+
>
|
|
37
|
+
Home
|
|
38
|
+
</NavLink>
|
|
39
|
+
<NavLink
|
|
40
|
+
className={linkClasses}
|
|
41
|
+
activeClassName={activeLinkClasses}
|
|
42
|
+
to="/docs"
|
|
43
|
+
>
|
|
44
|
+
Documentation
|
|
45
|
+
</NavLink>
|
|
46
|
+
<NavLink
|
|
47
|
+
className={linkClasses}
|
|
48
|
+
activeClassName={activeLinkClasses}
|
|
49
|
+
to="/about"
|
|
50
|
+
>
|
|
51
|
+
About
|
|
52
|
+
</NavLink>
|
|
53
|
+
<NavLink
|
|
54
|
+
className={linkClasses}
|
|
55
|
+
activeClassName={activeLinkClasses}
|
|
56
|
+
to="/contact"
|
|
57
|
+
>
|
|
58
|
+
Contact
|
|
59
|
+
</NavLink>
|
|
60
|
+
<a
|
|
61
|
+
href="https://github.com/Mohammed-Ben-Cheikh/router-kit"
|
|
62
|
+
target="_blank"
|
|
63
|
+
rel="noopener noreferrer"
|
|
64
|
+
className="ml-4 px-4 py-2 bg-gradient-blue text-primary-500 rounded-lg font-semibold hover:shadow-lg hover:shadow-accent-300/50 transition-all duration-300"
|
|
65
|
+
>
|
|
66
|
+
GitHub
|
|
67
|
+
</a>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
{/* Mobile Menu Button */}
|
|
71
|
+
<button
|
|
72
|
+
onClick={toggleMenu}
|
|
73
|
+
className="md:hidden p-2 text-white/80 hover:text-accent-300 transition-colors"
|
|
74
|
+
aria-label="Toggle menu"
|
|
75
|
+
>
|
|
76
|
+
<svg
|
|
77
|
+
className="w-6 h-6"
|
|
78
|
+
fill="none"
|
|
79
|
+
stroke="currentColor"
|
|
80
|
+
viewBox="0 0 24 24"
|
|
81
|
+
>
|
|
82
|
+
{isMenuOpen ? (
|
|
83
|
+
<path
|
|
84
|
+
strokeLinecap="round"
|
|
85
|
+
strokeLinejoin="round"
|
|
86
|
+
strokeWidth={2}
|
|
87
|
+
d="M6 18L18 6M6 6l12 12"
|
|
88
|
+
/>
|
|
89
|
+
) : (
|
|
90
|
+
<path
|
|
91
|
+
strokeLinecap="round"
|
|
92
|
+
strokeLinejoin="round"
|
|
93
|
+
strokeWidth={2}
|
|
94
|
+
d="M4 6h16M4 12h16M4 18h16"
|
|
95
|
+
/>
|
|
96
|
+
)}
|
|
97
|
+
</svg>
|
|
98
|
+
</button>
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
{/* Mobile Menu */}
|
|
102
|
+
<div
|
|
103
|
+
className={`md:hidden overflow-hidden transition-all duration-300 ease-in-out ${
|
|
104
|
+
isMenuOpen ? "max-h-96 opacity-100" : "max-h-0 opacity-0"
|
|
105
|
+
}`}
|
|
106
|
+
>
|
|
107
|
+
<div className="py-4 space-y-2" onClick={closeMenu}>
|
|
108
|
+
<NavLink
|
|
109
|
+
className={mobileLinkClasses}
|
|
110
|
+
activeClassName={activeLinkClasses}
|
|
111
|
+
to="/"
|
|
112
|
+
>
|
|
113
|
+
Home
|
|
114
|
+
</NavLink>
|
|
115
|
+
<NavLink
|
|
116
|
+
className={mobileLinkClasses}
|
|
117
|
+
activeClassName={activeLinkClasses}
|
|
118
|
+
to="/docs"
|
|
119
|
+
>
|
|
120
|
+
Documentation
|
|
121
|
+
</NavLink>
|
|
122
|
+
<NavLink
|
|
123
|
+
className={mobileLinkClasses}
|
|
124
|
+
activeClassName={activeLinkClasses}
|
|
125
|
+
to="/about"
|
|
126
|
+
>
|
|
127
|
+
About
|
|
128
|
+
</NavLink>
|
|
129
|
+
<NavLink
|
|
130
|
+
className={mobileLinkClasses}
|
|
131
|
+
activeClassName={activeLinkClasses}
|
|
132
|
+
to="/contact"
|
|
133
|
+
>
|
|
134
|
+
Contact
|
|
135
|
+
</NavLink>
|
|
136
|
+
<a
|
|
137
|
+
href="https://github.com/Mohammed-Ben-Cheikh/router-kit"
|
|
138
|
+
target="_blank"
|
|
139
|
+
rel="noopener noreferrer"
|
|
140
|
+
className="block mx-4 mt-4 px-4 py-2 bg-gradient-blue text-primary-500 rounded-lg font-semibold text-center hover:shadow-lg hover:shadow-accent-300/50 transition-all duration-300"
|
|
141
|
+
>
|
|
142
|
+
GitHub
|
|
143
|
+
</a>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
</nav>
|
|
148
|
+
);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
export default MainHeader;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import MainFooter from "../Footer/main";
|
|
3
|
+
import MainHeader from "../Header/main";
|
|
4
|
+
|
|
5
|
+
const MainLayout = ({ children }: { children: ReactNode }) => {
|
|
6
|
+
return (
|
|
7
|
+
<div className="min-h-screen bg-primary-500 flex flex-col">
|
|
8
|
+
<MainHeader />
|
|
9
|
+
<main className="pt-16 flex-grow">{children}</main>
|
|
10
|
+
<MainFooter />
|
|
11
|
+
</div>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default MainLayout;
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
|
3
|
+
import { vscDarkPlus } from "react-syntax-highlighter/dist/esm/styles/prism";
|
|
4
|
+
import dracula from "react-syntax-highlighter/dist/esm/styles/prism/dracula";
|
|
5
|
+
import okaidia from "react-syntax-highlighter/dist/esm/styles/prism/okaidia";
|
|
6
|
+
|
|
7
|
+
interface CodeBlockProps {
|
|
8
|
+
code: string;
|
|
9
|
+
language?: string;
|
|
10
|
+
showLineNumbers?: boolean;
|
|
11
|
+
showCopyButton?: boolean;
|
|
12
|
+
showHeader?: boolean;
|
|
13
|
+
maxHeight?: string;
|
|
14
|
+
expandable?: boolean;
|
|
15
|
+
filename?: string;
|
|
16
|
+
theme?: "vscDarkPlus" | "dracula" | "okaidia";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const CodeBlock = ({
|
|
20
|
+
code,
|
|
21
|
+
language = "tsx",
|
|
22
|
+
showLineNumbers = true,
|
|
23
|
+
showCopyButton = true,
|
|
24
|
+
showHeader = true,
|
|
25
|
+
maxHeight = "none",
|
|
26
|
+
expandable = false,
|
|
27
|
+
filename,
|
|
28
|
+
theme = "vscDarkPlus",
|
|
29
|
+
}: CodeBlockProps) => {
|
|
30
|
+
const [isCopied, setIsCopied] = useState(false);
|
|
31
|
+
const [isExpanded, setIsExpanded] = useState(!expandable);
|
|
32
|
+
const [showScrollIndicator, setShowScrollIndicator] = useState(false);
|
|
33
|
+
const codeRef = useRef<HTMLDivElement>(null);
|
|
34
|
+
|
|
35
|
+
const themes = {
|
|
36
|
+
vscDarkPlus,
|
|
37
|
+
dracula,
|
|
38
|
+
okaidia,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const copyToClipboard = async () => {
|
|
42
|
+
try {
|
|
43
|
+
await navigator.clipboard.writeText(code);
|
|
44
|
+
setIsCopied(true);
|
|
45
|
+
setTimeout(() => setIsCopied(false), 2000);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
console.error("Failed to copy code: ", err);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const toggleExpand = () => {
|
|
52
|
+
setIsExpanded(!isExpanded);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const checkScrollable = () => {
|
|
56
|
+
if (codeRef.current) {
|
|
57
|
+
const { scrollHeight, clientHeight } = codeRef.current;
|
|
58
|
+
setShowScrollIndicator(scrollHeight > clientHeight);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
checkScrollable();
|
|
64
|
+
window.addEventListener("resize", checkScrollable);
|
|
65
|
+
return () => window.removeEventListener("resize", checkScrollable);
|
|
66
|
+
}, [code, maxHeight]);
|
|
67
|
+
|
|
68
|
+
const simpleFormat = (src: string) => {
|
|
69
|
+
const indentUnit = " ";
|
|
70
|
+
const lines = src.replace(/\r/g, "").split("\n");
|
|
71
|
+
let level = 0;
|
|
72
|
+
const out: string[] = [];
|
|
73
|
+
|
|
74
|
+
for (let rawLine of lines) {
|
|
75
|
+
if (/^\s*$/.test(rawLine)) {
|
|
76
|
+
if (out.length === 0 || /^\s*$/.test(out[out.length - 1])) continue;
|
|
77
|
+
out.push("");
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
rawLine = rawLine.replace(/\t/g, indentUnit).trim();
|
|
82
|
+
if (/^[)}\]]/.test(rawLine)) {
|
|
83
|
+
level = Math.max(0, level - 1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
out.push(indentUnit.repeat(level) + rawLine);
|
|
87
|
+
|
|
88
|
+
if (/[{[(]$/.test(rawLine) || /:\s*$/.test(rawLine)) {
|
|
89
|
+
level++;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
while (out.length && out[0].trim() === "") out.shift();
|
|
94
|
+
while (out.length && out[out.length - 1].trim() === "") out.pop();
|
|
95
|
+
return out.join("\n");
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const formattedCode = (() => {
|
|
99
|
+
try {
|
|
100
|
+
return simpleFormat(code);
|
|
101
|
+
} catch {
|
|
102
|
+
return code;
|
|
103
|
+
}
|
|
104
|
+
})();
|
|
105
|
+
|
|
106
|
+
const getLanguageDisplayName = (lang: string) => {
|
|
107
|
+
const languageMap: { [key: string]: string } = {
|
|
108
|
+
tsx: "TypeScript",
|
|
109
|
+
jsx: "JavaScript",
|
|
110
|
+
javascript: "JavaScript",
|
|
111
|
+
typescript: "TypeScript",
|
|
112
|
+
python: "Python",
|
|
113
|
+
java: "Java",
|
|
114
|
+
cpp: "C++",
|
|
115
|
+
css: "CSS",
|
|
116
|
+
html: "HTML",
|
|
117
|
+
json: "JSON",
|
|
118
|
+
bash: "Bash",
|
|
119
|
+
shell: "Shell",
|
|
120
|
+
sql: "SQL",
|
|
121
|
+
};
|
|
122
|
+
return languageMap[lang] || lang.toUpperCase();
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<div className="bg-[#1e1e1e] rounded-lg border border-white/10 overflow-hidden shadow-xl hover:shadow-2xl transition-all duration-300">
|
|
127
|
+
{/* Header */}
|
|
128
|
+
{showHeader && (
|
|
129
|
+
<div className="flex items-center justify-between px-4 py-3 bg-gray-900/50 border-b border-white/10">
|
|
130
|
+
<div className="flex items-center gap-3">
|
|
131
|
+
{/* Language Indicator */}
|
|
132
|
+
<div className="flex items-center gap-2">
|
|
133
|
+
<div className="flex gap-1">
|
|
134
|
+
<div className="w-3 h-3 rounded-full bg-red-500"></div>
|
|
135
|
+
<div className="w-3 h-3 rounded-full bg-yellow-500"></div>
|
|
136
|
+
<div className="w-3 h-3 rounded-full bg-green-500"></div>
|
|
137
|
+
</div>
|
|
138
|
+
<span className="text-sm font-medium text-gray-300">
|
|
139
|
+
{getLanguageDisplayName(language)}
|
|
140
|
+
</span>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
{/* Filename */}
|
|
144
|
+
{filename && (
|
|
145
|
+
<div className="flex items-center gap-2 text-xs text-gray-400">
|
|
146
|
+
<svg
|
|
147
|
+
className="w-4 h-4"
|
|
148
|
+
fill="none"
|
|
149
|
+
stroke="currentColor"
|
|
150
|
+
viewBox="0 0 24 24"
|
|
151
|
+
>
|
|
152
|
+
<path
|
|
153
|
+
strokeLinecap="round"
|
|
154
|
+
strokeLinejoin="round"
|
|
155
|
+
strokeWidth={2}
|
|
156
|
+
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
|
157
|
+
/>
|
|
158
|
+
</svg>
|
|
159
|
+
{filename}
|
|
160
|
+
</div>
|
|
161
|
+
)}
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
<div className="flex items-center gap-2">
|
|
165
|
+
{/* Expand/Collapse Button */}
|
|
166
|
+
{expandable && (
|
|
167
|
+
<button
|
|
168
|
+
onClick={toggleExpand}
|
|
169
|
+
className="p-1.5 rounded-md hover:bg-white/10 transition-colors duration-200"
|
|
170
|
+
title={isExpanded ? "Collapse" : "Expand"}
|
|
171
|
+
>
|
|
172
|
+
<svg
|
|
173
|
+
className={`w-4 h-4 text-gray-400 transition-transform duration-200 ${
|
|
174
|
+
isExpanded ? "rotate-180" : ""
|
|
175
|
+
}`}
|
|
176
|
+
fill="none"
|
|
177
|
+
stroke="currentColor"
|
|
178
|
+
viewBox="0 0 24 24"
|
|
179
|
+
>
|
|
180
|
+
<path
|
|
181
|
+
strokeLinecap="round"
|
|
182
|
+
strokeLinejoin="round"
|
|
183
|
+
strokeWidth={2}
|
|
184
|
+
d="M19 9l-7 7-7-7"
|
|
185
|
+
/>
|
|
186
|
+
</svg>
|
|
187
|
+
</button>
|
|
188
|
+
)}
|
|
189
|
+
|
|
190
|
+
{/* Copy Button */}
|
|
191
|
+
{showCopyButton && (
|
|
192
|
+
<button
|
|
193
|
+
onClick={copyToClipboard}
|
|
194
|
+
className="flex items-center gap-2 px-3 py-1.5 text-xs font-medium rounded-md bg-blue-600 hover:bg-blue-700 text-white transition-all duration-200 active:scale-95"
|
|
195
|
+
>
|
|
196
|
+
{isCopied ? (
|
|
197
|
+
<>
|
|
198
|
+
<svg
|
|
199
|
+
className="w-4 h-4"
|
|
200
|
+
fill="none"
|
|
201
|
+
stroke="currentColor"
|
|
202
|
+
viewBox="0 0 24 24"
|
|
203
|
+
>
|
|
204
|
+
<path
|
|
205
|
+
strokeLinecap="round"
|
|
206
|
+
strokeLinejoin="round"
|
|
207
|
+
strokeWidth={2}
|
|
208
|
+
d="M5 13l4 4L19 7"
|
|
209
|
+
/>
|
|
210
|
+
</svg>
|
|
211
|
+
Copied!
|
|
212
|
+
</>
|
|
213
|
+
) : (
|
|
214
|
+
<>
|
|
215
|
+
<svg
|
|
216
|
+
className="w-4 h-4"
|
|
217
|
+
fill="none"
|
|
218
|
+
stroke="currentColor"
|
|
219
|
+
viewBox="0 0 24 24"
|
|
220
|
+
>
|
|
221
|
+
<path
|
|
222
|
+
strokeLinecap="round"
|
|
223
|
+
strokeLinejoin="round"
|
|
224
|
+
strokeWidth={2}
|
|
225
|
+
d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"
|
|
226
|
+
/>
|
|
227
|
+
</svg>
|
|
228
|
+
Copy
|
|
229
|
+
</>
|
|
230
|
+
)}
|
|
231
|
+
</button>
|
|
232
|
+
)}
|
|
233
|
+
</div>
|
|
234
|
+
</div>
|
|
235
|
+
)}
|
|
236
|
+
|
|
237
|
+
{/* Code Content */}
|
|
238
|
+
<div
|
|
239
|
+
ref={codeRef}
|
|
240
|
+
className={`relative transition-all duration-300 ${
|
|
241
|
+
isExpanded ? "max-h-none" : `max-h-96 overflow-hidden`
|
|
242
|
+
}`}
|
|
243
|
+
style={
|
|
244
|
+
isExpanded
|
|
245
|
+
? {}
|
|
246
|
+
: { maxHeight: maxHeight !== "none" ? maxHeight : "24rem" }
|
|
247
|
+
}
|
|
248
|
+
>
|
|
249
|
+
{/* Scroll Indicator */}
|
|
250
|
+
{showScrollIndicator && !isExpanded && (
|
|
251
|
+
<div className="absolute bottom-0 left-0 right-0 h-8 bg-gradient-to-t from-gray-900 to-transparent flex items-center justify-center">
|
|
252
|
+
<div className="flex items-center gap-1 text-xs text-gray-400 bg-gray-800 px-2 py-1 rounded-full">
|
|
253
|
+
<svg
|
|
254
|
+
className="w-3 h-3"
|
|
255
|
+
fill="none"
|
|
256
|
+
stroke="currentColor"
|
|
257
|
+
viewBox="0 0 24 24"
|
|
258
|
+
>
|
|
259
|
+
<path
|
|
260
|
+
strokeLinecap="round"
|
|
261
|
+
strokeLinejoin="round"
|
|
262
|
+
strokeWidth={2}
|
|
263
|
+
d="M19 14l-7 7m0 0l-7-7m7 7V3"
|
|
264
|
+
/>
|
|
265
|
+
</svg>
|
|
266
|
+
Scroll to see more
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
)}
|
|
270
|
+
|
|
271
|
+
<SyntaxHighlighter
|
|
272
|
+
language={language}
|
|
273
|
+
style={themes[theme]}
|
|
274
|
+
showLineNumbers={showLineNumbers}
|
|
275
|
+
customStyle={{
|
|
276
|
+
margin: 0,
|
|
277
|
+
padding: "1.5rem",
|
|
278
|
+
background: "#1e1e1e",
|
|
279
|
+
fontSize: "0.875rem",
|
|
280
|
+
lineHeight: "1.5",
|
|
281
|
+
border: "none",
|
|
282
|
+
borderRadius: 0,
|
|
283
|
+
}}
|
|
284
|
+
lineNumberStyle={{
|
|
285
|
+
minWidth: "3em",
|
|
286
|
+
paddingRight: "1em",
|
|
287
|
+
color: "#858585",
|
|
288
|
+
userSelect: "none",
|
|
289
|
+
background: "transparent",
|
|
290
|
+
}}
|
|
291
|
+
wrapLines={true}
|
|
292
|
+
wrapLongLines={true}
|
|
293
|
+
>
|
|
294
|
+
{formattedCode}
|
|
295
|
+
</SyntaxHighlighter>
|
|
296
|
+
|
|
297
|
+
{/* Expand Overlay */}
|
|
298
|
+
{expandable && !isExpanded && (
|
|
299
|
+
<div
|
|
300
|
+
className="absolute inset-0 bg-gradient-to-b from-transparent to-gray-900/80 cursor-pointer flex items-end justify-center pb-4"
|
|
301
|
+
onClick={toggleExpand}
|
|
302
|
+
>
|
|
303
|
+
<div className="flex items-center gap-2 px-4 py-2 bg-gray-800 rounded-lg text-sm text-gray-300 hover:bg-gray-700 transition-colors">
|
|
304
|
+
<svg
|
|
305
|
+
className="w-4 h-4"
|
|
306
|
+
fill="none"
|
|
307
|
+
stroke="currentColor"
|
|
308
|
+
viewBox="0 0 24 24"
|
|
309
|
+
>
|
|
310
|
+
<path
|
|
311
|
+
strokeLinecap="round"
|
|
312
|
+
strokeLinejoin="round"
|
|
313
|
+
strokeWidth={2}
|
|
314
|
+
d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5v-4m0 4h-4m4 0l-5-5"
|
|
315
|
+
/>
|
|
316
|
+
</svg>
|
|
317
|
+
Click to expand
|
|
318
|
+
</div>
|
|
319
|
+
</div>
|
|
320
|
+
)}
|
|
321
|
+
</div>
|
|
322
|
+
|
|
323
|
+
{/* Footer with character/lines count */}
|
|
324
|
+
<div className="px-4 py-2 bg-gray-900/30 border-t border-white/5 flex justify-between items-center text-xs text-gray-500">
|
|
325
|
+
<div className="flex items-center gap-4">
|
|
326
|
+
<span>{formattedCode.split("\n").length} lines</span>
|
|
327
|
+
<span>{formattedCode.length} characters</span>
|
|
328
|
+
</div>
|
|
329
|
+
<div className="flex items-center gap-1">
|
|
330
|
+
<svg
|
|
331
|
+
className="w-3 h-3"
|
|
332
|
+
fill="none"
|
|
333
|
+
stroke="currentColor"
|
|
334
|
+
viewBox="0 0 24 24"
|
|
335
|
+
>
|
|
336
|
+
<path
|
|
337
|
+
strokeLinecap="round"
|
|
338
|
+
strokeLinejoin="round"
|
|
339
|
+
strokeWidth={2}
|
|
340
|
+
d="M13 10V3L4 14h7v7l9-11h-7z"
|
|
341
|
+
/>
|
|
342
|
+
</svg>
|
|
343
|
+
routerkit.com
|
|
344
|
+
</div>
|
|
345
|
+
</div>
|
|
346
|
+
</div>
|
|
347
|
+
);
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
export default CodeBlock;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|