baraqex 2.0.3 → 2.0.4
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/dist/browser.js +11 -9
- package/dist/browser.js.map +2 -2
- package/dist/core/jsx-runtime.d.ts +1 -1
- package/dist/core/jsx-runtime.d.ts.map +1 -1
- package/dist/index.js +11 -9
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
- package/templates/fullstack-app/README.md +62 -37
- package/templates/fullstack-app/build-wasm.js +80 -0
- package/templates/fullstack-app/build.mjs +133 -0
- package/templates/fullstack-app/go/main.go +53 -0
- package/templates/fullstack-app/index.html +16 -36
- package/templates/fullstack-app/package.json +41 -35
- package/templates/fullstack-app/postcss.config.js +6 -6
- package/templates/fullstack-app/public/wasm/.gitkeep +1 -0
- package/templates/fullstack-app/server.ts +220 -474
- package/templates/fullstack-app/src/App.tsx +77 -0
- package/templates/fullstack-app/src/main.tsx +31 -155
- package/templates/fullstack-app/src/pages/About.tsx +40 -0
- package/templates/fullstack-app/src/pages/Home.tsx +67 -0
- package/templates/fullstack-app/src/pages/WasmDemo.tsx +81 -0
- package/templates/fullstack-app/src/styles.css +12 -5
- package/templates/fullstack-app/tailwind.config.js +8 -11
- package/templates/fullstack-app/tsconfig.json +18 -18
- package/templates/fullstack-app/api/hello.js +0 -11
- package/templates/fullstack-app/api/hello.ts +0 -18
- package/templates/fullstack-app/api/users/[id].ts +0 -73
- package/templates/fullstack-app/api/users/index.ts +0 -32
- package/templates/fullstack-app/build/main.css +0 -874
- package/templates/fullstack-app/build/main.css.map +0 -7
- package/templates/fullstack-app/build/main.js +0 -967
- package/templates/fullstack-app/build/main.js.map +0 -7
- package/templates/fullstack-app/build.ts +0 -210
- package/templates/fullstack-app/process-tailwind.js +0 -45
- package/templates/fullstack-app/public/_redirects +0 -1
- package/templates/fullstack-app/public/route-handler.js +0 -47
- package/templates/fullstack-app/public/spa-fix.html +0 -17
- package/templates/fullstack-app/public/styles.css +0 -975
- package/templates/fullstack-app/public/tailwind.css +0 -15
- package/templates/fullstack-app/server.js +0 -267
- package/templates/fullstack-app/src/README.md +0 -55
- package/templates/fullstack-app/src/client.js +0 -87
- package/templates/fullstack-app/src/components/ClientHome.tsx +0 -0
- package/templates/fullstack-app/src/components/ErrorBoundary.tsx +0 -36
- package/templates/fullstack-app/src/components/Layout.tsx +0 -42
- package/templates/fullstack-app/src/components/StateDemo.tsx +0 -207
- package/templates/fullstack-app/src/components/UserList.tsx +0 -44
- package/templates/fullstack-app/src/config.ts +0 -42
- package/templates/fullstack-app/src/data/api.ts +0 -206
- package/templates/fullstack-app/src/middleware.ts +0 -28
- package/templates/fullstack-app/src/pages/404.tsx +0 -28
- package/templates/fullstack-app/src/pages/[id].tsx +0 -0
- package/templates/fullstack-app/src/pages/_app.tsx +0 -11
- package/templates/fullstack-app/src/pages/_document.tsx +0 -25
- package/templates/fullstack-app/src/pages/_error.tsx +0 -45
- package/templates/fullstack-app/src/pages/about/index.tsx +0 -67
- package/templates/fullstack-app/src/pages/about.tsx +0 -71
- package/templates/fullstack-app/src/pages/api/users/[id].ts +0 -73
- package/templates/fullstack-app/src/pages/api/users/index.ts +0 -43
- package/templates/fullstack-app/src/pages/index.tsx +0 -110
- package/templates/fullstack-app/src/pages/users/[id].tsx +0 -153
- package/templates/fullstack-app/src/pages/users.tsx +0 -60
- package/templates/fullstack-app/src/pages/wasm-demo.tsx +0 -1
- package/templates/fullstack-app/src/router.ts +0 -255
- package/templates/fullstack-app/vite.config.js +0 -87
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/* All Tailwind directives */
|
|
2
|
-
@tailwind base;
|
|
3
|
-
@tailwind components;
|
|
4
|
-
@tailwind utilities;
|
|
5
|
-
|
|
6
|
-
/* Custom styles */
|
|
7
|
-
body {
|
|
8
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
|
9
|
-
line-height: 1.6;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/* Any custom utility classes can go here */
|
|
13
|
-
.custom-container {
|
|
14
|
-
@apply max-w-4xl mx-auto px-4 py-8;
|
|
15
|
-
}
|
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
import express from 'express';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
|
-
import fs from 'fs';
|
|
5
|
-
import dotenv from 'dotenv';
|
|
6
|
-
import { createServer } from 'http';
|
|
7
|
-
import { Server as SocketServer } from 'socket.io';
|
|
8
|
-
import compression from 'compression';
|
|
9
|
-
import cors from 'cors';
|
|
10
|
-
|
|
11
|
-
// Load environment variables
|
|
12
|
-
dotenv.config();
|
|
13
|
-
|
|
14
|
-
// Get __dirname equivalent in ESM
|
|
15
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
-
const __dirname = path.dirname(__filename);
|
|
17
|
-
|
|
18
|
-
// Create Express app
|
|
19
|
-
const app = express();
|
|
20
|
-
const PORT = process.env.PORT || 3000;
|
|
21
|
-
const httpServer = createServer(app);
|
|
22
|
-
|
|
23
|
-
// Create socket.io server
|
|
24
|
-
const io = new SocketServer(httpServer, {
|
|
25
|
-
cors: {
|
|
26
|
-
origin: process.env.NODE_ENV === 'production' ? false : '*',
|
|
27
|
-
methods: ['GET', 'POST']
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
// Middleware setup
|
|
32
|
-
app.use(express.json());
|
|
33
|
-
app.use(express.urlencoded({ extended: true }));
|
|
34
|
-
app.use(compression()); // Add compression for better performance
|
|
35
|
-
app.use(cors()); // Enable CORS for API access
|
|
36
|
-
|
|
37
|
-
// Add request logging in development mode
|
|
38
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
39
|
-
app.use((req, res, next) => {
|
|
40
|
-
console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
|
|
41
|
-
next();
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// For production, serve the built files
|
|
46
|
-
if (process.env.NODE_ENV === 'production') {
|
|
47
|
-
app.use(express.static(path.join(__dirname, 'dist')));
|
|
48
|
-
} else {
|
|
49
|
-
// In development, we'll let Vite handle the frontend
|
|
50
|
-
console.log('Running in development mode - Vite handles the frontend');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Global in-memory store for demo purposes
|
|
54
|
-
// In a real app, you would use a database
|
|
55
|
-
const store = {
|
|
56
|
-
users: [
|
|
57
|
-
{ id: 1, name: 'User 1', email: 'user1@example.com' },
|
|
58
|
-
{ id: 2, name: 'User 2', email: 'user2@example.com' },
|
|
59
|
-
{ id: 3, name: 'User 3', email: 'user3@example.com' }
|
|
60
|
-
],
|
|
61
|
-
posts: [
|
|
62
|
-
{ id: 1, title: 'Post 1', content: 'Content for post 1', authorId: 1 },
|
|
63
|
-
{ id: 2, title: 'Post 2', content: 'Content for post 2', authorId: 2 },
|
|
64
|
-
{ id: 3, title: 'Post 3', content: 'Content for post 3', authorId: 1 }
|
|
65
|
-
]
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
// Core API routes (available without file-based routing)
|
|
69
|
-
app.get('/api/hello', (req, res) => {
|
|
70
|
-
res.json({
|
|
71
|
-
message: "Hello from the API!",
|
|
72
|
-
time: new Date().toISOString(),
|
|
73
|
-
features: [
|
|
74
|
-
"Server-side rendering",
|
|
75
|
-
"API routes",
|
|
76
|
-
"Component-based UI",
|
|
77
|
-
"React-like development experience",
|
|
78
|
-
"WebSocket integration",
|
|
79
|
-
"Database connectivity",
|
|
80
|
-
"Authentication"
|
|
81
|
-
]
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
// REST API endpoints for store data
|
|
86
|
-
app.get('/api/users', (req, res) => {
|
|
87
|
-
res.json(store.users);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
app.get('/api/users/:id', (req, res) => {
|
|
91
|
-
const user = store.users.find(u => u.id === parseInt(req.params.id));
|
|
92
|
-
if (user) {
|
|
93
|
-
res.json(user);
|
|
94
|
-
} else {
|
|
95
|
-
res.status(404).json({ error: 'User not found' });
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
app.get('/api/posts', (req, res) => {
|
|
100
|
-
const { authorId } = req.query;
|
|
101
|
-
|
|
102
|
-
if (authorId) {
|
|
103
|
-
const filteredPosts = store.posts.filter(p => p.authorId === parseInt(authorId));
|
|
104
|
-
return res.json(filteredPosts);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
res.json(store.posts);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
// Improved dynamic API route loading with better error handling
|
|
111
|
-
const apiDir = path.join(__dirname, 'api');
|
|
112
|
-
if (fs.existsSync(apiDir)) {
|
|
113
|
-
console.log('Loading API routes from directory...');
|
|
114
|
-
|
|
115
|
-
// Function to map file paths to API routes
|
|
116
|
-
const registerApiRoutes = async (dir, routePrefix = '') => {
|
|
117
|
-
try {
|
|
118
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
119
|
-
|
|
120
|
-
for (const entry of entries) {
|
|
121
|
-
const fullPath = path.join(dir, entry.name);
|
|
122
|
-
|
|
123
|
-
if (entry.isDirectory()) {
|
|
124
|
-
// Process directories recursively
|
|
125
|
-
await registerApiRoutes(fullPath, `${routePrefix}/${entry.name}`);
|
|
126
|
-
} else if (entry.name.endsWith('.js') || entry.name.endsWith('.ts')) {
|
|
127
|
-
// Process API file
|
|
128
|
-
try {
|
|
129
|
-
const fileName = entry.name.replace(/\.[^/.]+$/, ""); // Remove extension
|
|
130
|
-
const routePath = fileName === 'index'
|
|
131
|
-
? routePrefix
|
|
132
|
-
: fileName.startsWith('[') && fileName.endsWith(']')
|
|
133
|
-
? `${routePrefix}/:${fileName.slice(1, -1)}` // Convert [param] to :param
|
|
134
|
-
: `${routePrefix}/${fileName}`;
|
|
135
|
-
|
|
136
|
-
// Import the route module
|
|
137
|
-
const module = await import(`file://${fullPath}`);
|
|
138
|
-
|
|
139
|
-
// Register handlers for HTTP methods
|
|
140
|
-
['get', 'post', 'put', 'delete', 'patch'].forEach(method => {
|
|
141
|
-
if (typeof module[method] === 'function') {
|
|
142
|
-
console.log(` Registered ${method.toUpperCase()} ${routePath}`);
|
|
143
|
-
app[method](`/api${routePath}`, module[method]);
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// Special case for 'delete' which might be named 'delete_' in some files
|
|
148
|
-
if (typeof module['delete_'] === 'function') {
|
|
149
|
-
console.log(` Registered DELETE ${routePath}`);
|
|
150
|
-
app.delete(`/api${routePath}`, module['delete_']);
|
|
151
|
-
}
|
|
152
|
-
} catch (err) {
|
|
153
|
-
console.error(`Error loading API route ${fullPath}:`, err);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
} catch (err) {
|
|
158
|
-
console.error(`Error reading directory ${dir}:`, err);
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
// Start registering API routes
|
|
163
|
-
registerApiRoutes(apiDir).catch(err => {
|
|
164
|
-
console.error('Error loading API routes:', err);
|
|
165
|
-
});
|
|
166
|
-
} else {
|
|
167
|
-
console.log('API directory not found, skipping API route loading');
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// WebSocket setup
|
|
171
|
-
io.on('connection', (socket) => {
|
|
172
|
-
console.log('Client connected:', socket.id);
|
|
173
|
-
|
|
174
|
-
// Send welcome message
|
|
175
|
-
socket.emit('welcome', {
|
|
176
|
-
message: 'Connected to WebSocket server',
|
|
177
|
-
time: new Date().toISOString()
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
// Handle client messages
|
|
181
|
-
socket.on('message', (data) => {
|
|
182
|
-
console.log('Received message:', data);
|
|
183
|
-
// Broadcast to all clients
|
|
184
|
-
io.emit('broadcast', {
|
|
185
|
-
from: socket.id,
|
|
186
|
-
data,
|
|
187
|
-
time: new Date().toISOString()
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
// Handle disconnect
|
|
192
|
-
socket.on('disconnect', () => {
|
|
193
|
-
console.log('Client disconnected:', socket.id);
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
// In production or if using SSR, handle all routes
|
|
198
|
-
app.get('*', (req, res, next) => {
|
|
199
|
-
// Skip API routes
|
|
200
|
-
if (req.path.startsWith('/api/')) {
|
|
201
|
-
return next();
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// In production, serve the index.html
|
|
205
|
-
if (process.env.NODE_ENV === 'production') {
|
|
206
|
-
const indexPath = path.join(__dirname, 'dist', 'index.html');
|
|
207
|
-
if (fs.existsSync(indexPath)) {
|
|
208
|
-
try {
|
|
209
|
-
// Read the index.html file
|
|
210
|
-
let html = fs.readFileSync(indexPath, 'utf8');
|
|
211
|
-
|
|
212
|
-
// Create initial state for client hydration
|
|
213
|
-
const initialState = {
|
|
214
|
-
route: req.path,
|
|
215
|
-
timestamp: new Date().toISOString()
|
|
216
|
-
};
|
|
217
|
-
|
|
218
|
-
// Inject initial state for hydration
|
|
219
|
-
html = html.replace(
|
|
220
|
-
'</head>',
|
|
221
|
-
`<script>window.__INITIAL_STATE__ = ${JSON.stringify(initialState)};</script></head>`
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
res.send(html);
|
|
225
|
-
} catch (error) {
|
|
226
|
-
console.error('Error serving HTML:', error);
|
|
227
|
-
res.status(500).send('Server Error');
|
|
228
|
-
}
|
|
229
|
-
} else {
|
|
230
|
-
res.status(404).send('Not found');
|
|
231
|
-
}
|
|
232
|
-
} else {
|
|
233
|
-
// In development, use Vite's dev server
|
|
234
|
-
// Instead of redirecting, proxy the request to maintain the URL path
|
|
235
|
-
// This allows Vite to handle the route properly
|
|
236
|
-
const viteDevServerUrl = 'http://localhost:5173';
|
|
237
|
-
|
|
238
|
-
// Tell the client which route to handle via response headers
|
|
239
|
-
res.setHeader('x-original-route', req.path);
|
|
240
|
-
|
|
241
|
-
// Forward the request to the Vite dev server with the original path
|
|
242
|
-
res.redirect(302, `${viteDevServerUrl}${req.path}`);
|
|
243
|
-
|
|
244
|
-
// Note: If the above still causes 404s, you can try this alternative
|
|
245
|
-
// res.redirect(302, `${viteDevServerUrl}/?_path=${encodeURIComponent(req.path)}`);
|
|
246
|
-
}
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
// Error handling middleware
|
|
250
|
-
app.use((err, req, res, next) => {
|
|
251
|
-
console.error('Server error:', err);
|
|
252
|
-
res.status(500).json({
|
|
253
|
-
error: process.env.NODE_ENV === 'production'
|
|
254
|
-
? 'Internal server error'
|
|
255
|
-
: err.message
|
|
256
|
-
});
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
// Start server
|
|
260
|
-
httpServer.listen(PORT, () => {
|
|
261
|
-
console.log(`Server running on http://localhost:${PORT}`);
|
|
262
|
-
|
|
263
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
264
|
-
console.log(`Frontend development server will run on http://localhost:5173`);
|
|
265
|
-
console.log(`Run 'npm run dev' to start both servers`);
|
|
266
|
-
}
|
|
267
|
-
});
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# Frontend Hamroun Fullstack Template
|
|
2
|
-
|
|
3
|
-
This template uses a file-system based routing system similar to Next.js. Here's how to use it:
|
|
4
|
-
|
|
5
|
-
## Creating New Pages
|
|
6
|
-
|
|
7
|
-
1. Add new pages in the `src/pages` directory
|
|
8
|
-
2. File names become routes automatically:
|
|
9
|
-
- `src/pages/index.tsx` → `/`
|
|
10
|
-
- `src/pages/about.tsx` → `/about`
|
|
11
|
-
- `src/pages/users/[id].tsx` → `/users/:id`
|
|
12
|
-
|
|
13
|
-
## Page Structure
|
|
14
|
-
|
|
15
|
-
Each page should follow this structure:
|
|
16
|
-
|
|
17
|
-
```tsx
|
|
18
|
-
import { jsx } from 'frontend-hamroun';
|
|
19
|
-
import Layout from '../components/Layout';
|
|
20
|
-
|
|
21
|
-
const YourPage = ({ initialState }) => (
|
|
22
|
-
<Layout title="Your Page Title">
|
|
23
|
-
{/* Your content here */}
|
|
24
|
-
</Layout>
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
// Optional: Add data fetching
|
|
28
|
-
YourPage.getInitialData = async (path) => {
|
|
29
|
-
// Fetch any data your page needs
|
|
30
|
-
return {
|
|
31
|
-
yourData: await fetchSomething()
|
|
32
|
-
};
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export default YourPage;
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Data Fetching
|
|
39
|
-
|
|
40
|
-
Use the API utilities in `src/data/api.ts` to fetch data in a consistent way:
|
|
41
|
-
|
|
42
|
-
```tsx
|
|
43
|
-
import { UserApi } from '../data/api';
|
|
44
|
-
|
|
45
|
-
// In your getInitialData method:
|
|
46
|
-
const users = await UserApi.getAll();
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Components
|
|
50
|
-
|
|
51
|
-
Store reusable components in the `src/components` directory and import them in your pages.
|
|
52
|
-
|
|
53
|
-
## Navigation
|
|
54
|
-
|
|
55
|
-
Client-side navigation is handled automatically. Just use regular `<a href="...">` links.
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { hydrate, createElement, jsx } from 'frontend-hamroun';
|
|
2
|
-
|
|
3
|
-
// Add global jsx for compatibility
|
|
4
|
-
window.jsx = jsx;
|
|
5
|
-
window.createElement = createElement;
|
|
6
|
-
|
|
7
|
-
// Get the initial state from the server (if available)
|
|
8
|
-
const initialState = window.__INITIAL_STATE__ || {
|
|
9
|
-
route: window.location.pathname
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
// Dynamically import the right page component based on the route
|
|
13
|
-
async function loadAndHydrateComponent() {
|
|
14
|
-
try {
|
|
15
|
-
const path = initialState.route || '/';
|
|
16
|
-
const normalizedPath = path === '/' ? '/index' : path;
|
|
17
|
-
|
|
18
|
-
console.log(`Loading component for path: ${normalizedPath}`);
|
|
19
|
-
|
|
20
|
-
// Try to import the page component
|
|
21
|
-
const module = await import(`./pages${normalizedPath}.js`).catch(() =>
|
|
22
|
-
import(`./pages${normalizedPath}/index.js`)).catch(() => {
|
|
23
|
-
console.warn(`No component found for ${normalizedPath}, falling back to index`);
|
|
24
|
-
return import('./pages/index.js');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
const PageComponent = module.default;
|
|
28
|
-
|
|
29
|
-
if (!PageComponent) {
|
|
30
|
-
throw new Error(`No default export found in module for ${normalizedPath}`);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Find the root element - try both 'root' and 'app' IDs
|
|
34
|
-
const rootElement = document.getElementById('root') || document.getElementById('app');
|
|
35
|
-
|
|
36
|
-
if (!rootElement) {
|
|
37
|
-
throw new Error('Could not find root element with id "root" or "app"');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Hydrate the application
|
|
41
|
-
hydrate(jsx(PageComponent, { initialState }), rootElement);
|
|
42
|
-
console.log('Hydration complete');
|
|
43
|
-
|
|
44
|
-
// Add navigation event listeners if using client-side routing
|
|
45
|
-
document.addEventListener('click', handleLinkClicks);
|
|
46
|
-
window.addEventListener('popstate', handleRouteChange);
|
|
47
|
-
|
|
48
|
-
return true;
|
|
49
|
-
} catch (error) {
|
|
50
|
-
console.error('Error during hydration:', error);
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Handle client-side navigation for links
|
|
56
|
-
function handleLinkClicks(event) {
|
|
57
|
-
// Only handle links within our app
|
|
58
|
-
if (event.target.tagName === 'A' &&
|
|
59
|
-
event.target.origin === window.location.origin &&
|
|
60
|
-
!event.target.hasAttribute('external')) {
|
|
61
|
-
|
|
62
|
-
event.preventDefault();
|
|
63
|
-
const href = event.target.getAttribute('href');
|
|
64
|
-
|
|
65
|
-
// Update history and load the new component
|
|
66
|
-
window.history.pushState({}, '', href);
|
|
67
|
-
handleRouteChange();
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Handle route changes (back/forward navigation or pushState)
|
|
72
|
-
function handleRouteChange() {
|
|
73
|
-
// Update the initialState with the new route
|
|
74
|
-
initialState.route = window.location.pathname;
|
|
75
|
-
|
|
76
|
-
// Load and hydrate the new component
|
|
77
|
-
loadAndHydrateComponent().catch(err => {
|
|
78
|
-
console.error('Failed to load component after route change:', err);
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Wait for DOM to be ready before hydrating
|
|
83
|
-
if (document.readyState === 'loading') {
|
|
84
|
-
document.addEventListener('DOMContentLoaded', loadAndHydrateComponent);
|
|
85
|
-
} else {
|
|
86
|
-
loadAndHydrateComponent();
|
|
87
|
-
}
|
|
File without changes
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { jsx, useState, useEffect, useErrorBoundary } from 'frontend-hamroun';
|
|
2
|
-
|
|
3
|
-
interface ErrorBoundaryProps {
|
|
4
|
-
children: any;
|
|
5
|
-
fallback?: (error: Error, reset: () => void) => any;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function ErrorBoundary({ children, fallback }: ErrorBoundaryProps) {
|
|
9
|
-
const [error, resetError] = useErrorBoundary();
|
|
10
|
-
|
|
11
|
-
if (error) {
|
|
12
|
-
if (fallback) {
|
|
13
|
-
return fallback(error, resetError);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return (
|
|
17
|
-
<div className="error-boundary p-4 border border-red-500 rounded bg-red-50">
|
|
18
|
-
<h3 className="text-lg font-medium text-red-800 mb-2">Something went wrong</h3>
|
|
19
|
-
<p className="text-red-600 mb-2">{error.message}</p>
|
|
20
|
-
<button
|
|
21
|
-
onClick={resetError}
|
|
22
|
-
className="px-3 py-1 bg-red-600 text-white rounded hover:bg-red-700"
|
|
23
|
-
>
|
|
24
|
-
Try again
|
|
25
|
-
</button>
|
|
26
|
-
{process.env.NODE_ENV !== 'production' && (
|
|
27
|
-
<pre className="mt-3 text-xs overflow-auto p-2 bg-gray-100">
|
|
28
|
-
{error.stack}
|
|
29
|
-
</pre>
|
|
30
|
-
)}
|
|
31
|
-
</div>
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return children;
|
|
36
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { jsx } from 'frontend-hamroun';
|
|
2
|
-
|
|
3
|
-
interface LayoutProps {
|
|
4
|
-
children: any;
|
|
5
|
-
title?: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export default function Layout({ children, title = 'Frontend Hamroun App' }: LayoutProps) {
|
|
9
|
-
return (
|
|
10
|
-
<div className="min-h-screen flex flex-col bg-gray-50">
|
|
11
|
-
<header className="bg-blue-600 text-white">
|
|
12
|
-
<div className="container mx-auto px-4 py-4 flex justify-between items-center">
|
|
13
|
-
<h1 className="text-xl font-bold">
|
|
14
|
-
<a href="/" className="hover:text-blue-100">
|
|
15
|
-
{title}
|
|
16
|
-
</a>
|
|
17
|
-
</h1>
|
|
18
|
-
|
|
19
|
-
<nav>
|
|
20
|
-
<ul className="flex space-x-6">
|
|
21
|
-
<li><a href="/" className="hover:text-blue-100">Home</a></li>
|
|
22
|
-
<li><a href="/about" className="hover:text-blue-100">About</a></li>
|
|
23
|
-
{/* WASM demo link removed */}
|
|
24
|
-
</ul>
|
|
25
|
-
</nav>
|
|
26
|
-
</div>
|
|
27
|
-
</header>
|
|
28
|
-
|
|
29
|
-
<main className="flex-grow container mx-auto px-4 py-8">
|
|
30
|
-
{children}
|
|
31
|
-
</main>
|
|
32
|
-
|
|
33
|
-
<footer className="bg-gray-800 text-white">
|
|
34
|
-
<div className="container mx-auto px-4 py-6">
|
|
35
|
-
<p className="text-center text-gray-400">
|
|
36
|
-
© {new Date().getFullYear()} Frontend Hamroun App. All rights reserved.
|
|
37
|
-
</p>
|
|
38
|
-
</div>
|
|
39
|
-
</footer>
|
|
40
|
-
</div>
|
|
41
|
-
);
|
|
42
|
-
}
|