frontend-hamroun 1.2.84 → 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 (215) hide show
  1. package/package.json +1 -1
  2. package/templates/basic-app/build.d.ts +2 -0
  3. package/templates/basic-app/build.d.ts.map +1 -0
  4. package/templates/basic-app/dev.d.ts +2 -0
  5. package/templates/basic-app/dev.d.ts.map +1 -0
  6. package/templates/basic-app/esbuild.config.d.ts +2 -0
  7. package/templates/basic-app/esbuild.config.d.ts.map +1 -0
  8. package/templates/basic-app/postcss.config.d.ts +8 -0
  9. package/templates/basic-app/postcss.config.d.ts.map +1 -0
  10. package/templates/basic-app/server.d.ts +2 -0
  11. package/templates/basic-app/server.d.ts.map +1 -0
  12. package/templates/basic-app/src/App.d.ts +2 -0
  13. package/templates/basic-app/src/App.d.ts.map +1 -0
  14. package/templates/basic-app/src/App.js +148 -0
  15. package/templates/basic-app/src/client.d.ts +2 -0
  16. package/templates/basic-app/src/client.d.ts.map +1 -0
  17. package/templates/basic-app/src/client.js +6 -0
  18. package/templates/basic-app/src/components/Counter.d.ts +4 -0
  19. package/templates/basic-app/src/components/Counter.d.ts.map +1 -0
  20. package/templates/basic-app/src/components/Counter.js +9 -0
  21. package/templates/basic-app/src/jsx-shim.d.ts +8 -0
  22. package/templates/basic-app/src/jsx-shim.d.ts.map +1 -0
  23. package/templates/basic-app/src/main.d.ts +2 -0
  24. package/templates/basic-app/src/main.d.ts.map +1 -0
  25. package/templates/basic-app/src/main.js +57 -0
  26. package/templates/basic-app/src/server.d.ts +2 -0
  27. package/templates/basic-app/src/server.d.ts.map +1 -0
  28. package/templates/basic-app/tailwind.config.d.ts +9 -0
  29. package/templates/basic-app/tailwind.config.d.ts.map +1 -0
  30. package/templates/basic-app/vite.config.d.ts +3 -0
  31. package/templates/basic-app/vite.config.d.ts.map +1 -0
  32. package/templates/basic-app/vite.config.js +7 -0
  33. package/templates/complete-app/api/hello.d.ts +1 -0
  34. package/templates/complete-app/api/hello.d.ts.map +1 -0
  35. package/templates/complete-app/client.d.ts +2 -0
  36. package/templates/complete-app/client.d.ts.map +1 -0
  37. package/templates/complete-app/lib/frontend-hamroun.d.ts +18 -0
  38. package/templates/complete-app/lib/frontend-hamroun.d.ts.map +1 -0
  39. package/templates/complete-app/pages/about.d.ts +7 -0
  40. package/templates/complete-app/pages/about.d.ts.map +1 -0
  41. package/templates/complete-app/pages/index.d.ts +7 -0
  42. package/templates/complete-app/pages/index.d.ts.map +1 -0
  43. package/templates/complete-app/pages/wasm-demo.d.ts +7 -0
  44. package/templates/complete-app/pages/wasm-demo.d.ts.map +1 -0
  45. package/templates/complete-app/public/client.d.ts +17 -0
  46. package/templates/complete-app/public/client.d.ts.map +1 -0
  47. package/templates/complete-app/server.d.ts +2 -0
  48. package/templates/complete-app/server.d.ts.map +1 -0
  49. package/templates/complete-app/server.js +236 -218
  50. package/templates/complete-app/src/App.d.ts +2 -0
  51. package/templates/complete-app/src/App.d.ts.map +1 -0
  52. package/templates/complete-app/src/App.js +27 -0
  53. package/templates/complete-app/src/client.d.ts +2 -0
  54. package/templates/complete-app/src/client.d.ts.map +1 -0
  55. package/templates/complete-app/src/client.js +52 -0
  56. package/templates/complete-app/src/pages/index.d.ts +2 -0
  57. package/templates/complete-app/src/pages/index.d.ts.map +1 -0
  58. package/templates/complete-app/src/pages/index.js +19 -0
  59. package/templates/complete-app/src/server.d.ts +2 -0
  60. package/templates/complete-app/src/server.d.ts.map +1 -0
  61. package/templates/complete-app/src/server.js +192 -0
  62. package/templates/complete-app/vite.config.d.ts +3 -0
  63. package/templates/complete-app/vite.config.d.ts.map +1 -0
  64. package/templates/complete-app/vite.config.js +29 -57
  65. package/templates/fullstack-app/api/hello.d.ts +4 -0
  66. package/templates/fullstack-app/api/hello.d.ts.map +1 -0
  67. package/templates/fullstack-app/api/hello.js +14 -11
  68. package/templates/fullstack-app/api/users/[id].d.ts +5 -0
  69. package/templates/fullstack-app/api/users/[id].d.ts.map +1 -0
  70. package/templates/fullstack-app/api/users/[id].js +52 -0
  71. package/templates/fullstack-app/api/users/index.d.ts +4 -0
  72. package/templates/fullstack-app/api/users/index.d.ts.map +1 -0
  73. package/templates/fullstack-app/api/users/index.js +25 -0
  74. package/templates/fullstack-app/build/main.d.ts +211 -0
  75. package/templates/fullstack-app/build/main.d.ts.map +1 -0
  76. package/templates/fullstack-app/build.d.ts +2 -0
  77. package/templates/fullstack-app/build.d.ts.map +1 -0
  78. package/templates/fullstack-app/build.js +190 -0
  79. package/templates/fullstack-app/postcss.config.d.ts +5 -0
  80. package/templates/fullstack-app/postcss.config.d.ts.map +1 -0
  81. package/templates/fullstack-app/process-tailwind.d.ts +2 -0
  82. package/templates/fullstack-app/process-tailwind.d.ts.map +1 -0
  83. package/templates/fullstack-app/public/route-handler.d.ts +1 -0
  84. package/templates/fullstack-app/public/route-handler.d.ts.map +1 -0
  85. package/templates/fullstack-app/server.d.ts +2 -0
  86. package/templates/fullstack-app/server.d.ts.map +1 -0
  87. package/templates/fullstack-app/server.js +428 -266
  88. package/templates/fullstack-app/src/client.d.ts +2 -0
  89. package/templates/fullstack-app/src/client.d.ts.map +1 -0
  90. package/templates/fullstack-app/src/components/ClientHome.d.ts +1 -0
  91. package/templates/fullstack-app/src/components/ClientHome.d.ts.map +1 -0
  92. package/templates/fullstack-app/src/components/ClientHome.js +1 -0
  93. package/templates/fullstack-app/src/components/ErrorBoundary.d.ts +7 -0
  94. package/templates/fullstack-app/src/components/ErrorBoundary.d.ts.map +1 -0
  95. package/templates/fullstack-app/src/components/ErrorBoundary.js +12 -0
  96. package/templates/fullstack-app/src/components/Layout.d.ts +7 -0
  97. package/templates/fullstack-app/src/components/Layout.d.ts.map +1 -0
  98. package/templates/fullstack-app/src/components/Layout.js +4 -0
  99. package/templates/fullstack-app/src/components/StateDemo.d.ts +2 -0
  100. package/templates/fullstack-app/src/components/StateDemo.d.ts.map +1 -0
  101. package/templates/fullstack-app/src/components/StateDemo.js +86 -0
  102. package/templates/fullstack-app/src/components/UserList.d.ts +11 -0
  103. package/templates/fullstack-app/src/components/UserList.d.ts.map +1 -0
  104. package/templates/fullstack-app/src/components/UserList.js +7 -0
  105. package/templates/fullstack-app/src/config.d.ts +29 -0
  106. package/templates/fullstack-app/src/config.d.ts.map +1 -0
  107. package/templates/fullstack-app/src/config.js +36 -0
  108. package/templates/fullstack-app/src/data/api.d.ts +35 -0
  109. package/templates/fullstack-app/src/data/api.d.ts.map +1 -0
  110. package/templates/fullstack-app/src/data/api.js +173 -0
  111. package/templates/fullstack-app/src/main.d.ts +7 -0
  112. package/templates/fullstack-app/src/main.d.ts.map +1 -0
  113. package/templates/fullstack-app/src/main.js +130 -0
  114. package/templates/fullstack-app/src/middleware.d.ts +10 -0
  115. package/templates/fullstack-app/src/middleware.d.ts.map +1 -0
  116. package/templates/fullstack-app/src/middleware.js +14 -0
  117. package/templates/fullstack-app/src/pages/404.d.ts +4 -0
  118. package/templates/fullstack-app/src/pages/404.d.ts.map +1 -0
  119. package/templates/fullstack-app/src/pages/404.js +4 -0
  120. package/templates/fullstack-app/src/pages/[id].d.ts +1 -0
  121. package/templates/fullstack-app/src/pages/[id].d.ts.map +1 -0
  122. package/templates/fullstack-app/src/pages/[id].js +1 -0
  123. package/templates/fullstack-app/src/pages/_app.d.ts +6 -0
  124. package/templates/fullstack-app/src/pages/_app.d.ts.map +1 -0
  125. package/templates/fullstack-app/src/pages/_app.js +6 -0
  126. package/templates/fullstack-app/src/pages/_document.d.ts +7 -0
  127. package/templates/fullstack-app/src/pages/_document.d.ts.map +1 -0
  128. package/templates/fullstack-app/src/pages/_document.js +4 -0
  129. package/templates/fullstack-app/src/pages/_error.d.ts +4 -0
  130. package/templates/fullstack-app/src/pages/_error.d.ts.map +1 -0
  131. package/templates/fullstack-app/src/pages/_error.js +8 -0
  132. package/templates/fullstack-app/src/pages/about/index.d.ts +5 -0
  133. package/templates/fullstack-app/src/pages/about/index.d.ts.map +1 -0
  134. package/templates/fullstack-app/src/pages/about/index.js +6 -0
  135. package/templates/fullstack-app/src/pages/about.d.ts +10 -0
  136. package/templates/fullstack-app/src/pages/about.d.ts.map +1 -0
  137. package/templates/fullstack-app/src/pages/about.js +21 -0
  138. package/templates/fullstack-app/src/pages/api/users/[id].d.ts +6 -0
  139. package/templates/fullstack-app/src/pages/api/users/[id].d.ts.map +1 -0
  140. package/templates/fullstack-app/src/pages/api/users/[id].js +51 -0
  141. package/templates/fullstack-app/src/pages/api/users/index.d.ts +4 -0
  142. package/templates/fullstack-app/src/pages/api/users/index.d.ts.map +1 -0
  143. package/templates/fullstack-app/src/pages/api/users/index.js +33 -0
  144. package/templates/fullstack-app/src/pages/index.d.ts +21 -0
  145. package/templates/fullstack-app/src/pages/index.d.ts.map +1 -0
  146. package/templates/fullstack-app/src/pages/index.js +66 -0
  147. package/templates/fullstack-app/src/pages/users/[id].d.ts +38 -0
  148. package/templates/fullstack-app/src/pages/users/[id].d.ts.map +1 -0
  149. package/templates/fullstack-app/src/pages/users/[id].js +79 -0
  150. package/templates/fullstack-app/src/pages/users.d.ts +14 -0
  151. package/templates/fullstack-app/src/pages/users.d.ts.map +1 -0
  152. package/templates/fullstack-app/src/pages/users.js +14 -0
  153. package/templates/fullstack-app/src/pages/wasm-demo.d.ts +1 -0
  154. package/templates/fullstack-app/src/pages/wasm-demo.d.ts.map +1 -0
  155. package/templates/fullstack-app/src/pages/wasm-demo.js +2 -0
  156. package/templates/fullstack-app/src/router.d.ts +22 -0
  157. package/templates/fullstack-app/src/router.d.ts.map +1 -0
  158. package/templates/fullstack-app/src/router.js +210 -0
  159. package/templates/fullstack-app/tailwind.config.d.ts +3 -0
  160. package/templates/fullstack-app/tailwind.config.d.ts.map +1 -0
  161. package/templates/fullstack-app/vite.config.d.ts +3 -0
  162. package/templates/fullstack-app/vite.config.d.ts.map +1 -0
  163. package/templates/ssr-template/dist/client/assets/main-D-VH3xOb.d.ts +2 -0
  164. package/templates/ssr-template/dist/client/assets/main-D-VH3xOb.d.ts.map +1 -0
  165. package/templates/ssr-template/dist/client.d.ts +85 -0
  166. package/templates/ssr-template/dist/client.d.ts.map +1 -0
  167. package/templates/ssr-template/dist/server.d.ts +2 -0
  168. package/templates/ssr-template/dist/server.d.ts.map +1 -0
  169. package/templates/ssr-template/esbuild.config.d.ts +2 -0
  170. package/templates/ssr-template/esbuild.config.d.ts.map +1 -0
  171. package/templates/ssr-template/jsx-shim.d.ts +2 -0
  172. package/templates/ssr-template/jsx-shim.d.ts.map +1 -0
  173. package/templates/ssr-template/src/App.d.ts +2 -0
  174. package/templates/ssr-template/src/App.d.ts.map +1 -0
  175. package/templates/ssr-template/src/App.js +625 -0
  176. package/templates/ssr-template/src/client.d.ts +2 -0
  177. package/templates/ssr-template/src/client.d.ts.map +1 -0
  178. package/templates/ssr-template/src/client.js +3 -0
  179. package/templates/ssr-template/src/server.d.ts +2 -0
  180. package/templates/ssr-template/src/server.d.ts.map +1 -0
  181. package/templates/ssr-template/src/server.js +29 -0
  182. package/templates/ssr-template/vite.config.d.ts +3 -0
  183. package/templates/ssr-template/vite.config.d.ts.map +1 -0
  184. package/templates/ssr-template/vite.config.js +30 -0
  185. package/templates/wasm/build-wasm.d.ts +2 -0
  186. package/templates/wasm/build-wasm.d.ts.map +1 -0
  187. package/templates/wasm/dist/assets/index-BNqTDBdE.d.ts +30 -0
  188. package/templates/wasm/dist/assets/index-BNqTDBdE.d.ts.map +1 -0
  189. package/templates/wasm/dist/wasm_exec.d.ts +1 -0
  190. package/templates/wasm/dist/wasm_exec.d.ts.map +1 -0
  191. package/templates/wasm/esbuild.config.d.ts +2 -0
  192. package/templates/wasm/esbuild.config.d.ts.map +1 -0
  193. package/templates/wasm/go/wasm_exec.d.ts +1 -0
  194. package/templates/wasm/go/wasm_exec.d.ts.map +1 -0
  195. package/templates/wasm/jsx-shim.d.ts +5 -0
  196. package/templates/wasm/jsx-shim.d.ts.map +1 -0
  197. package/templates/wasm/public/wasm_exec.d.ts +1 -0
  198. package/templates/wasm/public/wasm_exec.d.ts.map +1 -0
  199. package/templates/wasm/src/App.d.ts +2 -0
  200. package/templates/wasm/src/App.d.ts.map +1 -0
  201. package/templates/wasm/src/App.js +381 -0
  202. package/templates/wasm/src/client.d.ts +2 -0
  203. package/templates/wasm/src/client.d.ts.map +1 -0
  204. package/templates/wasm/src/client.js +210 -0
  205. package/templates/wasm/src/index.d.ts +2 -0
  206. package/templates/wasm/src/index.d.ts.map +1 -0
  207. package/templates/wasm/src/index.js +20 -0
  208. package/templates/wasm/src/server.d.ts +2 -0
  209. package/templates/wasm/src/server.d.ts.map +1 -0
  210. package/templates/wasm/src/server.js +131 -0
  211. package/templates/wasm/vite.config.d.ts +3 -0
  212. package/templates/wasm/vite.config.d.ts.map +1 -0
  213. package/templates/wasm/vite.config.js +36 -0
  214. package/templates/wasm/wasm-loader.d.ts +6 -0
  215. package/templates/wasm/wasm-loader.d.ts.map +1 -0
@@ -0,0 +1,625 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "frontend-hamroun/jsx-runtime";
2
+ import { useState, useEffect, useMemo, useErrorBoundary } from 'frontend-hamroun';
3
+ // Todo Item Component - simple props-based component
4
+ function TodoItem({ todo, onToggle, onDelete }) {
5
+ return (_jsxs("div", { className: `todo-item ${todo.completed ? 'completed' : ''}`, children: [_jsx("input", { type: "checkbox", checked: todo.completed, onChange: () => onToggle(todo.id), className: "todo-checkbox" }), _jsx("span", { className: "todo-text", children: todo.text }), _jsxs("span", { className: `todo-priority priority-${todo.priority}`, children: [todo.priority === 'high' && '🔴', todo.priority === 'medium' && '🟡', todo.priority === 'low' && '🟢', todo.priority] }), _jsx("div", { className: "todo-actions", children: _jsx("button", { onClick: () => onDelete(todo.id), className: "btn btn-sm btn-danger", title: "Delete todo", children: "\uD83D\uDDD1\uFE0F" }) })] }));
6
+ }
7
+ // Theme Toggle Button Component
8
+ function ThemeToggleButton({ theme, onToggle }) {
9
+ return (_jsx("button", { onClick: onToggle, className: "btn btn-secondary theme-toggle", title: "Toggle theme", children: theme === 'light' ? '🌙' : '☀️' }));
10
+ }
11
+ // App Footer Component
12
+ function AppFooter({ theme, isBrowser }) {
13
+ return (_jsx("footer", { className: "app-footer", children: _jsxs("div", { className: "container", children: [_jsx("p", { children: "Built with Frontend Hamroun \u2022 Hooks: useState, useEffect, useMemo, useErrorBoundary" }), _jsxs("p", { children: ["Theme: ", _jsx("strong", { children: theme }), " \u2022 Environment: ", _jsx("strong", { children: isBrowser ? 'Client' : 'Server' })] })] }) }));
14
+ }
15
+ export function App() {
16
+ // State hooks with proper typing
17
+ const [todos, setTodos] = useState([
18
+ { id: 1, text: 'Learn Frontend Hamroun hooks', completed: false, priority: 'high' },
19
+ { id: 2, text: 'Build a todo app', completed: false, priority: 'medium' },
20
+ { id: 3, text: 'Master SSR concepts', completed: true, priority: 'low' }
21
+ ]);
22
+ const [newTask, setNewTask] = useState('');
23
+ const [taskFilter, setTaskFilter] = useState('all');
24
+ const [taskPriority, setTaskPriority] = useState('medium');
25
+ const [theme, setTheme] = useState('light');
26
+ const [isLoading, setIsLoading] = useState(false);
27
+ // Error boundary hook
28
+ const [error, resetError] = useErrorBoundary();
29
+ // Check if we're in browser environment
30
+ const isBrowser = typeof window !== 'undefined';
31
+ // Mount effect
32
+ useEffect(() => {
33
+ if (!isBrowser)
34
+ return;
35
+ console.log('Todo App mounted');
36
+ // Load saved todos from localStorage
37
+ const savedTodos = localStorage.getItem('todos');
38
+ const savedTheme = localStorage.getItem('theme');
39
+ if (savedTodos) {
40
+ try {
41
+ setTodos(JSON.parse(savedTodos));
42
+ }
43
+ catch (e) {
44
+ console.error('Failed to load saved todos');
45
+ }
46
+ }
47
+ if (savedTheme) {
48
+ setTheme(savedTheme);
49
+ }
50
+ return () => {
51
+ console.log('Todo App unmounting');
52
+ };
53
+ }, []);
54
+ // Theme effect
55
+ useEffect(() => {
56
+ if (!isBrowser)
57
+ return;
58
+ document.body.className = `theme-${theme}`;
59
+ document.documentElement.setAttribute('data-theme', theme);
60
+ localStorage.setItem('theme', theme);
61
+ }, [theme, isBrowser]);
62
+ // Save todos effect
63
+ useEffect(() => {
64
+ if (!isBrowser || todos.length === 0)
65
+ return;
66
+ localStorage.setItem('todos', JSON.stringify(todos));
67
+ }, [todos, isBrowser]);
68
+ // Memoized filtered todos
69
+ const filteredTodos = useMemo(() => {
70
+ console.log('Filtering todos - memoized computation');
71
+ return todos.filter(todo => {
72
+ if (taskFilter === 'completed')
73
+ return todo.completed;
74
+ if (taskFilter === 'active')
75
+ return !todo.completed;
76
+ if (taskFilter === 'high')
77
+ return todo.priority === 'high';
78
+ if (taskFilter === 'medium')
79
+ return todo.priority === 'medium';
80
+ if (taskFilter === 'low')
81
+ return todo.priority === 'low';
82
+ return true;
83
+ });
84
+ }, [todos, taskFilter]);
85
+ // Memoized todo stats
86
+ const todoStats = useMemo(() => {
87
+ const total = todos.length;
88
+ const completed = todos.filter(t => t.completed).length;
89
+ const active = total - completed;
90
+ const highPriority = todos.filter(t => t.priority === 'high' && !t.completed).length;
91
+ return { total, completed, active, highPriority };
92
+ }, [todos]);
93
+ // Todo action handlers
94
+ const handleToggleTodo = (id) => {
95
+ console.log('Toggling todo:', id);
96
+ setTodos(prev => prev.map(todo => todo.id === id ? { ...todo, completed: !todo.completed } : todo));
97
+ };
98
+ const handleDeleteTodo = (id) => {
99
+ console.log('Deleting todo:', id);
100
+ setTodos(prev => prev.filter(todo => todo.id !== id));
101
+ };
102
+ const handleAddTodo = (text, priority) => {
103
+ if (!text || !text.trim())
104
+ return;
105
+ const newTodo = {
106
+ id: Date.now(),
107
+ text: text.trim(),
108
+ completed: false,
109
+ priority: priority || 'medium'
110
+ };
111
+ setTodos(prev => [...prev, newTodo]);
112
+ };
113
+ // Event handlers
114
+ const addTask = () => {
115
+ if (newTask.trim()) {
116
+ setIsLoading(true);
117
+ // Simulate async operation
118
+ setTimeout(() => {
119
+ handleAddTodo(newTask, taskPriority);
120
+ setNewTask('');
121
+ setIsLoading(false);
122
+ }, 300);
123
+ }
124
+ };
125
+ const handleKeyPress = (e) => {
126
+ if (e.key === 'Enter') {
127
+ addTask();
128
+ }
129
+ };
130
+ const clearCompleted = () => {
131
+ setTodos(prev => prev.filter(todo => !todo.completed));
132
+ };
133
+ const markAllComplete = () => {
134
+ setTodos(prev => prev.map(todo => ({ ...todo, completed: true })));
135
+ };
136
+ const toggleTheme = () => {
137
+ setTheme(prev => prev === 'light' ? 'dark' : 'light');
138
+ };
139
+ const simulateError = () => {
140
+ throw new Error('Simulated error for testing error boundary');
141
+ };
142
+ if (error) {
143
+ return (_jsxs("div", { className: "error-container", children: [_jsx("h2", { children: "Something went wrong!" }), _jsx("p", { children: error.message }), _jsx("button", { onClick: resetError, className: "btn btn-primary", children: "Try Again" })] }));
144
+ }
145
+ return (_jsxs("div", { className: `app theme-${theme}`, children: [_jsx("header", { className: "app-header", children: _jsxs("div", { className: "container", children: [_jsxs("h1", { className: "app-title", children: ["\uD83D\uDCDD Todo App", _jsx("span", { className: "subtitle", children: "Built with Frontend Hamroun" })] }), _jsxs("div", { className: "header-controls", children: [_jsx(ThemeToggleButton, { theme: theme, onToggle: toggleTheme }), _jsxs("div", { className: "stats", children: [_jsxs("span", { className: "stat", children: ["Total: ", _jsx("strong", { children: todoStats.total })] }), _jsxs("span", { className: "stat", children: ["Active: ", _jsx("strong", { children: todoStats.active })] }), _jsxs("span", { className: "stat", children: ["Done: ", _jsx("strong", { children: todoStats.completed })] })] })] })] }) }), _jsx("main", { className: "main-content", children: _jsxs("div", { className: "container", children: [_jsxs("section", { className: "card add-todo-section", children: [_jsx("h2", { children: "\u2795 Add New Todo" }), _jsxs("div", { className: "add-todo-form", children: [_jsx("input", { type: "text", value: newTask, onChange: (e) => setNewTask(e.target.value), onKeyPress: handleKeyPress, placeholder: "What needs to be done?", className: "input todo-input", disabled: isLoading }), _jsxs("select", { value: taskPriority, onChange: (e) => setTaskPriority(e.target.value), className: "select priority-select", disabled: isLoading, children: [_jsx("option", { value: "low", children: "\uD83D\uDFE2 Low Priority" }), _jsx("option", { value: "medium", children: "\uD83D\uDFE1 Medium Priority" }), _jsx("option", { value: "high", children: "\uD83D\uDD34 High Priority" })] }), _jsx("button", { onClick: addTask, className: `btn btn-primary add-btn ${isLoading ? 'loading' : ''}`, disabled: isLoading || !newTask.trim(), children: isLoading ? '⏳ Adding...' : '➕ Add Todo' })] })] }), _jsxs("section", { className: "card filters-section", children: [_jsx("h2", { children: "\uD83D\uDD0D Filter Todos" }), _jsx("div", { className: "filters", children: ['all', 'active', 'completed', 'high', 'medium', 'low'].map(filterType => (_jsxs("button", { onClick: () => setTaskFilter(filterType), className: `btn btn-sm filter-btn ${taskFilter === filterType ? 'btn-primary' : 'btn-outline'}`, children: [filterType === 'all' && '📋 All', filterType === 'active' && '⏳ Active', filterType === 'completed' && '✅ Completed', filterType === 'high' && '🔴 High Priority', filterType === 'medium' && '🟡 Medium Priority', filterType === 'low' && '🟢 Low Priority'] }, filterType))) }), _jsxs("div", { className: "bulk-actions", children: [_jsx("button", { onClick: markAllComplete, className: "btn btn-success btn-sm", disabled: todoStats.active === 0, children: "\u2705 Mark All Complete" }), _jsx("button", { onClick: clearCompleted, className: "btn btn-warning btn-sm", disabled: todoStats.completed === 0, children: "\uD83D\uDDD1\uFE0F Clear Completed" })] })] }), _jsxs("section", { className: "card todos-section", children: [_jsxs("div", { className: "section-header", children: [_jsx("h2", { children: "\uD83D\uDCCB Todo List" }), _jsxs("div", { className: "filter-info", children: ["Showing ", _jsx("strong", { children: filteredTodos.length }), " of ", _jsx("strong", { children: todoStats.total }), " todos", taskFilter !== 'all' && _jsx("span", { className: "filter-badge", children: taskFilter })] })] }), _jsx("div", { className: "todos-list", children: filteredTodos.length > 0 ? (filteredTodos.map(todo => (_jsx(TodoItem, { todo: todo, onToggle: handleToggleTodo, onDelete: handleDeleteTodo }, todo.id)))) : (_jsx("div", { className: "empty-state", children: _jsx("p", { children: taskFilter === 'all' ? '🎉 No todos yet. Add one above!' :
146
+ taskFilter === 'completed' ? '📝 No completed todos yet.' :
147
+ taskFilter === 'active' ? '🎯 No active todos. Great job!' :
148
+ `🔍 No ${taskFilter} priority todos found.` }) })) })] }), _jsxs("section", { className: "card actions-section", children: [_jsx("h2", { children: "\u2699\uFE0F Actions" }), _jsx("div", { className: "action-buttons", children: _jsx("button", { onClick: simulateError, className: "btn btn-danger", children: "\uD83D\uDCA5 Test Error Boundary" }) })] })] }) }), _jsx(AppFooter, { theme: theme, isBrowser: isBrowser }), _jsx("style", { children: `
149
+ * {
150
+ margin: 0;
151
+ padding: 0;
152
+ box-sizing: border-box;
153
+ }
154
+
155
+ :root {
156
+ --primary: #3b82f6;
157
+ --primary-dark: #2563eb;
158
+ --secondary: #6b7280;
159
+ --success: #10b981;
160
+ --warning: #f59e0b;
161
+ --danger: #ef4444;
162
+ --background: #ffffff;
163
+ --surface: #f9fafb;
164
+ --text: #111827;
165
+ --text-muted: #6b7280;
166
+ --border: #e5e7eb;
167
+ --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1);
168
+ --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
169
+ }
170
+
171
+ [data-theme="dark"] {
172
+ --primary: #3b82f6;
173
+ --secondary: #9ca3af;
174
+ --success: #10b981;
175
+ --warning: #f59e0b;
176
+ --danger: #ef4444;
177
+ --background: #111827;
178
+ --surface: #1f2937;
179
+ --text: #f9fafb;
180
+ --text-muted: #9ca3af;
181
+ --border: #374151;
182
+ }
183
+
184
+ body {
185
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
186
+ line-height: 1.6;
187
+ color: var(--text);
188
+ background-color: var(--background);
189
+ transition: all 0.3s ease;
190
+ }
191
+
192
+ .app {
193
+ min-height: 100vh;
194
+ display: flex;
195
+ flex-direction: column;
196
+ }
197
+
198
+ .container {
199
+ max-width: 800px;
200
+ margin: 0 auto;
201
+ padding: 0 1rem;
202
+ }
203
+
204
+ /* Header */
205
+ .app-header {
206
+ background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
207
+ color: white;
208
+ padding: 2rem 0;
209
+ box-shadow: var(--shadow-lg);
210
+ }
211
+
212
+ .app-header .container {
213
+ display: flex;
214
+ justify-content: space-between;
215
+ align-items: center;
216
+ flex-wrap: wrap;
217
+ gap: 1rem;
218
+ }
219
+
220
+ .app-title {
221
+ font-size: 2rem;
222
+ font-weight: 700;
223
+ margin: 0;
224
+ display: flex;
225
+ flex-direction: column;
226
+ gap: 0.25rem;
227
+ }
228
+
229
+ .subtitle {
230
+ font-size: 1rem;
231
+ font-weight: 400;
232
+ opacity: 0.9;
233
+ }
234
+
235
+ .header-controls {
236
+ display: flex;
237
+ align-items: center;
238
+ gap: 1rem;
239
+ }
240
+
241
+ .stats {
242
+ display: flex;
243
+ gap: 1rem;
244
+ font-size: 0.875rem;
245
+ }
246
+
247
+ .stat {
248
+ background: rgba(255, 255, 255, 0.2);
249
+ padding: 0.5rem 0.75rem;
250
+ border-radius: 0.5rem;
251
+ backdrop-filter: blur(10px);
252
+ }
253
+
254
+ /* Main content */
255
+ .main-content {
256
+ flex: 1;
257
+ padding: 2rem 0;
258
+ }
259
+
260
+ /* Cards */
261
+ .card {
262
+ background: var(--surface);
263
+ border-radius: 1rem;
264
+ padding: 1.5rem;
265
+ margin-bottom: 1.5rem;
266
+ box-shadow: var(--shadow);
267
+ border: 1px solid var(--border);
268
+ transition: transform 0.2s ease;
269
+ }
270
+
271
+ .card:hover {
272
+ transform: translateY(-2px);
273
+ }
274
+
275
+ .card h2 {
276
+ font-size: 1.25rem;
277
+ margin-bottom: 1rem;
278
+ color: var(--text);
279
+ }
280
+
281
+ /* Add Todo Form */
282
+ .add-todo-form {
283
+ display: flex;
284
+ gap: 0.75rem;
285
+ flex-wrap: wrap;
286
+ }
287
+
288
+ .todo-input {
289
+ flex: 1;
290
+ min-width: 250px;
291
+ }
292
+
293
+ .priority-select {
294
+ min-width: 150px;
295
+ }
296
+
297
+ /* Filters */
298
+ .filters {
299
+ display: flex;
300
+ gap: 0.5rem;
301
+ margin-bottom: 1rem;
302
+ flex-wrap: wrap;
303
+ }
304
+
305
+ .filter-btn {
306
+ font-size: 0.875rem;
307
+ }
308
+
309
+ .bulk-actions {
310
+ display: flex;
311
+ gap: 0.5rem;
312
+ flex-wrap: wrap;
313
+ }
314
+
315
+ /* Section Header */
316
+ .section-header {
317
+ display: flex;
318
+ justify-content: space-between;
319
+ align-items: center;
320
+ margin-bottom: 1rem;
321
+ flex-wrap: wrap;
322
+ gap: 1rem;
323
+ }
324
+
325
+ .filter-info {
326
+ font-size: 0.875rem;
327
+ color: var(--text-muted);
328
+ }
329
+
330
+ .filter-badge {
331
+ background: var(--primary);
332
+ color: white;
333
+ padding: 0.25rem 0.5rem;
334
+ border-radius: 0.25rem;
335
+ font-size: 0.75rem;
336
+ margin-left: 0.5rem;
337
+ }
338
+
339
+ /* Todos List */
340
+ .todos-list {
341
+ display: flex;
342
+ flex-direction: column;
343
+ gap: 0.75rem;
344
+ }
345
+
346
+ .todo-item {
347
+ display: flex;
348
+ align-items: center;
349
+ gap: 0.75rem;
350
+ padding: 1rem;
351
+ background: var(--background);
352
+ border-radius: 0.5rem;
353
+ border: 1px solid var(--border);
354
+ transition: all 0.2s ease;
355
+ }
356
+
357
+ .todo-item:hover {
358
+ border-color: var(--primary);
359
+ box-shadow: var(--shadow);
360
+ }
361
+
362
+ .todo-item.completed {
363
+ opacity: 0.7;
364
+ }
365
+
366
+ .todo-item.completed .todo-text {
367
+ text-decoration: line-through;
368
+ }
369
+
370
+ .todo-checkbox {
371
+ width: 1.25rem;
372
+ height: 1.25rem;
373
+ cursor: pointer;
374
+ }
375
+
376
+ .todo-text {
377
+ flex: 1;
378
+ font-size: 1rem;
379
+ }
380
+
381
+ .todo-priority {
382
+ padding: 0.25rem 0.5rem;
383
+ border-radius: 0.25rem;
384
+ font-size: 0.75rem;
385
+ font-weight: 600;
386
+ }
387
+
388
+ .priority-high {
389
+ background: #fee2e2;
390
+ color: #991b1b;
391
+ }
392
+
393
+ .priority-medium {
394
+ background: #fef3c7;
395
+ color: #92400e;
396
+ }
397
+
398
+ .priority-low {
399
+ background: #dcfce7;
400
+ color: #166534;
401
+ }
402
+
403
+ .todo-actions {
404
+ display: flex;
405
+ gap: 0.5rem;
406
+ }
407
+
408
+ /* Form elements */
409
+ .input, .select {
410
+ padding: 0.75rem;
411
+ border: 2px solid var(--border);
412
+ border-radius: 0.5rem;
413
+ font-size: 1rem;
414
+ background: var(--background);
415
+ color: var(--text);
416
+ transition: border-color 0.2s ease;
417
+ }
418
+
419
+ .input:focus, .select:focus {
420
+ outline: none;
421
+ border-color: var(--primary);
422
+ }
423
+
424
+ /* Buttons */
425
+ .btn {
426
+ display: inline-flex;
427
+ align-items: center;
428
+ justify-content: center;
429
+ gap: 0.5rem;
430
+ padding: 0.75rem 1.5rem;
431
+ border: none;
432
+ border-radius: 0.5rem;
433
+ font-size: 0.875rem;
434
+ font-weight: 500;
435
+ cursor: pointer;
436
+ transition: all 0.2s ease;
437
+ text-decoration: none;
438
+ user-select: none;
439
+ }
440
+
441
+ .btn:disabled {
442
+ opacity: 0.5;
443
+ cursor: not-allowed;
444
+ }
445
+
446
+ .btn-sm {
447
+ padding: 0.5rem 1rem;
448
+ font-size: 0.75rem;
449
+ }
450
+
451
+ .btn-primary {
452
+ background: var(--primary);
453
+ color: white;
454
+ }
455
+
456
+ .btn-primary:hover:not(:disabled) {
457
+ background: var(--primary-dark);
458
+ transform: translateY(-1px);
459
+ }
460
+
461
+ .btn-secondary {
462
+ background: var(--secondary);
463
+ color: white;
464
+ }
465
+
466
+ .btn-success {
467
+ background: var(--success);
468
+ color: white;
469
+ }
470
+
471
+ .btn-warning {
472
+ background: var(--warning);
473
+ color: white;
474
+ }
475
+
476
+ .btn-danger {
477
+ background: var(--danger);
478
+ color: white;
479
+ }
480
+
481
+ .btn-outline {
482
+ background: transparent;
483
+ color: var(--text);
484
+ border: 2px solid var(--border);
485
+ }
486
+
487
+ .btn-outline:hover:not(:disabled) {
488
+ background: var(--surface);
489
+ border-color: var(--primary);
490
+ }
491
+
492
+ .btn.loading {
493
+ position: relative;
494
+ color: transparent;
495
+ }
496
+
497
+ .btn.loading::after {
498
+ content: '';
499
+ position: absolute;
500
+ width: 1rem;
501
+ height: 1rem;
502
+ border: 2px solid transparent;
503
+ border-top: 2px solid currentColor;
504
+ border-radius: 50%;
505
+ animation: spin 1s linear infinite;
506
+ color: white;
507
+ }
508
+
509
+ @keyframes spin {
510
+ to { transform: rotate(360deg); }
511
+ }
512
+
513
+ .theme-toggle {
514
+ border-radius: 50%;
515
+ width: 3rem;
516
+ height: 3rem;
517
+ padding: 0;
518
+ font-size: 1.25rem;
519
+ background: rgba(255, 255, 255, 0.2);
520
+ color: white;
521
+ border: 2px solid rgba(255, 255, 255, 0.3);
522
+ }
523
+
524
+ .theme-toggle:hover {
525
+ background: rgba(255, 255, 255, 0.3);
526
+ transform: scale(1.05);
527
+ }
528
+
529
+ /* Empty state */
530
+ .empty-state {
531
+ text-align: center;
532
+ padding: 3rem 1rem;
533
+ color: var(--text-muted);
534
+ }
535
+
536
+ .empty-state p {
537
+ font-size: 1.1rem;
538
+ }
539
+
540
+ /* Actions section */
541
+ .action-buttons {
542
+ display: flex;
543
+ gap: 1rem;
544
+ flex-wrap: wrap;
545
+ }
546
+
547
+ /* Footer */
548
+ .app-footer {
549
+ background: var(--surface);
550
+ border-top: 1px solid var(--border);
551
+ padding: 1.5rem 0;
552
+ text-align: center;
553
+ color: var(--text-muted);
554
+ font-size: 0.875rem;
555
+ }
556
+
557
+ .app-footer p {
558
+ margin: 0.25rem 0;
559
+ }
560
+
561
+ /* Error container */
562
+ .error-container {
563
+ display: flex;
564
+ flex-direction: column;
565
+ align-items: center;
566
+ justify-content: center;
567
+ min-height: 100vh;
568
+ padding: 2rem;
569
+ text-align: center;
570
+ }
571
+
572
+ .error-container h2 {
573
+ color: var(--danger);
574
+ margin-bottom: 1rem;
575
+ }
576
+
577
+ .error-container p {
578
+ color: var(--text-muted);
579
+ margin-bottom: 2rem;
580
+ }
581
+
582
+ /* Responsive design */
583
+ @media (max-width: 768px) {
584
+ .app-header .container {
585
+ flex-direction: column;
586
+ text-align: center;
587
+ }
588
+
589
+ .app-title {
590
+ font-size: 1.75rem;
591
+ }
592
+
593
+ .add-todo-form {
594
+ flex-direction: column;
595
+ }
596
+
597
+ .todo-input, .priority-select {
598
+ min-width: auto;
599
+ width: 100%;
600
+ }
601
+
602
+ .section-header {
603
+ flex-direction: column;
604
+ align-items: stretch;
605
+ }
606
+
607
+ .stats {
608
+ justify-content: center;
609
+ }
610
+
611
+ .filters {
612
+ justify-content: center;
613
+ }
614
+
615
+ .bulk-actions {
616
+ justify-content: center;
617
+ }
618
+ }
619
+
620
+ /* Smooth transitions */
621
+ * {
622
+ transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
623
+ }
624
+ ` })] }));
625
+ }
@@ -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,3 @@
1
+ import { hydrate, jsx } from 'frontend-hamroun';
2
+ import { App } from './App.js';
3
+ hydrate(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,29 @@
1
+ import express from 'express';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { renderToString, jsx } from 'frontend-hamroun';
5
+ import { App } from './App.js';
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+ const app = express();
9
+ const port = 3000;
10
+ // Serve static files from dist directory
11
+ app.use(express.static(path.join(__dirname)));
12
+ app.get('/', async (req, res) => {
13
+ const html = await renderToString(jsx(App, {}));
14
+ res.send(`
15
+ <!DOCTYPE html>
16
+ <html>
17
+ <head>
18
+ <title>SSR App</title>
19
+ </head>
20
+ <body>
21
+ <div id="root">${html}</div>
22
+ <script type="module" src="/client.js"></script>
23
+ </body>
24
+ </html>
25
+ `);
26
+ });
27
+ app.listen(port, () => {
28
+ console.log(`Server running at http://localhost:${port}`);
29
+ });
@@ -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":";AAGA,wBA4BG"}