create-mcp-use-app 0.2.2 → 0.3.2
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/README.md +444 -0
- package/dist/index.js +88 -11
- package/dist/templates/ui/README.md +9 -9
- package/dist/templates/ui/index.ts +12 -0
- package/dist/templates/ui/package.json +6 -5
- package/dist/templates/ui/resources/kanban-board.tsx +1 -1
- package/dist/templates/ui/src/server.ts +17 -103
- package/dist/templates/ui/tsconfig.json +1 -1
- package/package.json +3 -3
- package/dist/templates/ui/resources/data-visualization.tsx +0 -475
- package/dist/templates/ui/resources/todo-list.tsx +0 -408
|
@@ -1,408 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useState } from 'react'
|
|
2
|
-
import { createRoot } from 'react-dom/client'
|
|
3
|
-
|
|
4
|
-
interface Todo {
|
|
5
|
-
id: string
|
|
6
|
-
text: string
|
|
7
|
-
completed: boolean
|
|
8
|
-
priority: 'low' | 'medium' | 'high'
|
|
9
|
-
dueDate?: string
|
|
10
|
-
category?: string
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
interface TodoListProps {
|
|
14
|
-
initialTodos?: Todo[]
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const TodoList: React.FC<TodoListProps> = ({ initialTodos = [] }) => {
|
|
18
|
-
const [todos, setTodos] = useState<Todo[]>(initialTodos)
|
|
19
|
-
const [newTodo, setNewTodo] = useState('')
|
|
20
|
-
const [filter, setFilter] = useState<'all' | 'active' | 'completed'>('all')
|
|
21
|
-
const [sortBy, setSortBy] = useState<'priority' | 'dueDate' | 'created'>('priority')
|
|
22
|
-
|
|
23
|
-
// Load todos from URL parameters or use defaults
|
|
24
|
-
useEffect(() => {
|
|
25
|
-
const urlParams = new URLSearchParams(window.location.search)
|
|
26
|
-
const todosParam = urlParams.get('todos')
|
|
27
|
-
|
|
28
|
-
if (todosParam) {
|
|
29
|
-
try {
|
|
30
|
-
const parsedTodos = JSON.parse(decodeURIComponent(todosParam))
|
|
31
|
-
setTodos(parsedTodos)
|
|
32
|
-
}
|
|
33
|
-
catch (error) {
|
|
34
|
-
console.error('Error parsing todos from URL:', error)
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
38
|
-
// Default todos for demo
|
|
39
|
-
setTodos([
|
|
40
|
-
{ id: '1', text: 'Complete project proposal', completed: false, priority: 'high', dueDate: '2024-01-15', category: 'Work' },
|
|
41
|
-
{ id: '2', text: 'Buy groceries', completed: false, priority: 'medium', dueDate: '2024-01-12', category: 'Personal' },
|
|
42
|
-
{ id: '3', text: 'Call dentist', completed: true, priority: 'low', category: 'Health' },
|
|
43
|
-
{ id: '4', text: 'Read React documentation', completed: false, priority: 'medium', category: 'Learning' },
|
|
44
|
-
{ id: '5', text: 'Plan weekend trip', completed: false, priority: 'low', dueDate: '2024-01-20', category: 'Personal' },
|
|
45
|
-
])
|
|
46
|
-
}
|
|
47
|
-
}, [])
|
|
48
|
-
|
|
49
|
-
const addTodo = () => {
|
|
50
|
-
if (newTodo.trim()) {
|
|
51
|
-
const todo: Todo = {
|
|
52
|
-
id: Date.now().toString(),
|
|
53
|
-
text: newTodo,
|
|
54
|
-
completed: false,
|
|
55
|
-
priority: 'medium',
|
|
56
|
-
}
|
|
57
|
-
setTodos([...todos, todo])
|
|
58
|
-
setNewTodo('')
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const toggleTodo = (id: string) => {
|
|
63
|
-
setTodos(todos.map(todo =>
|
|
64
|
-
todo.id === id ? { ...todo, completed: !todo.completed } : todo,
|
|
65
|
-
))
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const deleteTodo = (id: string) => {
|
|
69
|
-
setTodos(todos.filter(todo => todo.id !== id))
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const updateTodoPriority = (id: string, priority: Todo['priority']) => {
|
|
73
|
-
setTodos(todos.map(todo =>
|
|
74
|
-
todo.id === id ? { ...todo, priority } : todo,
|
|
75
|
-
))
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const getFilteredTodos = () => {
|
|
79
|
-
let filtered = todos
|
|
80
|
-
|
|
81
|
-
// Filter by status
|
|
82
|
-
switch (filter) {
|
|
83
|
-
case 'active':
|
|
84
|
-
filtered = filtered.filter(todo => !todo.completed)
|
|
85
|
-
break
|
|
86
|
-
case 'completed':
|
|
87
|
-
filtered = filtered.filter(todo => todo.completed)
|
|
88
|
-
break
|
|
89
|
-
default:
|
|
90
|
-
break
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Sort todos
|
|
94
|
-
return filtered.sort((a, b) => {
|
|
95
|
-
switch (sortBy) {
|
|
96
|
-
case 'priority':
|
|
97
|
-
const priorityOrder = { high: 3, medium: 2, low: 1 }
|
|
98
|
-
return priorityOrder[b.priority] - priorityOrder[a.priority]
|
|
99
|
-
case 'dueDate':
|
|
100
|
-
if (!a.dueDate && !b.dueDate)
|
|
101
|
-
return 0
|
|
102
|
-
if (!a.dueDate)
|
|
103
|
-
return 1
|
|
104
|
-
if (!b.dueDate)
|
|
105
|
-
return -1
|
|
106
|
-
return new Date(a.dueDate).getTime() - new Date(b.dueDate).getTime()
|
|
107
|
-
case 'created':
|
|
108
|
-
default:
|
|
109
|
-
return Number.parseInt(b.id) - Number.parseInt(a.id)
|
|
110
|
-
}
|
|
111
|
-
})
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// const getPriorityColor = (priority: Todo['priority']) => {
|
|
115
|
-
// switch (priority) {
|
|
116
|
-
// case 'high': return '#e74c3c'
|
|
117
|
-
// case 'medium': return '#f39c12'
|
|
118
|
-
// case 'low': return '#27ae60'
|
|
119
|
-
// default: return '#95a5a6'
|
|
120
|
-
// }
|
|
121
|
-
// }
|
|
122
|
-
|
|
123
|
-
const getPriorityIcon = (priority: Todo['priority']) => {
|
|
124
|
-
switch (priority) {
|
|
125
|
-
case 'high': return '🔴'
|
|
126
|
-
case 'medium': return '🟡'
|
|
127
|
-
case 'low': return '🟢'
|
|
128
|
-
default: return '⚪'
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const completedCount = todos.filter(todo => todo.completed).length
|
|
133
|
-
const totalCount = todos.length
|
|
134
|
-
const progressPercentage = totalCount > 0 ? (completedCount / totalCount) * 100 : 0
|
|
135
|
-
|
|
136
|
-
return (
|
|
137
|
-
<div style={{ padding: '20px' }}>
|
|
138
|
-
<div style={{ marginBottom: '30px' }}>
|
|
139
|
-
<h1 style={{ margin: '0 0 20px 0', color: '#2c3e50' }}>Todo List</h1>
|
|
140
|
-
|
|
141
|
-
{/* Progress bar */}
|
|
142
|
-
<div style={{
|
|
143
|
-
background: 'white',
|
|
144
|
-
padding: '20px',
|
|
145
|
-
borderRadius: '8px',
|
|
146
|
-
boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
|
|
147
|
-
marginBottom: '20px',
|
|
148
|
-
}}
|
|
149
|
-
>
|
|
150
|
-
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '10px' }}>
|
|
151
|
-
<span style={{ fontWeight: 'bold', color: '#2c3e50' }}>Progress</span>
|
|
152
|
-
<span style={{ color: '#7f8c8d' }}>
|
|
153
|
-
{completedCount}
|
|
154
|
-
{' '}
|
|
155
|
-
of
|
|
156
|
-
{' '}
|
|
157
|
-
{totalCount}
|
|
158
|
-
{' '}
|
|
159
|
-
completed
|
|
160
|
-
</span>
|
|
161
|
-
</div>
|
|
162
|
-
<div style={{
|
|
163
|
-
background: '#ecf0f1',
|
|
164
|
-
borderRadius: '10px',
|
|
165
|
-
height: '10px',
|
|
166
|
-
overflow: 'hidden',
|
|
167
|
-
}}
|
|
168
|
-
>
|
|
169
|
-
<div style={{
|
|
170
|
-
background: 'linear-gradient(90deg, #27ae60, #2ecc71)',
|
|
171
|
-
height: '100%',
|
|
172
|
-
width: `${progressPercentage}%`,
|
|
173
|
-
transition: 'width 0.3s ease',
|
|
174
|
-
}}
|
|
175
|
-
/>
|
|
176
|
-
</div>
|
|
177
|
-
</div>
|
|
178
|
-
|
|
179
|
-
{/* Add new todo */}
|
|
180
|
-
<div style={{
|
|
181
|
-
background: 'white',
|
|
182
|
-
padding: '20px',
|
|
183
|
-
borderRadius: '8px',
|
|
184
|
-
boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
|
|
185
|
-
marginBottom: '20px',
|
|
186
|
-
}}
|
|
187
|
-
>
|
|
188
|
-
<div style={{ display: 'flex', gap: '10px' }}>
|
|
189
|
-
<input
|
|
190
|
-
type="text"
|
|
191
|
-
placeholder="Add a new todo..."
|
|
192
|
-
value={newTodo}
|
|
193
|
-
onChange={e => setNewTodo(e.target.value)}
|
|
194
|
-
onKeyPress={e => e.key === 'Enter' && addTodo()}
|
|
195
|
-
style={{
|
|
196
|
-
flex: '1',
|
|
197
|
-
padding: '12px 16px',
|
|
198
|
-
border: '1px solid #ddd',
|
|
199
|
-
borderRadius: '6px',
|
|
200
|
-
fontSize: '16px',
|
|
201
|
-
}}
|
|
202
|
-
/>
|
|
203
|
-
<button
|
|
204
|
-
onClick={addTodo}
|
|
205
|
-
style={{
|
|
206
|
-
padding: '12px 24px',
|
|
207
|
-
background: '#3498db',
|
|
208
|
-
color: 'white',
|
|
209
|
-
border: 'none',
|
|
210
|
-
borderRadius: '6px',
|
|
211
|
-
cursor: 'pointer',
|
|
212
|
-
fontSize: '16px',
|
|
213
|
-
fontWeight: 'bold',
|
|
214
|
-
}}
|
|
215
|
-
>
|
|
216
|
-
Add
|
|
217
|
-
</button>
|
|
218
|
-
</div>
|
|
219
|
-
</div>
|
|
220
|
-
|
|
221
|
-
{/* Filters and sorting */}
|
|
222
|
-
<div style={{
|
|
223
|
-
background: 'white',
|
|
224
|
-
padding: '20px',
|
|
225
|
-
borderRadius: '8px',
|
|
226
|
-
boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
|
|
227
|
-
marginBottom: '20px',
|
|
228
|
-
}}
|
|
229
|
-
>
|
|
230
|
-
<div style={{ display: 'flex', gap: '20px', flexWrap: 'wrap', alignItems: 'center' }}>
|
|
231
|
-
<div>
|
|
232
|
-
<label style={{ marginRight: '10px', fontWeight: 'bold', color: '#2c3e50' }}>Filter:</label>
|
|
233
|
-
<select
|
|
234
|
-
value={filter}
|
|
235
|
-
onChange={e => setFilter(e.target.value as typeof filter)}
|
|
236
|
-
style={{
|
|
237
|
-
padding: '8px 12px',
|
|
238
|
-
border: '1px solid #ddd',
|
|
239
|
-
borderRadius: '4px',
|
|
240
|
-
}}
|
|
241
|
-
>
|
|
242
|
-
<option value="all">All</option>
|
|
243
|
-
<option value="active">Active</option>
|
|
244
|
-
<option value="completed">Completed</option>
|
|
245
|
-
</select>
|
|
246
|
-
</div>
|
|
247
|
-
|
|
248
|
-
<div>
|
|
249
|
-
<label style={{ marginRight: '10px', fontWeight: 'bold', color: '#2c3e50' }}>Sort by:</label>
|
|
250
|
-
<select
|
|
251
|
-
value={sortBy}
|
|
252
|
-
onChange={e => setSortBy(e.target.value as typeof sortBy)}
|
|
253
|
-
style={{
|
|
254
|
-
padding: '8px 12px',
|
|
255
|
-
border: '1px solid #ddd',
|
|
256
|
-
borderRadius: '4px',
|
|
257
|
-
}}
|
|
258
|
-
>
|
|
259
|
-
<option value="priority">Priority</option>
|
|
260
|
-
<option value="dueDate">Due Date</option>
|
|
261
|
-
<option value="created">Created</option>
|
|
262
|
-
</select>
|
|
263
|
-
</div>
|
|
264
|
-
</div>
|
|
265
|
-
</div>
|
|
266
|
-
</div>
|
|
267
|
-
|
|
268
|
-
{/* Todo list */}
|
|
269
|
-
<div style={{
|
|
270
|
-
background: 'white',
|
|
271
|
-
borderRadius: '8px',
|
|
272
|
-
boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
|
|
273
|
-
overflow: 'hidden',
|
|
274
|
-
}}
|
|
275
|
-
>
|
|
276
|
-
{getFilteredTodos().map(todo => (
|
|
277
|
-
<div
|
|
278
|
-
key={todo.id}
|
|
279
|
-
style={{
|
|
280
|
-
padding: '20px',
|
|
281
|
-
borderBottom: '1px solid #ecf0f1',
|
|
282
|
-
display: 'flex',
|
|
283
|
-
alignItems: 'center',
|
|
284
|
-
gap: '15px',
|
|
285
|
-
background: todo.completed ? '#f8f9fa' : 'white',
|
|
286
|
-
}}
|
|
287
|
-
>
|
|
288
|
-
<input
|
|
289
|
-
type="checkbox"
|
|
290
|
-
checked={todo.completed}
|
|
291
|
-
onChange={() => toggleTodo(todo.id)}
|
|
292
|
-
style={{
|
|
293
|
-
width: '20px',
|
|
294
|
-
height: '20px',
|
|
295
|
-
cursor: 'pointer',
|
|
296
|
-
}}
|
|
297
|
-
/>
|
|
298
|
-
|
|
299
|
-
<div style={{ flex: '1' }}>
|
|
300
|
-
<div style={{
|
|
301
|
-
display: 'flex',
|
|
302
|
-
alignItems: 'center',
|
|
303
|
-
gap: '10px',
|
|
304
|
-
marginBottom: '5px',
|
|
305
|
-
}}
|
|
306
|
-
>
|
|
307
|
-
<span style={{
|
|
308
|
-
fontSize: '18px',
|
|
309
|
-
textDecoration: todo.completed ? 'line-through' : 'none',
|
|
310
|
-
color: todo.completed ? '#7f8c8d' : '#2c3e50',
|
|
311
|
-
}}
|
|
312
|
-
>
|
|
313
|
-
{todo.text}
|
|
314
|
-
</span>
|
|
315
|
-
|
|
316
|
-
{todo.category && (
|
|
317
|
-
<span style={{
|
|
318
|
-
background: '#e9ecef',
|
|
319
|
-
color: '#495057',
|
|
320
|
-
padding: '2px 8px',
|
|
321
|
-
borderRadius: '12px',
|
|
322
|
-
fontSize: '12px',
|
|
323
|
-
}}
|
|
324
|
-
>
|
|
325
|
-
{todo.category}
|
|
326
|
-
</span>
|
|
327
|
-
)}
|
|
328
|
-
</div>
|
|
329
|
-
|
|
330
|
-
{todo.dueDate && (
|
|
331
|
-
<div style={{
|
|
332
|
-
fontSize: '14px',
|
|
333
|
-
color: '#7f8c8d',
|
|
334
|
-
display: 'flex',
|
|
335
|
-
alignItems: 'center',
|
|
336
|
-
gap: '5px',
|
|
337
|
-
}}
|
|
338
|
-
>
|
|
339
|
-
📅 Due:
|
|
340
|
-
{' '}
|
|
341
|
-
{new Date(todo.dueDate).toLocaleDateString()}
|
|
342
|
-
</div>
|
|
343
|
-
)}
|
|
344
|
-
</div>
|
|
345
|
-
|
|
346
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
|
|
347
|
-
<select
|
|
348
|
-
value={todo.priority}
|
|
349
|
-
onChange={e => updateTodoPriority(todo.id, e.target.value as Todo['priority'])}
|
|
350
|
-
style={{
|
|
351
|
-
padding: '4px 8px',
|
|
352
|
-
border: '1px solid #ddd',
|
|
353
|
-
borderRadius: '4px',
|
|
354
|
-
fontSize: '12px',
|
|
355
|
-
}}
|
|
356
|
-
>
|
|
357
|
-
<option value="low">Low</option>
|
|
358
|
-
<option value="medium">Medium</option>
|
|
359
|
-
<option value="high">High</option>
|
|
360
|
-
</select>
|
|
361
|
-
|
|
362
|
-
<span style={{ fontSize: '16px' }}>
|
|
363
|
-
{getPriorityIcon(todo.priority)}
|
|
364
|
-
</span>
|
|
365
|
-
|
|
366
|
-
<button
|
|
367
|
-
onClick={() => deleteTodo(todo.id)}
|
|
368
|
-
style={{
|
|
369
|
-
background: 'none',
|
|
370
|
-
border: 'none',
|
|
371
|
-
color: '#e74c3c',
|
|
372
|
-
cursor: 'pointer',
|
|
373
|
-
fontSize: '18px',
|
|
374
|
-
padding: '5px',
|
|
375
|
-
}}
|
|
376
|
-
>
|
|
377
|
-
🗑️
|
|
378
|
-
</button>
|
|
379
|
-
</div>
|
|
380
|
-
</div>
|
|
381
|
-
))}
|
|
382
|
-
|
|
383
|
-
{getFilteredTodos().length === 0 && (
|
|
384
|
-
<div style={{
|
|
385
|
-
textAlign: 'center',
|
|
386
|
-
padding: '40px 20px',
|
|
387
|
-
color: '#7f8c8d',
|
|
388
|
-
fontStyle: 'italic',
|
|
389
|
-
}}
|
|
390
|
-
>
|
|
391
|
-
{filter === 'all'
|
|
392
|
-
? 'No todos yet. Add one above!'
|
|
393
|
-
: filter === 'active'
|
|
394
|
-
? 'No active todos!'
|
|
395
|
-
: 'No completed todos!'}
|
|
396
|
-
</div>
|
|
397
|
-
)}
|
|
398
|
-
</div>
|
|
399
|
-
</div>
|
|
400
|
-
)
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
// Mount the component
|
|
404
|
-
const container = document.getElementById('widget-root')
|
|
405
|
-
if (container) {
|
|
406
|
-
const root = createRoot(container)
|
|
407
|
-
root.render(<TodoList />)
|
|
408
|
-
}
|