frontend-hamroun 1.2.83 → 1.2.85

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (245) hide show
  1. package/bin/cli.js +57 -869
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.client.cjs +1 -1
  5. package/dist/index.client.cjs.map +1 -1
  6. package/dist/index.client.js +2 -2
  7. package/dist/index.client.js.map +1 -1
  8. package/dist/index.js +116 -136
  9. package/dist/index.js.map +1 -1
  10. package/dist/jsx-runtime.cjs.map +1 -1
  11. package/dist/jsx-runtime.js.map +1 -1
  12. package/dist/renderer-DaVfBeVi.cjs +2 -0
  13. package/dist/renderer-DaVfBeVi.cjs.map +1 -0
  14. package/dist/renderer-nfT7XSpo.js +61 -0
  15. package/dist/renderer-nfT7XSpo.js.map +1 -0
  16. package/dist/server-renderer-B5b0Q0ck.cjs +2 -0
  17. package/dist/server-renderer-B5b0Q0ck.cjs.map +1 -0
  18. package/dist/{server-renderer-C1WXH-zV.js → server-renderer-C4MB-jAp.js} +6 -39
  19. package/dist/server-renderer-C4MB-jAp.js.map +1 -0
  20. package/dist/server-renderer.cjs +1 -1
  21. package/dist/server-renderer.js +1 -1
  22. package/package.json +1 -1
  23. package/templates/basic-app/build.d.ts +2 -0
  24. package/templates/basic-app/build.d.ts.map +1 -0
  25. package/templates/basic-app/dev.d.ts +2 -0
  26. package/templates/basic-app/dev.d.ts.map +1 -0
  27. package/templates/basic-app/esbuild.config.d.ts +2 -0
  28. package/templates/basic-app/esbuild.config.d.ts.map +1 -0
  29. package/templates/basic-app/postcss.config.d.ts +8 -0
  30. package/templates/basic-app/postcss.config.d.ts.map +1 -0
  31. package/templates/basic-app/server.d.ts +2 -0
  32. package/templates/basic-app/server.d.ts.map +1 -0
  33. package/templates/basic-app/src/App.d.ts +2 -0
  34. package/templates/basic-app/src/App.d.ts.map +1 -0
  35. package/templates/basic-app/src/App.js +148 -0
  36. package/templates/basic-app/src/App.tsx +397 -19
  37. package/templates/basic-app/src/client.d.ts +2 -0
  38. package/templates/basic-app/src/client.d.ts.map +1 -0
  39. package/templates/basic-app/src/client.js +6 -0
  40. package/templates/basic-app/src/components/Counter.d.ts +4 -0
  41. package/templates/basic-app/src/components/Counter.d.ts.map +1 -0
  42. package/templates/basic-app/src/components/Counter.js +9 -0
  43. package/templates/basic-app/src/jsx-shim.d.ts +8 -0
  44. package/templates/basic-app/src/jsx-shim.d.ts.map +1 -0
  45. package/templates/basic-app/src/main.d.ts +2 -0
  46. package/templates/basic-app/src/main.d.ts.map +1 -0
  47. package/templates/basic-app/src/main.js +57 -0
  48. package/templates/basic-app/src/server.d.ts +2 -0
  49. package/templates/basic-app/src/server.d.ts.map +1 -0
  50. package/templates/basic-app/tailwind.config.d.ts +9 -0
  51. package/templates/basic-app/tailwind.config.d.ts.map +1 -0
  52. package/templates/basic-app/vite.config.d.ts +3 -0
  53. package/templates/basic-app/vite.config.d.ts.map +1 -0
  54. package/templates/basic-app/vite.config.js +7 -0
  55. package/templates/complete-app/api/hello.d.ts +1 -0
  56. package/templates/complete-app/api/hello.d.ts.map +1 -0
  57. package/templates/complete-app/client.d.ts +2 -0
  58. package/templates/complete-app/client.d.ts.map +1 -0
  59. package/templates/complete-app/lib/frontend-hamroun.d.ts +18 -0
  60. package/templates/complete-app/lib/frontend-hamroun.d.ts.map +1 -0
  61. package/templates/complete-app/pages/about.d.ts +7 -0
  62. package/templates/complete-app/pages/about.d.ts.map +1 -0
  63. package/templates/complete-app/pages/index.d.ts +7 -0
  64. package/templates/complete-app/pages/index.d.ts.map +1 -0
  65. package/templates/complete-app/pages/wasm-demo.d.ts +7 -0
  66. package/templates/complete-app/pages/wasm-demo.d.ts.map +1 -0
  67. package/templates/complete-app/public/client.d.ts +17 -0
  68. package/templates/complete-app/public/client.d.ts.map +1 -0
  69. package/templates/complete-app/server.d.ts +2 -0
  70. package/templates/complete-app/server.d.ts.map +1 -0
  71. package/templates/complete-app/server.js +236 -218
  72. package/templates/complete-app/src/App.d.ts +2 -0
  73. package/templates/complete-app/src/App.d.ts.map +1 -0
  74. package/templates/complete-app/src/App.js +27 -0
  75. package/templates/complete-app/src/client.d.ts +2 -0
  76. package/templates/complete-app/src/client.d.ts.map +1 -0
  77. package/templates/complete-app/src/client.js +52 -0
  78. package/templates/complete-app/src/pages/index.d.ts +2 -0
  79. package/templates/complete-app/src/pages/index.d.ts.map +1 -0
  80. package/templates/complete-app/src/pages/index.js +19 -0
  81. package/templates/complete-app/src/server.d.ts +2 -0
  82. package/templates/complete-app/src/server.d.ts.map +1 -0
  83. package/templates/complete-app/src/server.js +192 -0
  84. package/templates/complete-app/vite.config.d.ts +3 -0
  85. package/templates/complete-app/vite.config.d.ts.map +1 -0
  86. package/templates/complete-app/vite.config.js +29 -57
  87. package/templates/fullstack-app/api/hello.d.ts +4 -0
  88. package/templates/fullstack-app/api/hello.d.ts.map +1 -0
  89. package/templates/fullstack-app/api/hello.js +14 -11
  90. package/templates/fullstack-app/api/users/[id].d.ts +5 -0
  91. package/templates/fullstack-app/api/users/[id].d.ts.map +1 -0
  92. package/templates/fullstack-app/api/users/[id].js +52 -0
  93. package/templates/fullstack-app/api/users/index.d.ts +4 -0
  94. package/templates/fullstack-app/api/users/index.d.ts.map +1 -0
  95. package/templates/fullstack-app/api/users/index.js +25 -0
  96. package/templates/fullstack-app/build/main.d.ts +211 -0
  97. package/templates/fullstack-app/build/main.d.ts.map +1 -0
  98. package/templates/fullstack-app/build.d.ts +2 -0
  99. package/templates/fullstack-app/build.d.ts.map +1 -0
  100. package/templates/fullstack-app/build.js +190 -0
  101. package/templates/fullstack-app/postcss.config.d.ts +5 -0
  102. package/templates/fullstack-app/postcss.config.d.ts.map +1 -0
  103. package/templates/fullstack-app/process-tailwind.d.ts +2 -0
  104. package/templates/fullstack-app/process-tailwind.d.ts.map +1 -0
  105. package/templates/fullstack-app/public/route-handler.d.ts +1 -0
  106. package/templates/fullstack-app/public/route-handler.d.ts.map +1 -0
  107. package/templates/fullstack-app/server.d.ts +2 -0
  108. package/templates/fullstack-app/server.d.ts.map +1 -0
  109. package/templates/fullstack-app/server.js +428 -266
  110. package/templates/fullstack-app/src/client.d.ts +2 -0
  111. package/templates/fullstack-app/src/client.d.ts.map +1 -0
  112. package/templates/fullstack-app/src/components/ClientHome.d.ts +1 -0
  113. package/templates/fullstack-app/src/components/ClientHome.d.ts.map +1 -0
  114. package/templates/fullstack-app/src/components/ClientHome.js +1 -0
  115. package/templates/fullstack-app/src/components/ErrorBoundary.d.ts +7 -0
  116. package/templates/fullstack-app/src/components/ErrorBoundary.d.ts.map +1 -0
  117. package/templates/fullstack-app/src/components/ErrorBoundary.js +12 -0
  118. package/templates/fullstack-app/src/components/Layout.d.ts +7 -0
  119. package/templates/fullstack-app/src/components/Layout.d.ts.map +1 -0
  120. package/templates/fullstack-app/src/components/Layout.js +4 -0
  121. package/templates/fullstack-app/src/components/StateDemo.d.ts +2 -0
  122. package/templates/fullstack-app/src/components/StateDemo.d.ts.map +1 -0
  123. package/templates/fullstack-app/src/components/StateDemo.js +86 -0
  124. package/templates/fullstack-app/src/components/UserList.d.ts +11 -0
  125. package/templates/fullstack-app/src/components/UserList.d.ts.map +1 -0
  126. package/templates/fullstack-app/src/components/UserList.js +7 -0
  127. package/templates/fullstack-app/src/config.d.ts +29 -0
  128. package/templates/fullstack-app/src/config.d.ts.map +1 -0
  129. package/templates/fullstack-app/src/config.js +36 -0
  130. package/templates/fullstack-app/src/data/api.d.ts +35 -0
  131. package/templates/fullstack-app/src/data/api.d.ts.map +1 -0
  132. package/templates/fullstack-app/src/data/api.js +173 -0
  133. package/templates/fullstack-app/src/main.d.ts +7 -0
  134. package/templates/fullstack-app/src/main.d.ts.map +1 -0
  135. package/templates/fullstack-app/src/main.js +130 -0
  136. package/templates/fullstack-app/src/middleware.d.ts +10 -0
  137. package/templates/fullstack-app/src/middleware.d.ts.map +1 -0
  138. package/templates/fullstack-app/src/middleware.js +14 -0
  139. package/templates/fullstack-app/src/pages/404.d.ts +4 -0
  140. package/templates/fullstack-app/src/pages/404.d.ts.map +1 -0
  141. package/templates/fullstack-app/src/pages/404.js +4 -0
  142. package/templates/fullstack-app/src/pages/[id].d.ts +1 -0
  143. package/templates/fullstack-app/src/pages/[id].d.ts.map +1 -0
  144. package/templates/fullstack-app/src/pages/[id].js +1 -0
  145. package/templates/fullstack-app/src/pages/_app.d.ts +6 -0
  146. package/templates/fullstack-app/src/pages/_app.d.ts.map +1 -0
  147. package/templates/fullstack-app/src/pages/_app.js +6 -0
  148. package/templates/fullstack-app/src/pages/_document.d.ts +7 -0
  149. package/templates/fullstack-app/src/pages/_document.d.ts.map +1 -0
  150. package/templates/fullstack-app/src/pages/_document.js +4 -0
  151. package/templates/fullstack-app/src/pages/_error.d.ts +4 -0
  152. package/templates/fullstack-app/src/pages/_error.d.ts.map +1 -0
  153. package/templates/fullstack-app/src/pages/_error.js +8 -0
  154. package/templates/fullstack-app/src/pages/about/index.d.ts +5 -0
  155. package/templates/fullstack-app/src/pages/about/index.d.ts.map +1 -0
  156. package/templates/fullstack-app/src/pages/about/index.js +6 -0
  157. package/templates/fullstack-app/src/pages/about.d.ts +10 -0
  158. package/templates/fullstack-app/src/pages/about.d.ts.map +1 -0
  159. package/templates/fullstack-app/src/pages/about.js +21 -0
  160. package/templates/fullstack-app/src/pages/api/users/[id].d.ts +6 -0
  161. package/templates/fullstack-app/src/pages/api/users/[id].d.ts.map +1 -0
  162. package/templates/fullstack-app/src/pages/api/users/[id].js +51 -0
  163. package/templates/fullstack-app/src/pages/api/users/index.d.ts +4 -0
  164. package/templates/fullstack-app/src/pages/api/users/index.d.ts.map +1 -0
  165. package/templates/fullstack-app/src/pages/api/users/index.js +33 -0
  166. package/templates/fullstack-app/src/pages/index.d.ts +21 -0
  167. package/templates/fullstack-app/src/pages/index.d.ts.map +1 -0
  168. package/templates/fullstack-app/src/pages/index.js +66 -0
  169. package/templates/fullstack-app/src/pages/users/[id].d.ts +38 -0
  170. package/templates/fullstack-app/src/pages/users/[id].d.ts.map +1 -0
  171. package/templates/fullstack-app/src/pages/users/[id].js +79 -0
  172. package/templates/fullstack-app/src/pages/users.d.ts +14 -0
  173. package/templates/fullstack-app/src/pages/users.d.ts.map +1 -0
  174. package/templates/fullstack-app/src/pages/users.js +14 -0
  175. package/templates/fullstack-app/src/pages/wasm-demo.d.ts +1 -0
  176. package/templates/fullstack-app/src/pages/wasm-demo.d.ts.map +1 -0
  177. package/templates/fullstack-app/src/pages/wasm-demo.js +2 -0
  178. package/templates/fullstack-app/src/router.d.ts +22 -0
  179. package/templates/fullstack-app/src/router.d.ts.map +1 -0
  180. package/templates/fullstack-app/src/router.js +210 -0
  181. package/templates/fullstack-app/tailwind.config.d.ts +3 -0
  182. package/templates/fullstack-app/tailwind.config.d.ts.map +1 -0
  183. package/templates/fullstack-app/vite.config.d.ts +3 -0
  184. package/templates/fullstack-app/vite.config.d.ts.map +1 -0
  185. package/templates/ssr-template/dist/client/assets/main-D-VH3xOb.d.ts +2 -0
  186. package/templates/ssr-template/dist/client/assets/main-D-VH3xOb.d.ts.map +1 -0
  187. package/templates/ssr-template/dist/client.d.ts +85 -0
  188. package/templates/ssr-template/dist/client.d.ts.map +1 -0
  189. package/templates/ssr-template/dist/server.d.ts +2 -0
  190. package/templates/ssr-template/dist/server.d.ts.map +1 -0
  191. package/templates/ssr-template/esbuild.config.d.ts +2 -0
  192. package/templates/ssr-template/esbuild.config.d.ts.map +1 -0
  193. package/templates/ssr-template/jsx-shim.d.ts +2 -0
  194. package/templates/ssr-template/jsx-shim.d.ts.map +1 -0
  195. package/templates/ssr-template/src/App.d.ts +2 -0
  196. package/templates/ssr-template/src/App.d.ts.map +1 -0
  197. package/templates/ssr-template/src/App.js +625 -0
  198. package/templates/ssr-template/src/App.tsx +43 -18
  199. package/templates/ssr-template/src/client.d.ts +2 -0
  200. package/templates/ssr-template/src/client.d.ts.map +1 -0
  201. package/templates/ssr-template/src/client.js +3 -0
  202. package/templates/ssr-template/src/server.d.ts +2 -0
  203. package/templates/ssr-template/src/server.d.ts.map +1 -0
  204. package/templates/ssr-template/src/server.js +29 -0
  205. package/templates/ssr-template/vite.config.d.ts +3 -0
  206. package/templates/ssr-template/vite.config.d.ts.map +1 -0
  207. package/templates/ssr-template/vite.config.js +30 -0
  208. package/templates/wasm/build-wasm.d.ts +2 -0
  209. package/templates/wasm/build-wasm.d.ts.map +1 -0
  210. package/templates/wasm/dist/assets/index-BNqTDBdE.d.ts +30 -0
  211. package/templates/wasm/dist/assets/index-BNqTDBdE.d.ts.map +1 -0
  212. package/templates/wasm/dist/wasm_exec.d.ts +1 -0
  213. package/templates/wasm/dist/wasm_exec.d.ts.map +1 -0
  214. package/templates/wasm/esbuild.config.d.ts +2 -0
  215. package/templates/wasm/esbuild.config.d.ts.map +1 -0
  216. package/templates/wasm/go/wasm_exec.d.ts +1 -0
  217. package/templates/wasm/go/wasm_exec.d.ts.map +1 -0
  218. package/templates/wasm/jsx-shim.d.ts +5 -0
  219. package/templates/wasm/jsx-shim.d.ts.map +1 -0
  220. package/templates/wasm/public/wasm_exec.d.ts +1 -0
  221. package/templates/wasm/public/wasm_exec.d.ts.map +1 -0
  222. package/templates/wasm/src/App.d.ts +2 -0
  223. package/templates/wasm/src/App.d.ts.map +1 -0
  224. package/templates/wasm/src/App.js +381 -0
  225. package/templates/wasm/src/client.d.ts +2 -0
  226. package/templates/wasm/src/client.d.ts.map +1 -0
  227. package/templates/wasm/src/client.js +210 -0
  228. package/templates/wasm/src/index.d.ts +2 -0
  229. package/templates/wasm/src/index.d.ts.map +1 -0
  230. package/templates/wasm/src/index.js +20 -0
  231. package/templates/wasm/src/server.d.ts +2 -0
  232. package/templates/wasm/src/server.d.ts.map +1 -0
  233. package/templates/wasm/src/server.js +131 -0
  234. package/templates/wasm/vite.config.d.ts +3 -0
  235. package/templates/wasm/vite.config.d.ts.map +1 -0
  236. package/templates/wasm/vite.config.js +36 -0
  237. package/templates/wasm/wasm-loader.d.ts +6 -0
  238. package/templates/wasm/wasm-loader.d.ts.map +1 -0
  239. package/dist/renderer-BL3gq8cW.cjs +0 -2
  240. package/dist/renderer-BL3gq8cW.cjs.map +0 -1
  241. package/dist/renderer-Dyy-o05F.js +0 -52
  242. package/dist/renderer-Dyy-o05F.js.map +0 -1
  243. package/dist/server-renderer-C1WXH-zV.js.map +0 -1
  244. package/dist/server-renderer-Chs-nmJm.cjs +0 -2
  245. package/dist/server-renderer-Chs-nmJm.cjs.map +0 -1
@@ -1,26 +1,404 @@
1
- import { useState } from 'frontend-hamroun';
1
+ import {
2
+ jsx,
3
+ useState,
4
+ useEffect,
5
+ useMemo,
6
+ useErrorBoundary,
7
+ useForm
8
+ } from 'frontend-hamroun';
2
9
 
3
- export function App() {
4
- // Initialize with a default state that works on both server and client
5
- const [count, setCount] = useState(0);
6
-
10
+ // Todo item interface
11
+ interface Todo {
12
+ id: number;
13
+ text: string;
14
+ completed: boolean;
15
+ priority: 'high' | 'medium' | 'low';
16
+ }
17
+
18
+ // Component prop interfaces
19
+ interface TodoItemProps {
20
+ todo: Todo;
21
+ onToggle: (id: number) => void;
22
+ onDelete: (id: number) => void;
23
+ }
24
+
25
+ interface ThemeToggleButtonProps {
26
+ theme: string;
27
+ onToggle: () => void;
28
+ }
29
+
30
+ interface AppFooterProps {
31
+ theme: string;
32
+ }
33
+
34
+ // Todo Item Component
35
+ function TodoItem({ todo, onToggle, onDelete }: TodoItemProps) {
7
36
  return (
8
- <div>
9
- <h1>Server-Side Rendered App</h1>
10
- <div>
37
+ <div className={`todo-item ${todo.completed ? 'completed' : ''}`}>
38
+ <input
39
+ type="checkbox"
40
+ checked={todo.completed}
41
+ onChange={() => onToggle(todo.id)}
42
+ className="todo-checkbox"
43
+ />
44
+ <span className="todo-text">{todo.text}</span>
45
+ <span className={`todo-priority priority-${todo.priority}`}>
46
+ {todo.priority === 'high' && '🔴'}
47
+ {todo.priority === 'medium' && '🟡'}
48
+ {todo.priority === 'low' && '🟢'}
49
+ {todo.priority}
50
+ </span>
51
+ <div className="todo-actions">
11
52
  <button
12
- onClick={() => setCount(count - 1)}
13
- data-action="decrement"
14
- >-</button>
15
- <span style={{ margin: '0 10px' }}>{count}</span>
16
- <button
17
- onClick={() => setCount(count + 1)}
18
- data-action="increment"
19
- >+</button>
53
+ onClick={() => onDelete(todo.id)}
54
+ className="btn btn-sm btn-danger"
55
+ title="Delete todo"
56
+ >
57
+ 🗑️
58
+ </button>
59
+ </div>
60
+ </div>
61
+ );
62
+ }
63
+
64
+ // Theme Toggle Button Component
65
+ function ThemeToggleButton({ theme, onToggle }: ThemeToggleButtonProps) {
66
+ return (
67
+ <button
68
+ onClick={onToggle}
69
+ className="btn btn-secondary theme-toggle"
70
+ title="Toggle theme"
71
+ >
72
+ {theme === 'light' ? '🌙' : '☀️'}
73
+ </button>
74
+ );
75
+ }
76
+
77
+ // App Footer Component
78
+ function AppFooter({ theme }: AppFooterProps) {
79
+ return (
80
+ <footer className="app-footer">
81
+ <div className="container">
82
+ <p>Built with Frontend Hamroun • Hooks: useState, useEffect, useMemo, useErrorBoundary, useForm</p>
83
+ <p>Theme: <strong>{theme}</strong> • Environment: <strong>Client</strong></p>
84
+ </div>
85
+ </footer>
86
+ );
87
+ }
88
+
89
+ export function App() {
90
+ // State hooks with proper typing
91
+ const [todos, setTodos] = useState<Todo[]>([
92
+ { id: 1, text: 'Learn Frontend Hamroun hooks', completed: false, priority: 'high' },
93
+ { id: 2, text: 'Build a todo app', completed: false, priority: 'medium' },
94
+ { id: 3, text: 'Master client-side concepts', completed: true, priority: 'low' }
95
+ ]);
96
+ const [theme, setTheme] = useState<string>('light');
97
+ const [taskFilter, setTaskFilter] = useState<string>('all');
98
+
99
+ // Error boundary hook
100
+ const [error, resetError] = useErrorBoundary();
101
+
102
+ // Form hook for adding new todos
103
+ const addTodoForm = useForm({
104
+ initialValues: {
105
+ text: '',
106
+ priority: 'medium' as 'high' | 'medium' | 'low'
107
+ },
108
+ validate: (values) => {
109
+ const errors: Record<string, string> = {};
110
+ if (!values.text.trim()) {
111
+ errors.text = 'Todo text is required';
112
+ }
113
+ return errors;
114
+ },
115
+ onSubmit: (values) => {
116
+ const newTodo: Todo = {
117
+ id: Date.now(),
118
+ text: values.text.trim(),
119
+ completed: false,
120
+ priority: values.priority
121
+ };
122
+ setTodos(prev => [...prev, newTodo]);
123
+ addTodoForm.resetForm();
124
+ }
125
+ });
126
+
127
+ // Mount effect
128
+ useEffect(() => {
129
+ console.log('Todo App mounted');
130
+
131
+ // Load saved todos from localStorage
132
+ const savedTodos = localStorage.getItem('todos');
133
+ const savedTheme = localStorage.getItem('theme');
134
+
135
+ if (savedTodos) {
136
+ try {
137
+ setTodos(JSON.parse(savedTodos));
138
+ } catch (e) {
139
+ console.error('Failed to load saved todos');
140
+ }
141
+ }
142
+
143
+ if (savedTheme) {
144
+ setTheme(savedTheme);
145
+ }
146
+
147
+ return () => {
148
+ console.log('Todo App unmounting');
149
+ };
150
+ }, []);
151
+
152
+ // Theme effect
153
+ useEffect(() => {
154
+ document.body.className = `theme-${theme}`;
155
+ document.documentElement.setAttribute('data-theme', theme);
156
+ localStorage.setItem('theme', theme);
157
+ }, [theme]);
158
+
159
+ // Save todos effect
160
+ useEffect(() => {
161
+ if (todos.length === 0) return;
162
+ localStorage.setItem('todos', JSON.stringify(todos));
163
+ }, [todos]);
164
+
165
+ // Memoized filtered todos
166
+ const filteredTodos = useMemo(() => {
167
+ console.log('Filtering todos - memoized computation');
168
+ return todos.filter(todo => {
169
+ if (taskFilter === 'completed') return todo.completed;
170
+ if (taskFilter === 'active') return !todo.completed;
171
+ if (taskFilter === 'high') return todo.priority === 'high';
172
+ if (taskFilter === 'medium') return todo.priority === 'medium';
173
+ if (taskFilter === 'low') return todo.priority === 'low';
174
+ return true;
175
+ });
176
+ }, [todos, taskFilter]);
177
+
178
+ // Memoized todo stats
179
+ const todoStats = useMemo(() => {
180
+ const total = todos.length;
181
+ const completed = todos.filter(t => t.completed).length;
182
+ const active = total - completed;
183
+ const highPriority = todos.filter(t => t.priority === 'high' && !t.completed).length;
184
+
185
+ return { total, completed, active, highPriority };
186
+ }, [todos]);
187
+
188
+ // Todo action handlers
189
+ const handleToggleTodo = (id: number) => {
190
+ console.log('Toggling todo:', id);
191
+ setTodos(prev => prev.map(todo =>
192
+ todo.id === id ? { ...todo, completed: !todo.completed } : todo
193
+ ));
194
+ };
195
+
196
+ const handleDeleteTodo = (id: number) => {
197
+ console.log('Deleting todo:', id);
198
+ setTodos(prev => prev.filter(todo => todo.id !== id));
199
+ };
200
+
201
+ // Event handlers
202
+ const clearCompleted = () => {
203
+ setTodos(prev => prev.filter(todo => !todo.completed));
204
+ };
205
+
206
+ const markAllComplete = () => {
207
+ setTodos(prev => prev.map(todo => ({ ...todo, completed: true })));
208
+ };
209
+
210
+ const toggleTheme = () => {
211
+ setTheme(prev => prev === 'light' ? 'dark' : 'light');
212
+ };
213
+
214
+ const simulateError = () => {
215
+ throw new Error('Simulated error for testing error boundary');
216
+ };
217
+
218
+ if (error) {
219
+ return (
220
+ <div className="error-container">
221
+ <h2>Something went wrong!</h2>
222
+ <p>{(error as Error).message}</p>
223
+ <button onClick={resetError} className="btn btn-primary">
224
+ Try Again
225
+ </button>
20
226
  </div>
21
- <script dangerouslySetInnerHTML={{
22
- __html: `window.__INITIAL_STATE__ = ${JSON.stringify({ count: 0 })};`
23
- }} />
227
+ );
228
+ }
229
+
230
+ return (
231
+ <div className={`app theme-${theme}`}>
232
+
233
+ {/* Header */}
234
+ <header className="app-header">
235
+ <div className="container">
236
+ <h1 className="app-title">
237
+ 📝 Todo App
238
+ <span className="subtitle">Built with Frontend Hamroun</span>
239
+ </h1>
240
+
241
+ <div className="header-controls">
242
+ <ThemeToggleButton theme={theme} onToggle={toggleTheme} />
243
+ <div className="stats">
244
+ <span className="stat">
245
+ Total: <strong>{todoStats.total}</strong>
246
+ </span>
247
+ <span className="stat">
248
+ Active: <strong>{todoStats.active}</strong>
249
+ </span>
250
+ <span className="stat">
251
+ Done: <strong>{todoStats.completed}</strong>
252
+ </span>
253
+ </div>
254
+ </div>
255
+ </div>
256
+ </header>
257
+
258
+ <main className="main-content">
259
+ <div className="container">
260
+
261
+ {/* Add Todo Section */}
262
+ <section className="card add-todo-section">
263
+ <h2>➕ Add New Todo</h2>
264
+
265
+ <form onSubmit={addTodoForm.handleSubmit} className="add-todo-form">
266
+ <input
267
+ type="text"
268
+ name="text"
269
+ value={addTodoForm.values.text}
270
+ onChange={addTodoForm.handleChange}
271
+ onBlur={addTodoForm.handleBlur}
272
+ placeholder="What needs to be done?"
273
+ className={`input todo-input ${addTodoForm.errors.text && addTodoForm.touched.text ? 'error' : ''}`}
274
+ />
275
+
276
+ <select
277
+ name="priority"
278
+ value={addTodoForm.values.priority}
279
+ onChange={addTodoForm.handleChange}
280
+ className="select priority-select"
281
+ >
282
+ <option value="low">🟢 Low Priority</option>
283
+ <option value="medium">🟡 Medium Priority</option>
284
+ <option value="high">🔴 High Priority</option>
285
+ </select>
286
+
287
+ <button
288
+ type="submit"
289
+ className={`btn btn-primary add-btn ${addTodoForm.isSubmitting ? 'loading' : ''}`}
290
+ disabled={addTodoForm.isSubmitting || !addTodoForm.values.text.trim()}
291
+ >
292
+ {addTodoForm.isSubmitting ? '⏳ Adding...' : '➕ Add Todo'}
293
+ </button>
294
+ </form>
295
+
296
+ {addTodoForm.errors.text && addTodoForm.touched.text && (
297
+ <div className="error-message">{addTodoForm.errors.text}</div>
298
+ )}
299
+ </section>
300
+
301
+ {/* Filters Section */}
302
+ <section className="card filters-section">
303
+ <h2>🔍 Filter Todos</h2>
304
+
305
+ <div className="filters">
306
+ {(['all', 'active', 'completed', 'high', 'medium', 'low'] as const).map(filterType => (
307
+ <button
308
+ key={filterType}
309
+ onClick={() => setTaskFilter(filterType)}
310
+ className={`btn btn-sm filter-btn ${taskFilter === filterType ? 'btn-primary' : 'btn-outline'}`}
311
+ >
312
+ {filterType === 'all' && '📋 All'}
313
+ {filterType === 'active' && '⏳ Active'}
314
+ {filterType === 'completed' && '✅ Completed'}
315
+ {filterType === 'high' && '🔴 High Priority'}
316
+ {filterType === 'medium' && '🟡 Medium Priority'}
317
+ {filterType === 'low' && '🟢 Low Priority'}
318
+ </button>
319
+ ))}
320
+ </div>
321
+
322
+ <div className="bulk-actions">
323
+ <button
324
+ onClick={markAllComplete}
325
+ className="btn btn-success btn-sm"
326
+ disabled={todoStats.active === 0}
327
+ >
328
+ ✅ Mark All Complete
329
+ </button>
330
+ <button
331
+ onClick={clearCompleted}
332
+ className="btn btn-warning btn-sm"
333
+ disabled={todoStats.completed === 0}
334
+ >
335
+ 🗑️ Clear Completed
336
+ </button>
337
+ </div>
338
+ </section>
339
+
340
+ {/* Todos List Section */}
341
+ <section className="card todos-section">
342
+ <div className="section-header">
343
+ <h2>📋 Todo List</h2>
344
+ <div className="filter-info">
345
+ Showing <strong>{filteredTodos.length}</strong> of <strong>{todoStats.total}</strong> todos
346
+ {taskFilter !== 'all' && <span className="filter-badge">{taskFilter}</span>}
347
+ </div>
348
+ </div>
349
+
350
+ <div className="todos-list">
351
+ {filteredTodos.length > 0 ? (
352
+ filteredTodos.map(todo => (
353
+ <TodoItem
354
+ key={todo.id}
355
+ todo={todo}
356
+ onToggle={handleToggleTodo}
357
+ onDelete={handleDeleteTodo}
358
+ />
359
+ ))
360
+ ) : (
361
+ <div className="empty-state">
362
+ <p>
363
+ {taskFilter === 'all' ? '🎉 No todos yet. Add one above!' :
364
+ taskFilter === 'completed' ? '📝 No completed todos yet.' :
365
+ taskFilter === 'active' ? '🎯 No active todos. Great job!' :
366
+ `🔍 No ${taskFilter} priority todos found.`}
367
+ </p>
368
+ </div>
369
+ )}
370
+ </div>
371
+ </section>
372
+
373
+ {/* Actions Section */}
374
+ <section className="card actions-section">
375
+ <h2>⚙️ Actions</h2>
376
+ <div className="action-buttons">
377
+ <button onClick={simulateError} className="btn btn-danger">
378
+ 💥 Test Error Boundary
379
+ </button>
380
+ </div>
381
+ </section>
382
+
383
+ </div>
384
+ </main>
385
+
386
+ {/* Footer */}
387
+ <AppFooter theme={theme} />
388
+
389
+ {/* Styles */}
390
+ <style>{`
391
+ /* ...existing styles... */
392
+ .error-message {
393
+ color: #e74c3c;
394
+ font-size: 0.875rem;
395
+ margin-top: 0.5rem;
396
+ }
397
+
398
+ .input.error {
399
+ border-color: #e74c3c;
400
+ }
401
+ `}</style>
24
402
  </div>
25
403
  );
26
404
  }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["client.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx } from "frontend-hamroun/jsx-runtime";
2
+ import { hydrate } from 'frontend-hamroun';
3
+ import { App } from './App';
4
+ document.addEventListener('DOMContentLoaded', () => {
5
+ hydrate(_jsx(App, {}), document.getElementById('root'));
6
+ });
@@ -0,0 +1,4 @@
1
+ export declare function Counter({ initial }: {
2
+ initial?: number | undefined;
3
+ }): JSX.Element;
4
+ //# sourceMappingURL=Counter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Counter.d.ts","sourceRoot":"","sources":["Counter.tsx"],"names":[],"mappings":"AAEA,wBAAgB,OAAO,CAAC,EAAE,OAAW,EAAE;;CAAA,eAetC"}
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "frontend-hamroun/jsx-runtime";
2
+ import { useState, useEffect } from 'frontend-hamroun';
3
+ export function Counter({ initial = 0 }) {
4
+ const [count, setCount] = useState(initial);
5
+ useEffect(() => {
6
+ document.title = `Count: ${count}`;
7
+ }, [count]);
8
+ return (_jsxs("div", { children: [_jsx("h2", { children: "Counter Component" }), _jsx("button", { onClick: () => setCount(count - 1), children: "-" }), _jsx("span", { style: { margin: '0 10px' }, children: count }), _jsx("button", { onClick: () => setCount(count + 1), children: "+" })] }));
9
+ }
@@ -0,0 +1,8 @@
1
+ import { jsx as createElement, Fragment } from 'frontend-hamroun';
2
+ declare global {
3
+ interface Window {
4
+ createElement: typeof createElement;
5
+ Fragment: typeof Fragment;
6
+ }
7
+ }
8
+ //# sourceMappingURL=jsx-shim.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsx-shim.d.ts","sourceRoot":"","sources":["jsx-shim.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,IAAI,aAAa,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAElE,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,aAAa,EAAE,OAAO,aAAa,CAAC;QACpC,QAAQ,EAAE,OAAO,QAAQ,CAAC;KAC3B;CACF"}
@@ -0,0 +1,2 @@
1
+ import './main.css';
2
+ //# sourceMappingURL=main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["main.tsx"],"names":[],"mappings":"AACA,OAAO,YAAY,CAAC"}
@@ -0,0 +1,57 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "frontend-hamroun/jsx-runtime";
2
+ import { render, useState, useEffect, useMemo, useRef, useErrorBoundary, createContext, useContext } from 'frontend-hamroun';
3
+ import './main.css';
4
+ // Create a theme context
5
+ const ThemeContext = createContext('light');
6
+ // Create a component that will throw an error when clicked
7
+ function BuggyComponent() {
8
+ const [shouldError, setShouldError] = useState(false);
9
+ if (shouldError) {
10
+ throw new Error("Test error from BuggyComponent");
11
+ }
12
+ return (_jsx("button", { onClick: () => setShouldError(true), className: "bg-red-600 text-white px-4 py-2 rounded mt-4 hover:bg-red-700", children: "Trigger Error" }));
13
+ }
14
+ function App() {
15
+ const [count, setCount] = useState(0);
16
+ const [theme, setTheme] = useState('light');
17
+ const renderCount = useRef(0);
18
+ const [error, resetError] = useErrorBoundary();
19
+ // Demonstrate useEffect
20
+ useEffect(() => {
21
+ document.title = `Count: ${count}`;
22
+ renderCount.current += 1;
23
+ return () => {
24
+ console.log('Cleanup effect');
25
+ };
26
+ }, [count]);
27
+ // Demonstrate useMemo
28
+ const doubled = useMemo(() => count * 2, [count]);
29
+ if (error) {
30
+ return (_jsxs("div", { className: "p-8 max-w-md mx-auto bg-red-50 rounded-lg shadow-lg", children: [_jsx("h1", { className: "text-2xl font-bold text-red-600 mb-4", children: "Something went wrong!" }), _jsx("button", { onClick: resetError, className: "bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded", children: "Try again" })] }));
31
+ }
32
+ return (_jsx(ThemeContext.Provider, { value: theme, children: _jsxs("div", { className: `p-8 max-w-4xl mx-auto rounded-lg shadow-lg transition-colors ${theme === 'dark'
33
+ ? 'bg-gray-800 text-white'
34
+ : 'bg-white text-gray-900'}`, children: [_jsx("h1", { className: "text-3xl font-bold mb-6", children: "Welcome to Frontend Hamroun" }), _jsxs("div", { className: "mb-6", children: [_jsx("button", { onClick: () => setCount(count - 1), className: "bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded", children: "-" }), _jsx("span", { className: "mx-4 text-xl", children: count }), _jsx("button", { onClick: () => setCount(count + 1), className: "bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded", children: "+" })] }), _jsxs("p", { className: "mb-2 text-lg", children: ["Doubled value: ", _jsx("span", { className: "font-semibold", children: doubled })] }), _jsxs("p", { className: "mb-4 text-lg", children: ["Render count: ", _jsx("span", { className: "font-semibold", children: renderCount.current })] }), _jsxs("button", { onClick: () => setTheme(theme === 'light' ? 'dark' : 'light'), className: `px-4 py-2 rounded mb-8 ${theme === 'dark'
35
+ ? 'bg-yellow-400 text-gray-900 hover:bg-yellow-300'
36
+ : 'bg-gray-800 text-white hover:bg-gray-700'}`, children: ["Toggle Theme (", theme, ")"] }), _jsxs("div", { className: "mt-8 border-t pt-6", children: [_jsx("h2", { className: "text-2xl font-bold mb-4", children: "Test Error Boundary" }), _jsx(BuggyComponent, {})] }), _jsxs("div", { className: "mt-8 border-t pt-6", children: [_jsx("h2", { className: "text-2xl font-bold mb-4", children: "Test Context" }), _jsx(ContextConsumer, {})] })] }) }));
37
+ }
38
+ // Component to test context
39
+ function ContextConsumer() {
40
+ // Get the full context object for debugging
41
+ const themeContext = useContext(ThemeContext);
42
+ console.log('Theme context object:', themeContext);
43
+ // Access the current theme from the parent component instead
44
+ // This is a workaround until context is properly implemented
45
+ return (_jsxs("div", { className: `mt-4 p-4 rounded-md border ${themeContext === 'dark' ? 'border-gray-600' : 'border-gray-300'}`, children: [_jsxs("p", { className: "mb-2", children: [_jsx("strong", { children: "Note:" }), " Context API needs a fix in the framework."] }), _jsxs("p", { className: "mb-1", children: ["Context object type: ", typeof themeContext] }), _jsxs("p", { className: "mb-4", children: ["Context object keys: ", Object.keys(themeContext).join(', ') || 'none'] }), _jsx("button", { onClick: () => {
46
+ // Since we can't get the value directly from context,
47
+ // Let's add a button to demonstrate we can still use the Provider
48
+ const newTheme = document.body.style.backgroundColor === 'rgb(51, 51, 51)'
49
+ ? 'light' : 'dark';
50
+ console.log('Manually changing theme to:', newTheme);
51
+ // This won't work yet, but demonstrates the intent
52
+ if (ThemeContext) {
53
+ console.log('Provider exists, would set value to:', newTheme);
54
+ }
55
+ }, className: "bg-purple-500 hover:bg-purple-600 text-white px-4 py-2 rounded", children: "Check Context Implementation" })] }));
56
+ }
57
+ render(_jsx(App, {}), document.getElementById('root'));
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["server.ts"],"names":[],"mappings":""}
@@ -0,0 +1,9 @@
1
+ declare const _default: {
2
+ content: string[];
3
+ theme: {
4
+ extend: {};
5
+ };
6
+ plugins: never[];
7
+ };
8
+ export default _default;
9
+ //# sourceMappingURL=tailwind.config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tailwind.config.d.ts","sourceRoot":"","sources":["tailwind.config.js"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ declare const _default: import("vite").UserConfig;
2
+ export default _default;
3
+ //# sourceMappingURL=vite.config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite.config.d.ts","sourceRoot":"","sources":["vite.config.ts"],"names":[],"mappings":";AAEA,wBAKG"}
@@ -0,0 +1,7 @@
1
+ import { defineConfig } from 'vite';
2
+ export default defineConfig({
3
+ esbuild: {
4
+ jsxFactory: 'jsx',
5
+ jsxFragment: 'Fragment'
6
+ }
7
+ });
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=hello.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hello.d.ts","sourceRoot":"","sources":["hello.js"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["client.js"],"names":[],"mappings":""}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Simple JSX implementation for server-side rendering
3
+ */
4
+ export function renderToString(vnode: any): any;
5
+ export function jsx(type: any, props: any, ...children: any[]): {
6
+ type: any;
7
+ props: any;
8
+ };
9
+ export function requestLogger(req: any, res: any, next: any): void;
10
+ export function errorHandler(err: any, req: any, res: any, next: any): void;
11
+ export function notFoundHandler(req: any, res: any): void;
12
+ export function rateLimit(options?: {
13
+ windowMs: number;
14
+ max: number;
15
+ }): (req: any, res: any, next: any) => any;
16
+ export function loadGoWasmFromFile(): void;
17
+ export const Fragment: unique symbol;
18
+ //# sourceMappingURL=frontend-hamroun.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontend-hamroun.d.ts","sourceRoot":"","sources":["frontend-hamroun.js"],"names":[],"mappings":"AAAA;;GAEG;AAGH,gDAuGC;AAGD;;;EAMC;AAkBD,mEAGC;AAGD,4EAGC;AAGD,0DAEC;AAGD;;;2CAuBC;AAID,2CAEC;AAHD,qCAAqD"}
@@ -0,0 +1,7 @@
1
+ declare function AboutPage(props: any): import("frontend-hamroun").VNode;
2
+ declare namespace AboutPage {
3
+ function getTitle(): string;
4
+ function getDescription(): string;
5
+ }
6
+ export default AboutPage;
7
+ //# sourceMappingURL=about.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"about.d.ts","sourceRoot":"","sources":["about.js"],"names":[],"mappings":"AAoBA,yEA8FC;;IAGD,4BAA+D;IAC/D,kCAAoH"}
@@ -0,0 +1,7 @@
1
+ declare function HomePage(props: any): import("frontend-hamroun").VNode;
2
+ declare namespace HomePage {
3
+ function getTitle(props: any): string;
4
+ function getDescription(props: any): string;
5
+ }
6
+ export default HomePage;
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":"AAqFA,wEAmEC;;IAGD,sCAA+E;IAC/E,4CAAsJ"}
@@ -0,0 +1,7 @@
1
+ declare function WasmDemo(props: any): import("frontend-hamroun").VNode;
2
+ declare namespace WasmDemo {
3
+ function getTitle(): string;
4
+ function getDescription(): string;
5
+ }
6
+ export default WasmDemo;
7
+ //# sourceMappingURL=wasm-demo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wasm-demo.d.ts","sourceRoot":"","sources":["wasm-demo.js"],"names":[],"mappings":"AAGA,wEA0RC;;IAGD,4BAA+D;IAC/D,kCAAiI"}