frontend-hamroun 1.2.77 → 1.2.79
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/batch/package.json +1 -1
- package/dist/client-router/package.json +1 -1
- package/dist/component/package.json +1 -1
- package/dist/context/package.json +1 -1
- package/dist/event-bus/package.json +1 -1
- package/dist/forms/package.json +1 -1
- package/dist/hooks/package.json +1 -1
- package/dist/index.mjs +1 -0
- package/dist/jsx-runtime/package.json +1 -1
- package/dist/lifecycle-events/package.json +1 -1
- package/dist/package.json +1 -1
- package/dist/render-component/package.json +1 -1
- package/dist/renderer/package.json +1 -1
- package/dist/router/package.json +1 -1
- package/dist/server/package.json +1 -1
- package/dist/server/src/index.js +24 -23
- package/dist/server/src/index.js.map +1 -1
- package/dist/server/src/renderComponent.d.ts +8 -9
- package/dist/server/src/renderComponent.js +10 -5
- package/dist/server/src/renderComponent.js.map +1 -1
- package/dist/server/src/server/index.d.ts +23 -34
- package/dist/server/src/server/index.js +170 -50
- package/dist/server/src/server/index.js.map +1 -1
- package/dist/server/src/server/templates.d.ts +2 -0
- package/dist/server/src/server/templates.js +9 -5
- package/dist/server/src/server/templates.js.map +1 -1
- package/dist/server/src/server/utils.d.ts +1 -1
- package/dist/server/src/server/utils.js +1 -1
- package/dist/server/src/server/utils.js.map +1 -1
- package/dist/server/tsconfig.server.tsbuildinfo +1 -1
- package/dist/server-renderer/package.json +1 -1
- package/dist/store/package.json +1 -1
- package/dist/types/package.json +1 -1
- package/dist/utils/package.json +1 -1
- package/dist/vdom/package.json +1 -1
- package/dist/wasm/package.json +1 -1
- package/package.json +1 -1
- package/templates/complete-app/client.js +58 -0
- package/templates/complete-app/package-lock.json +2536 -0
- package/templates/complete-app/package.json +8 -31
- package/templates/complete-app/pages/about.js +119 -0
- package/templates/complete-app/pages/index.js +157 -0
- package/templates/complete-app/pages/wasm-demo.js +290 -0
- package/templates/complete-app/public/client.js +80 -0
- package/templates/complete-app/public/index.html +47 -0
- package/templates/complete-app/public/styles.css +446 -212
- package/templates/complete-app/readme.md +188 -0
- package/templates/complete-app/server.js +417 -0
- package/templates/complete-app/server.ts +275 -0
- package/templates/complete-app/src/App.tsx +59 -0
- package/templates/complete-app/src/client.ts +61 -0
- package/templates/complete-app/src/client.tsx +18 -0
- package/templates/complete-app/src/pages/index.tsx +51 -0
- package/templates/complete-app/src/server.ts +218 -0
- package/templates/complete-app/tsconfig.json +22 -0
- package/templates/complete-app/tsconfig.server.json +19 -0
- package/templates/complete-app/vite.config.js +57 -0
- package/templates/complete-app/vite.config.ts +30 -0
- package/templates/go/example.go +154 -99
- package/templates/complete-app/build.js +0 -284
- package/templates/complete-app/src/api/index.js +0 -31
- package/templates/complete-app/src/client.js +0 -93
- package/templates/complete-app/src/components/App.js +0 -66
- package/templates/complete-app/src/components/Footer.js +0 -19
- package/templates/complete-app/src/components/Header.js +0 -38
- package/templates/complete-app/src/pages/About.js +0 -59
- package/templates/complete-app/src/pages/Home.js +0 -54
- package/templates/complete-app/src/pages/WasmDemo.js +0 -136
- package/templates/complete-app/src/server.js +0 -186
- package/templates/complete-app/src/wasm/build.bat +0 -16
- package/templates/complete-app/src/wasm/build.sh +0 -16
- package/templates/complete-app/src/wasm/example.go +0 -101
@@ -1,40 +1,17 @@
|
|
1
1
|
{
|
2
|
-
"name": "
|
3
|
-
"version": "1.0
|
4
|
-
"description": "Complete Frontend Hamroun App with all features: SSR, WASM, API routes, and more",
|
2
|
+
"name": "ssr-app",
|
3
|
+
"version": "0.1.0",
|
5
4
|
"type": "module",
|
6
5
|
"scripts": {
|
7
|
-
"dev": "
|
8
|
-
"
|
9
|
-
"build
|
10
|
-
"start": "cross-env NODE_ENV=production node dist/server.js",
|
11
|
-
"test": "vitest run",
|
12
|
-
"wasm:build": "cd src/wasm && ./build.sh"
|
6
|
+
"dev": "node server.js",
|
7
|
+
"start": "node server.js",
|
8
|
+
"build": "echo 'No build step required for this template'"
|
13
9
|
},
|
14
10
|
"dependencies": {
|
15
|
-
"compression": "^1.
|
16
|
-
"
|
17
|
-
"dotenv": "^16.3.1",
|
11
|
+
"compression": "^1.8.0",
|
12
|
+
"dotenv": "^16.0.3",
|
18
13
|
"express": "^4.18.2",
|
19
14
|
"frontend-hamroun": "latest",
|
20
|
-
"
|
21
|
-
"socket.io": "^4.7.2"
|
22
|
-
},
|
23
|
-
"devDependencies": {
|
24
|
-
"@types/compression": "^1.7.3",
|
25
|
-
"@types/cors": "^2.8.14",
|
26
|
-
"@types/express": "^4.17.18",
|
27
|
-
"@types/jsonwebtoken": "^9.0.3",
|
28
|
-
"@types/node": "^20.8.2",
|
29
|
-
"autoprefixer": "^10.4.14",
|
30
|
-
"chokidar": "^3.5.3",
|
31
|
-
"cross-env": "^7.0.3",
|
32
|
-
"esbuild": "^0.19.5",
|
33
|
-
"postcss": "^8.4.27",
|
34
|
-
"tailwindcss": "^3.3.3",
|
35
|
-
"vitest": "^0.34.6"
|
36
|
-
},
|
37
|
-
"engines": {
|
38
|
-
"node": ">=16.0.0"
|
15
|
+
"node-fetch": "^3.3.1"
|
39
16
|
}
|
40
17
|
}
|
@@ -0,0 +1,119 @@
|
|
1
|
+
import { jsx, useState, useEffect } from 'frontend-hamroun';
|
2
|
+
|
3
|
+
// Simple animation component to demonstrate useEffect
|
4
|
+
function FadeIn({ children, delay = 0 }) {
|
5
|
+
const [opacity, setOpacity] = useState(0);
|
6
|
+
|
7
|
+
useEffect(() => {
|
8
|
+
const timer = setTimeout(() => {
|
9
|
+
setOpacity(1);
|
10
|
+
}, delay);
|
11
|
+
|
12
|
+
return () => clearTimeout(timer);
|
13
|
+
}, [delay]);
|
14
|
+
|
15
|
+
return jsx('div', {
|
16
|
+
style: `opacity: ${opacity}; transition: opacity 0.5s ease-in-out;`
|
17
|
+
}, children);
|
18
|
+
}
|
19
|
+
|
20
|
+
// Main about page component
|
21
|
+
export default function AboutPage(props) {
|
22
|
+
const [activeSection, setActiveSection] = useState('overview');
|
23
|
+
|
24
|
+
return jsx('div', { className: 'container' }, [
|
25
|
+
jsx('header', { className: 'header' }, [
|
26
|
+
jsx('h1', {}, 'About Frontend Hamroun'),
|
27
|
+
jsx('a', { href: '/', className: 'back-link' }, '← Back to Home')
|
28
|
+
]),
|
29
|
+
|
30
|
+
jsx('main', {}, [
|
31
|
+
jsx('div', { className: 'about-nav' }, [
|
32
|
+
jsx('button', {
|
33
|
+
className: activeSection === 'overview' ? 'active' : '',
|
34
|
+
onClick: () => setActiveSection('overview')
|
35
|
+
}, 'Overview'),
|
36
|
+
jsx('button', {
|
37
|
+
className: activeSection === 'features' ? 'active' : '',
|
38
|
+
onClick: () => setActiveSection('features')
|
39
|
+
}, 'Features'),
|
40
|
+
jsx('button', {
|
41
|
+
className: activeSection === 'team' ? 'active' : '',
|
42
|
+
onClick: () => setActiveSection('team')
|
43
|
+
}, 'Team')
|
44
|
+
]),
|
45
|
+
|
46
|
+
// Overview section
|
47
|
+
activeSection === 'overview' && jsx(FadeIn, {}, [
|
48
|
+
jsx('section', { className: 'about-section' }, [
|
49
|
+
jsx('h2', {}, 'Framework Overview'),
|
50
|
+
jsx('p', {}, 'Frontend Hamroun is a lightweight JavaScript framework for building modern web applications with server-side rendering and client-side hydration.'),
|
51
|
+
jsx('p', {}, 'Inspired by React and other modern frameworks, it provides a simple yet powerful component model with hooks for state management and side effects.'),
|
52
|
+
jsx('p', {}, `This page was rendered on the server at ${props.api?.serverTime} and then hydrated on the client to provide interactivity.`)
|
53
|
+
])
|
54
|
+
]),
|
55
|
+
|
56
|
+
// Features section
|
57
|
+
activeSection === 'features' && jsx(FadeIn, {}, [
|
58
|
+
jsx('section', { className: 'about-section' }, [
|
59
|
+
jsx('h2', {}, 'Key Features'),
|
60
|
+
jsx('ul', { className: 'feature-list expanded' }, [
|
61
|
+
jsx('li', {}, [
|
62
|
+
jsx('strong', {}, 'Server-Side Rendering (SSR):'),
|
63
|
+
jsx('p', {}, 'Render pages on the server for faster initial load and improved SEO.')
|
64
|
+
]),
|
65
|
+
jsx('li', {}, [
|
66
|
+
jsx('strong', {}, 'Client-Side Hydration:'),
|
67
|
+
jsx('p', {}, 'Add interactivity to server-rendered HTML without rebuilding the DOM.')
|
68
|
+
]),
|
69
|
+
jsx('li', {}, [
|
70
|
+
jsx('strong', {}, 'React-like Hooks:'),
|
71
|
+
jsx('p', {}, 'Use useState, useEffect, useMemo, and useRef for managing component state and lifecycle.')
|
72
|
+
]),
|
73
|
+
jsx('li', {}, [
|
74
|
+
jsx('strong', {}, 'Context API:'),
|
75
|
+
jsx('p', {}, 'Share state between components without prop drilling using createContext and useContext.')
|
76
|
+
]),
|
77
|
+
jsx('li', {}, [
|
78
|
+
jsx('strong', {}, 'WebAssembly Integration:'),
|
79
|
+
jsx('p', {}, 'Leverage high-performance Go code compiled to WebAssembly for computationally intensive tasks.')
|
80
|
+
]),
|
81
|
+
jsx('li', {}, [
|
82
|
+
jsx('strong', {}, 'Automatic Route Handling:'),
|
83
|
+
jsx('p', {}, 'File-based routing system similar to Next.js for intuitive page organization.')
|
84
|
+
])
|
85
|
+
])
|
86
|
+
])
|
87
|
+
]),
|
88
|
+
|
89
|
+
// Team section
|
90
|
+
activeSection === 'team' && jsx(FadeIn, {}, [
|
91
|
+
jsx('section', { className: 'about-section' }, [
|
92
|
+
jsx('h2', {}, 'The Team'),
|
93
|
+
jsx('div', { className: 'team-grid' }, [
|
94
|
+
jsx('div', { className: 'team-member' }, [
|
95
|
+
jsx('div', { className: 'avatar' }, 'MH'),
|
96
|
+
jsx('h3', {}, 'Mohamed Hamroun'),
|
97
|
+
jsx('p', { className: 'title' }, 'Lead Framework Developer'),
|
98
|
+
jsx('p', { className: 'bio' }, 'Creator of Frontend Hamroun and full-stack JavaScript enthusiast.')
|
99
|
+
]),
|
100
|
+
jsx('div', { className: 'team-member' }, [
|
101
|
+
jsx('div', { className: 'avatar' }, 'AI'),
|
102
|
+
jsx('h3', {}, 'AI Assistant'),
|
103
|
+
jsx('p', { className: 'title' }, 'Documentation & Support'),
|
104
|
+
jsx('p', { className: 'bio' }, 'Helps with documentation, examples, and framework support.')
|
105
|
+
])
|
106
|
+
])
|
107
|
+
])
|
108
|
+
])
|
109
|
+
]),
|
110
|
+
|
111
|
+
jsx('footer', {}, [
|
112
|
+
jsx('p', {}, '© 2025 Frontend Hamroun Framework')
|
113
|
+
])
|
114
|
+
]);
|
115
|
+
}
|
116
|
+
|
117
|
+
// Static metadata for SEO
|
118
|
+
AboutPage.getTitle = () => 'About - Frontend Hamroun Framework';
|
119
|
+
AboutPage.getDescription = () => 'Learn about the Frontend Hamroun framework, its features, and the team behind it.';
|
@@ -0,0 +1,157 @@
|
|
1
|
+
import { jsx, useState, useEffect, useMemo, useRef, useContext, createContext } from 'frontend-hamroun';
|
2
|
+
|
3
|
+
// Create a context for theme
|
4
|
+
const ThemeContext = createContext({ theme: 'light', toggleTheme: () => {} });
|
5
|
+
|
6
|
+
// Demo counter component using hooks
|
7
|
+
function Counter({ initialCount = 0 }) {
|
8
|
+
const [count, setCount] = useState(initialCount);
|
9
|
+
const countRef = useRef(count);
|
10
|
+
const { theme } = useContext(ThemeContext);
|
11
|
+
|
12
|
+
// Update ref when count changes
|
13
|
+
useEffect(() => {
|
14
|
+
countRef.current = count;
|
15
|
+
console.log('Count updated to:', count);
|
16
|
+
}, [count]);
|
17
|
+
|
18
|
+
// Use memoization for expensive calculation
|
19
|
+
const isEven = useMemo(() => {
|
20
|
+
console.log('Computing isEven...');
|
21
|
+
return count % 2 === 0;
|
22
|
+
}, [count]);
|
23
|
+
|
24
|
+
const themeClass = theme === 'dark' ? 'dark-theme' : 'light-theme';
|
25
|
+
|
26
|
+
return jsx('div', { className: `counter ${themeClass}` }, [
|
27
|
+
jsx('h3', {}, 'Interactive Counter'),
|
28
|
+
jsx('p', {}, [
|
29
|
+
jsx('span', {}, `Current count: ${count} `),
|
30
|
+
jsx('span', { className: 'badge' }, isEven ? 'even' : 'odd')
|
31
|
+
]),
|
32
|
+
jsx('div', { className: 'button-group' }, [
|
33
|
+
jsx('button', {
|
34
|
+
onClick: () => setCount(c => c - 1),
|
35
|
+
className: 'btn'
|
36
|
+
}, 'Decrease'),
|
37
|
+
jsx('button', {
|
38
|
+
onClick: () => setCount(c => c + 1),
|
39
|
+
className: 'btn primary'
|
40
|
+
}, 'Increase'),
|
41
|
+
jsx('button', {
|
42
|
+
onClick: () => setCount(initialCount),
|
43
|
+
className: 'btn secondary'
|
44
|
+
}, 'Reset')
|
45
|
+
])
|
46
|
+
]);
|
47
|
+
}
|
48
|
+
|
49
|
+
// ServerInfo component to show server time
|
50
|
+
function ServerInfo({ serverTime }) {
|
51
|
+
const [clientTime, setClientTime] = useState(new Date().toISOString());
|
52
|
+
const [timeVisible, setTimeVisible] = useState(true);
|
53
|
+
|
54
|
+
// Update client time every second
|
55
|
+
useEffect(() => {
|
56
|
+
if (!timeVisible) return;
|
57
|
+
|
58
|
+
const interval = setInterval(() => {
|
59
|
+
setClientTime(new Date().toISOString());
|
60
|
+
}, 1000);
|
61
|
+
|
62
|
+
return () => clearInterval(interval);
|
63
|
+
}, [timeVisible]);
|
64
|
+
|
65
|
+
return jsx('div', { className: 'server-info card' }, [
|
66
|
+
jsx('h3', {}, 'Server/Client Time'),
|
67
|
+
jsx('button', {
|
68
|
+
onClick: () => setTimeVisible(!timeVisible),
|
69
|
+
className: 'btn small'
|
70
|
+
}, timeVisible ? 'Hide Times' : 'Show Times'),
|
71
|
+
timeVisible && jsx('div', { className: 'time-container' }, [
|
72
|
+
jsx('p', {}, [
|
73
|
+
jsx('strong', {}, 'Server Time: '),
|
74
|
+
jsx('span', {}, serverTime)
|
75
|
+
]),
|
76
|
+
jsx('p', {}, [
|
77
|
+
jsx('strong', {}, 'Client Time: '),
|
78
|
+
jsx('span', {}, clientTime)
|
79
|
+
]),
|
80
|
+
jsx('p', { className: 'note' }, 'The client time updates every second to demonstrate client-side effects.')
|
81
|
+
])
|
82
|
+
]);
|
83
|
+
}
|
84
|
+
|
85
|
+
// Main page component
|
86
|
+
export default function HomePage(props) {
|
87
|
+
const [theme, setTheme] = useState('light');
|
88
|
+
const toggleTheme = () => setTheme(theme === 'light' ? 'dark' : 'light');
|
89
|
+
|
90
|
+
const themeValue = useMemo(() => ({ theme, toggleTheme }), [theme]);
|
91
|
+
|
92
|
+
return jsx(ThemeContext.Provider, { value: themeValue }, [
|
93
|
+
jsx('div', { className: `container ${theme}-theme` }, [
|
94
|
+
jsx('header', { className: 'header' }, [
|
95
|
+
jsx('div', { className: 'logo' }, [
|
96
|
+
jsx('h1', {}, 'Frontend Hamroun'),
|
97
|
+
jsx('span', { className: 'version' }, 'v1.2')
|
98
|
+
]),
|
99
|
+
jsx('button', {
|
100
|
+
onClick: toggleTheme,
|
101
|
+
className: 'theme-toggle'
|
102
|
+
}, theme === 'light' ? '🌙 Dark Mode' : '☀️ Light Mode')
|
103
|
+
]),
|
104
|
+
|
105
|
+
jsx('main', {}, [
|
106
|
+
jsx('section', { className: 'hero' }, [
|
107
|
+
jsx('h2', {}, 'Welcome to the Complete Server-Side Rendered App'),
|
108
|
+
jsx('p', {}, 'This demo showcases the power of Frontend Hamroun framework with server-side rendering and hydration.')
|
109
|
+
]),
|
110
|
+
|
111
|
+
jsx('div', { className: 'grid' }, [
|
112
|
+
jsx('div', { className: 'card' }, [
|
113
|
+
jsx('h3', {}, 'Framework Features'),
|
114
|
+
jsx('ul', { className: 'feature-list' }, [
|
115
|
+
jsx('li', {}, 'Server-Side Rendering'),
|
116
|
+
jsx('li', {}, 'Client-Side Hydration'),
|
117
|
+
jsx('li', {}, 'React-like Hooks (useState, useEffect, useMemo, useRef)'),
|
118
|
+
jsx('li', {}, 'Context API for State Management'),
|
119
|
+
jsx('li', {}, 'WebAssembly Integration'),
|
120
|
+
jsx('li', {}, 'SEO Optimization')
|
121
|
+
])
|
122
|
+
]),
|
123
|
+
|
124
|
+
jsx('div', { className: 'card' }, [
|
125
|
+
jsx(Counter, { initialCount: props.params.count ? parseInt(props.params.count) : 0 })
|
126
|
+
]),
|
127
|
+
|
128
|
+
jsx('div', { className: 'card' }, [
|
129
|
+
jsx(ServerInfo, { serverTime: props.api?.serverTime || 'Not available' })
|
130
|
+
]),
|
131
|
+
|
132
|
+
jsx('div', { className: 'card' }, [
|
133
|
+
jsx('h3', {}, 'Navigation'),
|
134
|
+
jsx('nav', { className: 'nav-links' }, [
|
135
|
+
jsx('a', { href: '/', className: 'active' }, 'Home'),
|
136
|
+
jsx('a', { href: '/about', className: '' }, 'About'),
|
137
|
+
jsx('a', { href: '/users', className: '' }, 'Users'),
|
138
|
+
jsx('a', { href: '/wasm-demo', className: '' }, 'WebAssembly Demo')
|
139
|
+
])
|
140
|
+
])
|
141
|
+
])
|
142
|
+
]),
|
143
|
+
|
144
|
+
jsx('footer', {}, [
|
145
|
+
jsx('p', {}, [
|
146
|
+
'© ',
|
147
|
+
jsx('span', {}, new Date().getFullYear()),
|
148
|
+
' Frontend Hamroun Framework. Built with 💙 using SSR and hydration.'
|
149
|
+
])
|
150
|
+
])
|
151
|
+
])
|
152
|
+
]);
|
153
|
+
}
|
154
|
+
|
155
|
+
// Static metadata for SEO
|
156
|
+
HomePage.getTitle = (props) => 'Frontend Hamroun - Modern JavaScript Framework';
|
157
|
+
HomePage.getDescription = (props) => 'A server-side rendered demo of the Frontend Hamroun framework with React-like hooks and component architecture.';
|
@@ -0,0 +1,290 @@
|
|
1
|
+
import { jsx, useState, useEffect } from 'frontend-hamroun';
|
2
|
+
import { loadGoWasm } from 'frontend-hamroun/wasm';
|
3
|
+
|
4
|
+
export default function WasmDemo(props) {
|
5
|
+
// State for WASM loading
|
6
|
+
const [wasm, setWasm] = useState(null);
|
7
|
+
const [loading, setLoading] = useState(true);
|
8
|
+
const [error, setError] = useState(null);
|
9
|
+
|
10
|
+
// State for math demo
|
11
|
+
const [num1, setNum1] = useState(5);
|
12
|
+
const [num2, setNum2] = useState(3);
|
13
|
+
const [operation, setOperation] = useState('add');
|
14
|
+
const [result, setResult] = useState(null);
|
15
|
+
|
16
|
+
// State for string operations
|
17
|
+
const [inputText, setInputText] = useState('Hello WebAssembly!');
|
18
|
+
const [processedText, setProcessedText] = useState('');
|
19
|
+
|
20
|
+
// State for array processing
|
21
|
+
const [arrayInput, setArrayInput] = useState('apple, banana, orange');
|
22
|
+
const [arrayResult, setArrayResult] = useState('');
|
23
|
+
|
24
|
+
// Load WASM module on client-side only
|
25
|
+
useEffect(() => {
|
26
|
+
async function loadWasmModule() {
|
27
|
+
setLoading(true);
|
28
|
+
try {
|
29
|
+
const wasmModule = await loadGoWasm('/wasm/example.wasm', {
|
30
|
+
debug: true
|
31
|
+
});
|
32
|
+
setWasm(wasmModule);
|
33
|
+
setError(null);
|
34
|
+
console.log('WASM module loaded:', wasmModule);
|
35
|
+
} catch (err) {
|
36
|
+
console.error('Failed to load WASM:', err);
|
37
|
+
setError(`Error loading WASM: ${err.message}`);
|
38
|
+
} finally {
|
39
|
+
setLoading(false);
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
loadWasmModule();
|
44
|
+
}, []);
|
45
|
+
|
46
|
+
// Update result when inputs or operation change
|
47
|
+
useEffect(() => {
|
48
|
+
if (!wasm || !wasm.functions) return;
|
49
|
+
|
50
|
+
try {
|
51
|
+
let calculatedResult;
|
52
|
+
switch (operation) {
|
53
|
+
case 'add':
|
54
|
+
calculatedResult = wasm.functions.goAdd(num1.toString(), num2.toString());
|
55
|
+
break;
|
56
|
+
case 'subtract':
|
57
|
+
calculatedResult = wasm.functions.goSubtract(num1.toString(), num2.toString());
|
58
|
+
break;
|
59
|
+
case 'multiply':
|
60
|
+
calculatedResult = wasm.functions.goMultiply(num1.toString(), num2.toString());
|
61
|
+
break;
|
62
|
+
case 'divide':
|
63
|
+
calculatedResult = wasm.functions.goDivide(num1.toString(), num2.toString());
|
64
|
+
break;
|
65
|
+
default:
|
66
|
+
calculatedResult = 'Unknown operation';
|
67
|
+
}
|
68
|
+
|
69
|
+
setResult(calculatedResult);
|
70
|
+
} catch (err) {
|
71
|
+
setResult(`Error: ${err.message}`);
|
72
|
+
}
|
73
|
+
}, [wasm, num1, num2, operation]);
|
74
|
+
|
75
|
+
// Handle text processing
|
76
|
+
const handleProcessText = () => {
|
77
|
+
if (!wasm || !wasm.functions) return;
|
78
|
+
|
79
|
+
try {
|
80
|
+
// Process the text with the WASM module's goReverseString function
|
81
|
+
const reversed = wasm.functions.goReverseString(inputText);
|
82
|
+
setProcessedText(reversed);
|
83
|
+
} catch (err) {
|
84
|
+
setProcessedText(`Error: ${err.message}`);
|
85
|
+
}
|
86
|
+
};
|
87
|
+
|
88
|
+
// Handle array processing
|
89
|
+
const handleProcessArray = () => {
|
90
|
+
if (!wasm || !wasm.functions) return;
|
91
|
+
|
92
|
+
try {
|
93
|
+
// Process the array with the WASM module's goProcessArray function
|
94
|
+
const processed = wasm.functions.goProcessArray(arrayInput);
|
95
|
+
setArrayResult(processed);
|
96
|
+
} catch (err) {
|
97
|
+
setArrayResult(`Error: ${err.message}`);
|
98
|
+
}
|
99
|
+
};
|
100
|
+
|
101
|
+
// Handle factorial calculation
|
102
|
+
const [factorialInput, setFactorialInput] = useState(5);
|
103
|
+
const [factorialResult, setFactorialResult] = useState(null);
|
104
|
+
|
105
|
+
const calculateFactorial = () => {
|
106
|
+
if (!wasm || !wasm.functions) return;
|
107
|
+
|
108
|
+
try {
|
109
|
+
const result = wasm.functions.goCalculateFactorial(factorialInput.toString());
|
110
|
+
setFactorialResult(result);
|
111
|
+
} catch (err) {
|
112
|
+
setFactorialResult(`Error: ${err.message}`);
|
113
|
+
}
|
114
|
+
};
|
115
|
+
|
116
|
+
if (loading) {
|
117
|
+
return jsx('div', { className: 'container loading-container' }, [
|
118
|
+
jsx('h1', {}, 'WebAssembly Demo'),
|
119
|
+
jsx('div', { className: 'loading' }, [
|
120
|
+
jsx('div', { className: 'spinner' }),
|
121
|
+
jsx('p', {}, 'Loading WebAssembly module...')
|
122
|
+
])
|
123
|
+
]);
|
124
|
+
}
|
125
|
+
|
126
|
+
if (error) {
|
127
|
+
return jsx('div', { className: 'container error-container' }, [
|
128
|
+
jsx('h1', {}, 'WebAssembly Demo'),
|
129
|
+
jsx('div', { className: 'error-box' }, [
|
130
|
+
jsx('h3', {}, 'Error Loading WebAssembly'),
|
131
|
+
jsx('p', {}, error),
|
132
|
+
jsx('p', {}, 'Make sure your browser supports WebAssembly and that the WASM file is correctly built and accessible.')
|
133
|
+
]),
|
134
|
+
jsx('a', { href: '/', className: 'button' }, 'Back to Home')
|
135
|
+
]);
|
136
|
+
}
|
137
|
+
|
138
|
+
return jsx('div', { className: 'container' }, [
|
139
|
+
jsx('header', { className: 'header' }, [
|
140
|
+
jsx('h1', {}, 'WebAssembly Integration Demo'),
|
141
|
+
jsx('a', { href: '/', className: 'back-link' }, '← Back to Home')
|
142
|
+
]),
|
143
|
+
|
144
|
+
jsx('main', { className: 'wasm-demo' }, [
|
145
|
+
jsx('section', { className: 'intro-section' }, [
|
146
|
+
jsx('h2', {}, 'Go + WebAssembly Integration'),
|
147
|
+
jsx('p', {}, 'This page demonstrates how Frontend Hamroun integrates with WebAssembly modules compiled from Go. The following examples show different operations performed by Go code running in your browser.')
|
148
|
+
]),
|
149
|
+
|
150
|
+
jsx('div', { className: 'demo-grid' }, [
|
151
|
+
// Math Operations Demo
|
152
|
+
jsx('section', { className: 'card' }, [
|
153
|
+
jsx('h3', {}, 'Math Operations'),
|
154
|
+
|
155
|
+
jsx('div', { className: 'form-grid' }, [
|
156
|
+
jsx('div', { className: 'form-group' }, [
|
157
|
+
jsx('label', { htmlFor: 'num1' }, 'First Number'),
|
158
|
+
jsx('input', {
|
159
|
+
id: 'num1',
|
160
|
+
type: 'number',
|
161
|
+
value: num1,
|
162
|
+
onChange: (e) => setNum1(parseInt(e.target.value))
|
163
|
+
})
|
164
|
+
]),
|
165
|
+
|
166
|
+
jsx('div', { className: 'form-group' }, [
|
167
|
+
jsx('label', { htmlFor: 'num2' }, 'Second Number'),
|
168
|
+
jsx('input', {
|
169
|
+
id: 'num2',
|
170
|
+
type: 'number',
|
171
|
+
value: num2,
|
172
|
+
onChange: (e) => setNum2(parseInt(e.target.value))
|
173
|
+
})
|
174
|
+
]),
|
175
|
+
|
176
|
+
jsx('div', { className: 'form-group' }, [
|
177
|
+
jsx('label', { htmlFor: 'operation' }, 'Operation'),
|
178
|
+
jsx('select', {
|
179
|
+
id: 'operation',
|
180
|
+
value: operation,
|
181
|
+
onChange: (e) => setOperation(e.target.value)
|
182
|
+
}, [
|
183
|
+
jsx('option', { value: 'add' }, 'Addition'),
|
184
|
+
jsx('option', { value: 'subtract' }, 'Subtraction'),
|
185
|
+
jsx('option', { value: 'multiply' }, 'Multiplication'),
|
186
|
+
jsx('option', { value: 'divide' }, 'Division')
|
187
|
+
])
|
188
|
+
])
|
189
|
+
]),
|
190
|
+
|
191
|
+
jsx('div', { className: 'result-box' }, [
|
192
|
+
jsx('div', { className: 'result-label' }, 'Result:'),
|
193
|
+
jsx('div', { className: 'result-value' }, result !== null ? result : 'Calculating...')
|
194
|
+
])
|
195
|
+
]),
|
196
|
+
|
197
|
+
// String Operations Demo
|
198
|
+
jsx('section', { className: 'card' }, [
|
199
|
+
jsx('h3', {}, 'String Reversal'),
|
200
|
+
|
201
|
+
jsx('div', { className: 'form-group' }, [
|
202
|
+
jsx('label', { htmlFor: 'inputText' }, 'Input Text'),
|
203
|
+
jsx('input', {
|
204
|
+
id: 'inputText',
|
205
|
+
type: 'text',
|
206
|
+
value: inputText,
|
207
|
+
onChange: (e) => setInputText(e.target.value)
|
208
|
+
})
|
209
|
+
]),
|
210
|
+
|
211
|
+
jsx('button', {
|
212
|
+
onClick: handleProcessText,
|
213
|
+
className: 'btn primary'
|
214
|
+
}, 'Reverse Text'),
|
215
|
+
|
216
|
+
jsx('div', { className: 'result-box' }, [
|
217
|
+
jsx('div', { className: 'result-label' }, 'Reversed Text:'),
|
218
|
+
jsx('div', { className: 'result-value' }, processedText || 'Click button to process')
|
219
|
+
])
|
220
|
+
]),
|
221
|
+
|
222
|
+
// Factorial Calculation Demo
|
223
|
+
jsx('section', { className: 'card' }, [
|
224
|
+
jsx('h3', {}, 'Factorial Calculation'),
|
225
|
+
|
226
|
+
jsx('div', { className: 'form-group' }, [
|
227
|
+
jsx('label', { htmlFor: 'factorialInput' }, 'Number (0-20)'),
|
228
|
+
jsx('input', {
|
229
|
+
id: 'factorialInput',
|
230
|
+
type: 'number',
|
231
|
+
min: '0',
|
232
|
+
max: '20',
|
233
|
+
value: factorialInput,
|
234
|
+
onChange: (e) => setFactorialInput(parseInt(e.target.value))
|
235
|
+
})
|
236
|
+
]),
|
237
|
+
|
238
|
+
jsx('button', {
|
239
|
+
onClick: calculateFactorial,
|
240
|
+
className: 'btn primary'
|
241
|
+
}, 'Calculate Factorial'),
|
242
|
+
|
243
|
+
jsx('div', { className: 'result-box' }, [
|
244
|
+
jsx('div', { className: 'result-label' }, `Factorial of ${factorialInput}:`),
|
245
|
+
jsx('div', { className: 'result-value' }, factorialResult || 'Click button to calculate')
|
246
|
+
])
|
247
|
+
]),
|
248
|
+
|
249
|
+
// Array Processing Demo
|
250
|
+
jsx('section', { className: 'card' }, [
|
251
|
+
jsx('h3', {}, 'Array Processing'),
|
252
|
+
|
253
|
+
jsx('div', { className: 'form-group' }, [
|
254
|
+
jsx('label', { htmlFor: 'arrayInput' }, 'Comma-separated values'),
|
255
|
+
jsx('input', {
|
256
|
+
id: 'arrayInput',
|
257
|
+
type: 'text',
|
258
|
+
value: arrayInput,
|
259
|
+
onChange: (e) => setArrayInput(e.target.value)
|
260
|
+
})
|
261
|
+
]),
|
262
|
+
|
263
|
+
jsx('button', {
|
264
|
+
onClick: handleProcessArray,
|
265
|
+
className: 'btn primary'
|
266
|
+
}, 'Process Array'),
|
267
|
+
|
268
|
+
jsx('div', { className: 'result-box' }, [
|
269
|
+
jsx('div', { className: 'result-label' }, 'Processed Array (uppercase):'),
|
270
|
+
jsx('div', { className: 'result-value array-result' }, arrayResult || 'Click button to process')
|
271
|
+
])
|
272
|
+
])
|
273
|
+
]),
|
274
|
+
|
275
|
+
jsx('section', { className: 'info-section' }, [
|
276
|
+
jsx('h3', {}, 'How It Works'),
|
277
|
+
jsx('p', {}, 'The computations on this page are performed by Go code compiled to WebAssembly. The WebAssembly binary is loaded by Frontend Hamroun\'s WASM utility and executed in your browser.'),
|
278
|
+
jsx('p', {}, 'This demonstrates how you can leverage the performance and type safety of Go while maintaining the interactivity of a JavaScript application.')
|
279
|
+
])
|
280
|
+
]),
|
281
|
+
|
282
|
+
jsx('footer', {}, [
|
283
|
+
jsx('p', {}, '© 2025 Frontend Hamroun Framework - WebAssembly Demo')
|
284
|
+
])
|
285
|
+
]);
|
286
|
+
}
|
287
|
+
|
288
|
+
// Static metadata for SEO
|
289
|
+
WasmDemo.getTitle = () => 'WebAssembly Demo - Frontend Hamroun';
|
290
|
+
WasmDemo.getDescription = () => 'Interactive demonstration of WebAssembly integration with Go in the Frontend Hamroun framework.';
|