arcanajs 2.1.5 → 2.2.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ArcanaJS Framework
2
2
 
3
- ArcanaJS is a modern React framework for building server-side rendered (SSR) applications with ease. It combines the power of Express, React, and TypeScript to provide a seamless development experience.
3
+ ArcanaJS is a modern React framework for building server-side rendered (SSR) applications with ease. It combines the power of Express, React, TypeScript, and Tailwind CSS v4 to provide a seamless development experience.
4
4
 
5
5
  ## Features
6
6
 
@@ -8,9 +8,176 @@ ArcanaJS is a modern React framework for building server-side rendered (SSR) app
8
8
  - **TypeScript Support:** Built with TypeScript for type safety and better developer experience.
9
9
  - **File-based Routing:** Intuitive routing based on your file structure.
10
10
  - **Hot Module Replacement (HMR):** Fast development cycle with instant updates.
11
- - **Tailwind CSS:** Integrated support for utility-first CSS.
11
+ - **Tailwind CSS v4:** Integrated support for the latest Tailwind CSS with CSS-first configuration.
12
+ - **Zero Configuration:** Get started quickly with sensible defaults.
12
13
 
14
+ ## Quick Start
13
15
 
16
+ ### Installation
17
+
18
+ ```bash
19
+ npm install arcanajs
20
+ ```
21
+
22
+ ### Initialize a New Project
23
+
24
+ ```bash
25
+ npx arcanajs init
26
+ ```
27
+
28
+ This creates the following structure:
29
+ ```
30
+ your-project/
31
+ ├── src/
32
+ │ ├── client/
33
+ │ │ └── index.tsx # Client-side entry point
34
+ │ ├── server/
35
+ │ │ └── index.ts # Server-side entry point
36
+ │ ├── views/
37
+ │ │ └── Home.tsx # Your first page component
38
+ │ └── globals.css # Tailwind CSS styles and theme
39
+ ├── postcss.config.js # PostCSS configuration
40
+ └── package.json
41
+ ```
42
+
43
+ ### Development
44
+
45
+ Start the development server:
46
+
47
+ ```bash
48
+ npm run dev
49
+ ```
50
+
51
+ Visit `http://localhost:3000` to see your application.
52
+
53
+ ### Build for Production
54
+
55
+ ```bash
56
+ npm run build
57
+ ```
58
+
59
+ ### Start Production Server
60
+
61
+ ```bash
62
+ npm run start
63
+ ```
64
+
65
+ ## Tailwind CSS v4 Integration
66
+
67
+ ArcanaJS comes with Tailwind CSS v4 pre-configured. Unlike previous versions, Tailwind v4 uses CSS-first configuration.
68
+
69
+ ### Theme Customization
70
+
71
+ Edit `src/globals.css` to customize your theme:
72
+
73
+ ```css
74
+ @import "tailwindcss";
75
+
76
+ @theme {
77
+ --color-primary: #3b82f6;
78
+ --font-family-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
79
+ --spacing-custom: 2.5rem;
80
+ }
81
+ ```
82
+
83
+ ### Using Tailwind Classes
84
+
85
+ Use Tailwind utility classes directly in your components:
86
+
87
+ ```tsx
88
+ export default function MyComponent() {
89
+ return (
90
+ <div className="bg-primary text-white p-custom rounded-lg">
91
+ <h1 className="text-2xl font-bold">Hello Tailwind v4!</h1>
92
+ </div>
93
+ );
94
+ }
95
+ ```
96
+
97
+ ### Custom Components
98
+
99
+ Define reusable component styles in your `globals.css`:
100
+
101
+ ```css
102
+ .btn {
103
+ @apply px-4 py-2 rounded font-medium transition-colors;
104
+ }
105
+
106
+ .btn-primary {
107
+ @apply btn bg-blue-600 text-white hover:bg-blue-700;
108
+ }
109
+ ```
110
+
111
+ ## Views and Routing
112
+
113
+ Create pages by adding components to the `src/views/` directory:
114
+
115
+ ```tsx
116
+ // src/views/About.tsx
117
+ import { Page, Head, Body } from 'arcanajs';
118
+
119
+ export default function About() {
120
+ return (
121
+ <Page>
122
+ <Head>
123
+ <title>About Us</title>
124
+ </Head>
125
+ <Body>
126
+ <div className="container mx-auto px-4 py-8">
127
+ <h1 className="text-4xl font-bold text-gray-900">About Us</h1>
128
+ </div>
129
+ </Body>
130
+ </Page>
131
+ );
132
+ }
133
+ ```
134
+
135
+ The page will automatically be available at `/About`.
136
+
137
+ ## Server Configuration
138
+
139
+ Customize your server in `src/server/index.ts`:
140
+
141
+ ```tsx
142
+ import express from 'express';
143
+ import { createArcanaServer } from 'arcanajs/server';
144
+
145
+ const app = express();
146
+ const server = createArcanaServer(app, {
147
+ // Custom configuration options
148
+ });
149
+
150
+ const PORT = process.env.PORT || 3000;
151
+ server.listen(PORT, () => {
152
+ console.log(`🚀 ArcanaJS server running on http://localhost:${PORT}`);
153
+ });
154
+ ```
155
+
156
+ ## Available Commands
157
+
158
+ - `arcanajs init` - Initialize a new ArcanaJS project
159
+ - `arcanajs dev` - Start development server with hot reload
160
+ - `arcanajs build` - Build for production
161
+ - `arcanajs start` - Start production server
162
+
163
+ ## Components
164
+
165
+ ArcanaJS provides several built-in components:
166
+
167
+ - `<Page>` - Wrapper component for pages
168
+ - `<Head>` - Document head management
169
+ - `<Body>` - Body content wrapper
170
+ - `<Link>` - Client-side navigation
171
+ - `<NavLink>` - Navigation link with active state
172
+
173
+ ## Hooks
174
+
175
+ - `useRouter()` - Access router functionality
176
+ - `useLocation()` - Get current location
177
+ - `useParams()` - Get URL parameters
178
+ - `useQuery()` - Get query parameters
179
+ - `usePage()` - Access page context
180
+ - `useHead()` - Manage document head
14
181
 
15
182
  ## License
16
183
 
@@ -4,13 +4,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const child_process_1 = require("child_process");
7
+ const fs_1 = __importDefault(require("fs"));
7
8
  const path_1 = __importDefault(require("path"));
8
9
  const webpack_1 = __importDefault(require("webpack"));
9
10
  const webpack_config_1 = require("./webpack.config");
10
11
  const args = process.argv.slice(2);
11
12
  const command = args[0];
12
13
  if (!command) {
13
- console.error("Please specify a command: dev, build, start");
14
+ console.error("Please specify a command: init, dev, build, start");
14
15
  process.exit(1);
15
16
  }
16
17
  const runCompiler = (compiler) => {
@@ -82,6 +83,90 @@ const dev = async () => {
82
83
  startDevServer();
83
84
  });
84
85
  };
86
+ const init = () => {
87
+ console.log("Initializing ArcanaJS project with Tailwind CSS...");
88
+ const cwd = process.cwd();
89
+ const templatesDir = path_1.default.resolve(__dirname, "../templates");
90
+ // Create necessary directories
91
+ const publicDir = path_1.default.resolve(cwd, "public");
92
+ const srcDir = path_1.default.resolve(cwd, "src");
93
+ const clientDir = path_1.default.resolve(cwd, "src/client");
94
+ const serverDir = path_1.default.resolve(cwd, "src/server");
95
+ const routesDir = path_1.default.resolve(cwd, "src/server/routes");
96
+ const controllersDir = path_1.default.resolve(cwd, "src/server/controllers");
97
+ const viewsDir = path_1.default.resolve(cwd, "src/views");
98
+ [
99
+ publicDir,
100
+ srcDir,
101
+ clientDir,
102
+ serverDir,
103
+ routesDir,
104
+ controllersDir,
105
+ viewsDir,
106
+ ].forEach((dir) => {
107
+ if (!fs_1.default.existsSync(dir)) {
108
+ fs_1.default.mkdirSync(dir, { recursive: true });
109
+ console.log(`Created directory: ${path_1.default.relative(cwd, dir)}`);
110
+ }
111
+ });
112
+ // Copy configuration files
113
+ const configFiles = [
114
+ { src: "package.json", dest: "package.json" },
115
+ { src: "postcss.config.js", dest: "postcss.config.js" },
116
+ { src: "globals.css", dest: "src/client/globals.css" },
117
+ { src: "client-index.tsx", dest: "src/client/index.tsx" },
118
+ { src: "server-index.ts", dest: "src/server/index.ts" },
119
+ { src: "server-routes-web.ts", dest: "src/server/routes/web.ts" },
120
+ {
121
+ src: "server-controller-home.ts",
122
+ dest: "src/server/controllers/HomeController.ts",
123
+ },
124
+ {
125
+ src: "HomePage.tsx",
126
+ dest: "src/views/HomePage.tsx",
127
+ },
128
+ {
129
+ src: "arcanajs.png",
130
+ dest: "public/arcanajs.png",
131
+ },
132
+ {
133
+ src: "arcanajs.svg",
134
+ dest: "public/arcanajs.svg",
135
+ },
136
+ {
137
+ src: "favicon.ico",
138
+ dest: "public/favicon.ico",
139
+ },
140
+ ];
141
+ configFiles.forEach(({ src, dest }) => {
142
+ const srcPath = path_1.default.resolve(templatesDir, src);
143
+ const destPath = path_1.default.resolve(cwd, dest);
144
+ if (!fs_1.default.existsSync(destPath)) {
145
+ fs_1.default.copyFileSync(srcPath, destPath);
146
+ console.log(`Created: ${dest}`);
147
+ }
148
+ else {
149
+ console.log(`Skipped: ${dest} (already exists)`);
150
+ }
151
+ });
152
+ // Create default error pages
153
+ const errorPages = ["NotFoundPage.tsx", "ErrorPage.tsx"];
154
+ errorPages.forEach((page) => {
155
+ const viewPath = path_1.default.resolve(cwd, `src/views/${page}`);
156
+ const templatePath = path_1.default.resolve(templatesDir, page);
157
+ if (!fs_1.default.existsSync(viewPath) && fs_1.default.existsSync(templatePath)) {
158
+ fs_1.default.copyFileSync(templatePath, viewPath);
159
+ console.log(`Created: src/views/${page}`);
160
+ }
161
+ });
162
+ console.log("\n✅ ArcanaJS project initialized successfully!");
163
+ console.log("\nNext steps:");
164
+ console.log("1. Run 'npm run dev' to start development");
165
+ console.log("2. Visit http://localhost:3000 to see your app");
166
+ console.log("3. Edit src/views/HomePage.tsx to customize your homepage");
167
+ console.log("4. Customize your theme in src/client/globals.css");
168
+ console.log("5. Add your Tailwind classes and enjoy!");
169
+ };
85
170
  const start = () => {
86
171
  process.env.NODE_ENV = "production";
87
172
  const serverPath = path_1.default.resolve(process.cwd(), "dist/server.js");
@@ -92,6 +177,9 @@ const start = () => {
92
177
  });
93
178
  };
94
179
  switch (command) {
180
+ case "init":
181
+ init();
182
+ break;
95
183
  case "build":
96
184
  build();
97
185
  break;
@@ -41,8 +41,25 @@ const findEntry = (searchPaths) => {
41
41
  return exampleServer;
42
42
  throw new Error(`Could not find entry point. Searched in: ${searchPaths.join(", ")}`);
43
43
  };
44
+ const getViewsLoaderPath = () => {
45
+ const viewsDir = path_1.default.resolve(cwd, "src/views");
46
+ const hasViews = fs_1.default.existsSync(viewsDir);
47
+ const viewsLoaderPath = path_1.default.resolve(__dirname, "../../node_modules/.cache/arcanajs/views-loader.js");
48
+ // Ensure cache directory exists
49
+ const cacheDir = path_1.default.dirname(viewsLoaderPath);
50
+ if (!fs_1.default.existsSync(cacheDir)) {
51
+ fs_1.default.mkdirSync(cacheDir, { recursive: true });
52
+ }
53
+ // Generate the loader file
54
+ const loaderContent = hasViews
55
+ ? `module.exports = require.context('${viewsDir}', true, /\\.(tsx|jsx)$/);`
56
+ : `module.exports = null;`;
57
+ fs_1.default.writeFileSync(viewsLoaderPath, loaderContent);
58
+ return viewsLoaderPath;
59
+ };
44
60
  const createClientConfig = () => {
45
61
  const isProduction = process.env.NODE_ENV === "production";
62
+ const viewsLoaderPath = getViewsLoaderPath();
46
63
  const clientEntry = findEntry([
47
64
  "src/client",
48
65
  "src/client/index",
@@ -65,6 +82,9 @@ const createClientConfig = () => {
65
82
  },
66
83
  resolve: {
67
84
  extensions: [".ts", ".tsx", ".js", ".jsx"],
85
+ alias: {
86
+ "arcana-views": viewsLoaderPath,
87
+ },
68
88
  },
69
89
  resolveLoader: {
70
90
  modules: ["node_modules", path_1.default.resolve(__dirname, "../../node_modules")],
@@ -94,8 +114,20 @@ const createClientConfig = () => {
94
114
  isProduction
95
115
  ? mini_css_extract_plugin_1.default.loader
96
116
  : resolveLoader("style-loader"),
97
- resolveLoader("css-loader"),
98
- resolveLoader("postcss-loader"),
117
+ {
118
+ loader: resolveLoader("css-loader"),
119
+ options: {
120
+ importLoaders: 1,
121
+ },
122
+ },
123
+ {
124
+ loader: resolveLoader("postcss-loader"),
125
+ options: {
126
+ postcssOptions: {
127
+ config: path_1.default.resolve(cwd, "postcss.config.js"),
128
+ },
129
+ },
130
+ },
99
131
  ],
100
132
  },
101
133
  {
@@ -152,20 +184,7 @@ const createServerConfig = () => {
152
184
  "src/server/index",
153
185
  "src/server/main",
154
186
  ]);
155
- // View Injection Logic
156
- const viewsDir = path_1.default.resolve(cwd, "src/views");
157
- const hasViews = fs_1.default.existsSync(viewsDir);
158
- const viewsLoaderPath = path_1.default.resolve(__dirname, "../../node_modules/.cache/arcanajs/views-loader.js");
159
- // Ensure cache directory exists
160
- const cacheDir = path_1.default.dirname(viewsLoaderPath);
161
- if (!fs_1.default.existsSync(cacheDir)) {
162
- fs_1.default.mkdirSync(cacheDir, { recursive: true });
163
- }
164
- // Generate the loader file
165
- const loaderContent = hasViews
166
- ? `module.exports = require.context('${viewsDir}', true, /\\.(tsx|jsx)$/);`
167
- : `module.exports = null;`;
168
- fs_1.default.writeFileSync(viewsLoaderPath, loaderContent);
187
+ const viewsLoaderPath = getViewsLoaderPath();
169
188
  return {
170
189
  mode: isProduction ? "production" : "development",
171
190
  target: "node",
@@ -1,10 +1,15 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.hydrateArcanaJS = void 0;
4
7
  const jsx_runtime_1 = require("react/jsx-runtime");
5
8
  const client_1 = require("react-dom/client");
6
9
  const HeadContext_1 = require("../shared/context/HeadContext");
7
10
  const ArcanaJSApp_1 = require("../shared/core/ArcanaJSApp");
11
+ const ErrorPage_1 = __importDefault(require("../shared/views/ErrorPage"));
12
+ const NotFoundPage_1 = __importDefault(require("../shared/views/NotFoundPage"));
8
13
  const hydrateArcanaJS = (viewsOrContext, layout) => {
9
14
  let views = {};
10
15
  if (viewsOrContext.keys && typeof viewsOrContext.keys === "function") {
@@ -16,6 +21,13 @@ const hydrateArcanaJS = (viewsOrContext, layout) => {
16
21
  else {
17
22
  views = viewsOrContext;
18
23
  }
24
+ // Add default error views if not present
25
+ if (!views["NotFoundPage"]) {
26
+ views["NotFoundPage"] = NotFoundPage_1.default;
27
+ }
28
+ if (!views["ErrorPage"]) {
29
+ views["ErrorPage"] = ErrorPage_1.default;
30
+ }
19
31
  const container = document.getElementById("root");
20
32
  const dataScript = document.getElementById("__ArcanaJS_DATA__");
21
33
  // Client-side HeadManager (noop for push, as Head handles client updates via useEffect)
@@ -15,3 +15,5 @@ export * from "./shared/hooks/usePage";
15
15
  export * from "./shared/hooks/useParams";
16
16
  export * from "./shared/hooks/useQuery";
17
17
  export * from "./shared/hooks/useRouter";
18
+ export { default as NotFoundPage } from "./shared/views/NotFoundPage";
19
+ export { default as ErrorPage } from "./shared/views/ErrorPage";
@@ -13,7 +13,11 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
13
13
  var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
+ var __importDefault = (this && this.__importDefault) || function (mod) {
17
+ return (mod && mod.__esModule) ? mod : { "default": mod };
18
+ };
16
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.ErrorPage = exports.NotFoundPage = void 0;
17
21
  __exportStar(require("./client"), exports);
18
22
  __exportStar(require("./shared/components/Body"), exports);
19
23
  __exportStar(require("./shared/components/Head"), exports);
@@ -31,3 +35,8 @@ __exportStar(require("./shared/hooks/usePage"), exports);
31
35
  __exportStar(require("./shared/hooks/useParams"), exports);
32
36
  __exportStar(require("./shared/hooks/useQuery"), exports);
33
37
  __exportStar(require("./shared/hooks/useRouter"), exports);
38
+ // Default error views
39
+ var NotFoundPage_1 = require("./shared/views/NotFoundPage");
40
+ Object.defineProperty(exports, "NotFoundPage", { enumerable: true, get: function () { return __importDefault(NotFoundPage_1).default; } });
41
+ var ErrorPage_1 = require("./shared/views/ErrorPage");
42
+ Object.defineProperty(exports, "ErrorPage", { enumerable: true, get: function () { return __importDefault(ErrorPage_1).default; } });
@@ -14,6 +14,8 @@ const ArcanaJSMiddleware_1 = require("./ArcanaJSMiddleware");
14
14
  const CsrfMiddleware_1 = require("./CsrfMiddleware");
15
15
  const DynamicRouter_1 = require("./DynamicRouter");
16
16
  const ResponseHandlerMiddleware_1 = require("./ResponseHandlerMiddleware");
17
+ const NotFoundPage_1 = __importDefault(require("../shared/views/NotFoundPage"));
18
+ const ErrorPage_1 = __importDefault(require("../shared/views/ErrorPage"));
17
19
  class ArcanaJSServer {
18
20
  constructor(config) {
19
21
  this.config = config;
@@ -51,6 +53,13 @@ class ArcanaJSServer {
51
53
  console.warn("No views found. Please check your views directory.");
52
54
  views = {};
53
55
  }
56
+ // Add default error views if not already present
57
+ if (!views.NotFoundPage) {
58
+ views.NotFoundPage = NotFoundPage_1.default;
59
+ }
60
+ if (!views.ErrorPage) {
61
+ views.ErrorPage = ErrorPage_1.default;
62
+ }
54
63
  // Security and Performance
55
64
  this.app.use((0, helmet_1.default)({
56
65
  contentSecurityPolicy: false,
@@ -1,6 +1,15 @@
1
+ import { Express } from "express";
2
+ import { ArcanaJSServer, ArcanaJSConfig } from "./server/ArcanaJSServer";
1
3
  export * from "./server/ArcanaJSMiddleware";
2
4
  export * from "./server/ArcanaJSServer";
3
5
  export { default as ControllerBinder } from "./server/ControllerBinder";
4
6
  export * from "./server/DynamicRouter";
5
7
  export * from "./server/Router";
6
8
  export { default as Route } from "./server/Router";
9
+ /**
10
+ * Create an ArcanaJS server with the given Express app
11
+ * @param app Express application instance
12
+ * @param config Optional ArcanaJS configuration
13
+ * @returns ArcanaJSServer instance
14
+ */
15
+ export declare function createArcanaServer(app: Express, config?: Partial<ArcanaJSConfig>): ArcanaJSServer;
@@ -18,6 +18,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
18
18
  };
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
20
  exports.Route = exports.ControllerBinder = void 0;
21
+ exports.createArcanaServer = createArcanaServer;
22
+ const ArcanaJSServer_1 = require("./server/ArcanaJSServer");
21
23
  __exportStar(require("./server/ArcanaJSMiddleware"), exports);
22
24
  __exportStar(require("./server/ArcanaJSServer"), exports);
23
25
  var ControllerBinder_1 = require("./server/ControllerBinder");
@@ -26,3 +28,13 @@ __exportStar(require("./server/DynamicRouter"), exports);
26
28
  __exportStar(require("./server/Router"), exports);
27
29
  var Router_1 = require("./server/Router");
28
30
  Object.defineProperty(exports, "Route", { enumerable: true, get: function () { return __importDefault(Router_1).default; } });
31
+ /**
32
+ * Create an ArcanaJS server with the given Express app
33
+ * @param app Express application instance
34
+ * @param config Optional ArcanaJS configuration
35
+ * @returns ArcanaJSServer instance
36
+ */
37
+ function createArcanaServer(app, config) {
38
+ const server = new ArcanaJSServer_1.ArcanaJSServer({ ...config });
39
+ return server;
40
+ }
@@ -0,0 +1,7 @@
1
+ interface ErrorPageProps {
2
+ message?: string;
3
+ statusCode?: number;
4
+ stack?: string;
5
+ }
6
+ export default function ErrorPage({ message, statusCode, stack, }: ErrorPageProps): import("react/jsx-runtime").JSX.Element;
7
+ export {};
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = ErrorPage;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const Page_1 = require("../components/Page");
6
+ const Head_1 = require("../components/Head");
7
+ const Body_1 = require("../components/Body");
8
+ function ErrorPage({ message = "Something went wrong", statusCode = 500, stack, }) {
9
+ const isDevelopment = process.env.NODE_ENV === "development";
10
+ return ((0, jsx_runtime_1.jsxs)(Page_1.Page, { children: [(0, jsx_runtime_1.jsxs)(Head_1.Head, { children: [(0, jsx_runtime_1.jsxs)("title", { children: [statusCode, " - Server Error"] }), (0, jsx_runtime_1.jsx)("meta", { name: "description", content: "An error occurred on the server" })] }), (0, jsx_runtime_1.jsx)(Body_1.Body, { children: (0, jsx_runtime_1.jsx)("div", { className: "min-h-screen bg-red-50 flex flex-col justify-center items-center px-4", children: (0, jsx_runtime_1.jsxs)("div", { className: "max-w-2xl w-full text-center", children: [(0, jsx_runtime_1.jsxs)("div", { className: "mb-8", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-6xl mb-4", children: (0, jsx_runtime_1.jsx)("span", { className: "text-red-500", children: "\u26A0\uFE0F" }) }), (0, jsx_runtime_1.jsx)("h1", { className: "text-6xl font-bold text-red-500 mb-4", children: statusCode }), (0, jsx_runtime_1.jsx)("h2", { className: "text-2xl font-semibold text-gray-900 mb-2", children: "Server Error" }), (0, jsx_runtime_1.jsx)("p", { className: "text-gray-600 mb-8", children: message })] }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("a", { href: "/", className: "inline-flex items-center justify-center px-6 py-3 border border-transparent text-base font-medium rounded-md text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 transition-colors duration-200", children: [(0, jsx_runtime_1.jsx)("svg", { className: "w-5 h-5 mr-2", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" }) }), "Go Home"] }), (0, jsx_runtime_1.jsxs)("button", { onClick: () => window.location.reload(), className: "block w-full px-6 py-3 border border-gray-300 text-base font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 transition-colors duration-200", children: [(0, jsx_runtime_1.jsx)("svg", { className: "w-5 h-5 inline mr-2", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" }) }), "Try Again"] })] }), isDevelopment && stack && ((0, jsx_runtime_1.jsx)("div", { className: "mt-8 text-left", children: (0, jsx_runtime_1.jsxs)("details", { className: "bg-gray-100 rounded-lg p-4", children: [(0, jsx_runtime_1.jsx)("summary", { className: "cursor-pointer font-medium text-gray-700 mb-2", children: "Stack Trace (Development Only)" }), (0, jsx_runtime_1.jsx)("pre", { className: "text-xs text-gray-600 overflow-x-auto whitespace-pre-wrap", children: stack })] }) })), (0, jsx_runtime_1.jsx)("div", { className: "mt-8 text-sm text-gray-500", children: (0, jsx_runtime_1.jsxs)("p", { children: ["If this problem persists, please", " ", (0, jsx_runtime_1.jsx)("a", { href: "/contact", className: "text-red-600 hover:text-red-500 underline", children: "contact support" }), "."] }) })] }) }) })] }));
11
+ }
@@ -0,0 +1,5 @@
1
+ interface NotFoundPageProps {
2
+ url?: string;
3
+ }
4
+ export default function NotFoundPage({ url }: NotFoundPageProps): import("react/jsx-runtime").JSX.Element;
5
+ export {};
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = NotFoundPage;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const Page_1 = require("../components/Page");
6
+ const Head_1 = require("../components/Head");
7
+ const Body_1 = require("../components/Body");
8
+ function NotFoundPage({ url }) {
9
+ return ((0, jsx_runtime_1.jsxs)(Page_1.Page, { children: [(0, jsx_runtime_1.jsxs)(Head_1.Head, { children: [(0, jsx_runtime_1.jsx)("title", { children: "404 - Page Not Found" }), (0, jsx_runtime_1.jsx)("meta", { name: "description", content: "The page you're looking for doesn't exist" })] }), (0, jsx_runtime_1.jsx)(Body_1.Body, { children: (0, jsx_runtime_1.jsx)("div", { className: "min-h-screen bg-gray-50 flex flex-col justify-center items-center px-4", children: (0, jsx_runtime_1.jsxs)("div", { className: "max-w-md w-full text-center", children: [(0, jsx_runtime_1.jsxs)("div", { className: "mb-8", children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-9xl font-bold text-gray-200 mb-4", children: "404" }), (0, jsx_runtime_1.jsx)("h2", { className: "text-2xl font-semibold text-gray-900 mb-2", children: "Page Not Found" }), (0, jsx_runtime_1.jsx)("p", { className: "text-gray-600 mb-8", children: url
10
+ ? `The page "${url}" you're looking for doesn't exist.`
11
+ : "The page you're looking for doesn't exist." })] }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("a", { href: "/", className: "inline-flex items-center justify-center px-6 py-3 border border-transparent text-base font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors duration-200", children: [(0, jsx_runtime_1.jsx)("svg", { className: "w-5 h-5 mr-2", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" }) }), "Go Home"] }), (0, jsx_runtime_1.jsxs)("button", { onClick: () => window.history.back(), className: "block w-full px-6 py-3 border border-gray-300 text-base font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors duration-200", children: [(0, jsx_runtime_1.jsx)("svg", { className: "w-5 h-5 inline mr-2", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M10 19l-7-7m0 0l7-7m-7 7h18" }) }), "Go Back"] })] }), (0, jsx_runtime_1.jsx)("div", { className: "mt-8 text-sm text-gray-500", children: (0, jsx_runtime_1.jsxs)("p", { children: ["If you think this is an error, please", " ", (0, jsx_runtime_1.jsx)("a", { href: "/contact", className: "text-blue-600 hover:text-blue-500 underline", children: "contact us" }), "."] }) })] }) }) })] }));
12
+ }