import { effect, html, signal, render } from './uhtml.js'; import StateMachine from './fsm.js'; // Application state signals const todos = signal([]); const inputValue = signal(''); const currentFilter = signal('all'); // Todo state machine - models the lifecycle of individual todos const createTodoFSM = (id) => { return new StateMachine({ initial: 'pending', states: { pending: ['completed', 'editing'], completed: ['pending', 'editing'], editing: ['pending', 'completed'] } }); }; // App state machine - models the overall application state const appFSM = new StateMachine({ initial: 'viewing', states: { viewing: ['adding', 'filtering'], adding: ['viewing'], filtering: ['viewing'] } }); // Todo data structure let todoCounter = 0; const createTodo = (text) => ({ id: ++todoCounter, text: text, state: 'pending', fsm: createTodoFSM(todoCounter), created: new Date() }); // State management functions const addTodo = (text) => { console.log('addTodo called with:', text); if (text.trim()) { const newTodo = createTodo(text.trim()); console.log('Creating new todo:', newTodo); todos.value = [...todos.value, newTodo]; inputValue.value = ''; console.log('Todos updated, count:', todos.value.length); } }; const toggleTodo = async (id) => { const todoList = [...todos.value]; const todo = todoList.find(t => t.id === id); if (todo) { const newState = todo.state === 'pending' ? 'completed' : 'pending'; try { await todo.fsm.go(newState); todo.state = newState; todos.value = todoList; } catch (error) { console.error('Invalid state transition:', error); } } }; const deleteTodo = (id) => { todos.value = todos.value.filter(t => t.id !== id); }; const filteredTodos = signal([]); // Reactive effect to update filtered todos when todos or filter changes effect(() => { const filter = currentFilter.value; const todoList = todos.value; switch (filter) { case 'active': filteredTodos.value = todoList.filter(t => t.state === 'pending'); break; case 'completed': filteredTodos.value = todoList.filter(t => t.state === 'completed'); break; default: filteredTodos.value = todoList; } }); // Set up FSM event handlers appFSM.on('*', (prev, next) => { console.log(`App state: ${prev} → ${next}`); }); appFSM.on('adding', () => { const input = document.querySelector('.todo-input'); if (input) input.focus(); }); // Components const TodoInput = ({ value, onAdd, fsm }) => { const onInput = e => value.value = e.target.value; const onKeyPress = async e => { if (e.key === 'Enter') { console.log('Enter pressed, input value:', value.value); try { await fsm.go('adding'); onAdd(value.value); await fsm.go('viewing'); } catch (error) { console.error('FSM transition error:', error); } } }; return html`