create-theokit 1.0.6 → 1.0.7
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/package.json
CHANGED
|
@@ -220,6 +220,7 @@ code, pre { font-family: var(--font-mono); }
|
|
|
220
220
|
border: 1px solid var(--border);
|
|
221
221
|
border-radius: 12px;
|
|
222
222
|
padding: 24px;
|
|
223
|
+
width: 100%;
|
|
223
224
|
}
|
|
224
225
|
|
|
225
226
|
.card h2 {
|
|
@@ -314,6 +315,41 @@ tr.done td { opacity: 0.4; text-decoration: line-through; }
|
|
|
314
315
|
}
|
|
315
316
|
.chat-bar input:focus { border-color: var(--accent); }
|
|
316
317
|
|
|
318
|
+
/* ─── Features grid ─────────────────────────────────── */
|
|
319
|
+
|
|
320
|
+
.features {
|
|
321
|
+
margin-top: 32px;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.feature {
|
|
325
|
+
padding: 20px;
|
|
326
|
+
border: 1px solid var(--border);
|
|
327
|
+
border-radius: 12px;
|
|
328
|
+
background: var(--card);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.feature h3 {
|
|
332
|
+
font-size: 14px;
|
|
333
|
+
font-weight: 600;
|
|
334
|
+
margin-bottom: 6px;
|
|
335
|
+
font-family: var(--font-mono);
|
|
336
|
+
color: var(--accent);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.feature p {
|
|
340
|
+
font-size: 13px;
|
|
341
|
+
line-height: 1.5;
|
|
342
|
+
color: var(--text-secondary);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.feature code {
|
|
346
|
+
background: var(--bg);
|
|
347
|
+
border: 1px solid var(--border);
|
|
348
|
+
padding: 1px 5px;
|
|
349
|
+
border-radius: 4px;
|
|
350
|
+
font-size: 12px;
|
|
351
|
+
}
|
|
352
|
+
|
|
317
353
|
/* ─── Footer ────────────────────────────────────────── */
|
|
318
354
|
|
|
319
355
|
.footer {
|
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useState, useEffect, useCallback,
|
|
3
|
+
import { useState, useEffect, useCallback, type FormEvent } from 'react'
|
|
4
4
|
|
|
5
|
-
interface Task {
|
|
6
|
-
id: number
|
|
7
|
-
title: string
|
|
8
|
-
priority: 'high' | 'medium' | 'low'
|
|
9
|
-
done: boolean
|
|
10
|
-
}
|
|
5
|
+
interface Task { id: number; title: string; priority: 'high' | 'medium' | 'low'; done: boolean }
|
|
11
6
|
type Role = '' | 'user' | 'admin'
|
|
12
|
-
interface ChatMsg {
|
|
13
|
-
role: 'user' | 'agent' | 'tool' | 'system' | 'error'
|
|
14
|
-
text: string
|
|
15
|
-
}
|
|
16
7
|
|
|
17
8
|
export default function Page() {
|
|
18
9
|
const [tasks, setTasks] = useState<Task[]>([])
|
|
@@ -20,12 +11,6 @@ export default function Page() {
|
|
|
20
11
|
const [title, setTitle] = useState('')
|
|
21
12
|
const [priority, setPriority] = useState<Task['priority']>('medium')
|
|
22
13
|
const [formError, setFormError] = useState('')
|
|
23
|
-
const [chat, setChat] = useState<ChatMsg[]>([
|
|
24
|
-
{ role: 'system', text: 'Ask me to list, create, or complete tasks...' },
|
|
25
|
-
])
|
|
26
|
-
const [chatInput, setChatInput] = useState('')
|
|
27
|
-
const [chatBusy, setChatBusy] = useState(false)
|
|
28
|
-
const chatRef = useRef<HTMLDivElement>(null)
|
|
29
14
|
|
|
30
15
|
const hdrs = useCallback((): Record<string, string> => {
|
|
31
16
|
const h: Record<string, string> = { 'Content-Type': 'application/json' }
|
|
@@ -38,91 +23,19 @@ export default function Page() {
|
|
|
38
23
|
if (res.ok) setTasks(await res.json())
|
|
39
24
|
}, [])
|
|
40
25
|
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
loadTasks()
|
|
43
|
-
}, [loadTasks])
|
|
26
|
+
useEffect(() => { loadTasks() }, [loadTasks])
|
|
44
27
|
|
|
45
28
|
const createTask = async (e: FormEvent) => {
|
|
46
29
|
e.preventDefault()
|
|
47
30
|
setFormError('')
|
|
48
31
|
if (!title.trim()) return
|
|
49
|
-
const res = await fetch('/api/tasks', {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
body: JSON.stringify({ title, priority }),
|
|
53
|
-
})
|
|
54
|
-
if (res.status === 403) {
|
|
55
|
-
setFormError('403 — Need User role')
|
|
56
|
-
return
|
|
57
|
-
}
|
|
58
|
-
if (!res.ok) {
|
|
59
|
-
const b = await res.json()
|
|
60
|
-
setFormError(b.error?.issues?.[0]?.message ?? `Error ${res.status}`)
|
|
61
|
-
return
|
|
62
|
-
}
|
|
32
|
+
const res = await fetch('/api/tasks', { method: 'POST', headers: hdrs(), body: JSON.stringify({ title, priority }) })
|
|
33
|
+
if (res.status === 403) { setFormError('403 — Need User role'); return }
|
|
34
|
+
if (!res.ok) { const b = await res.json(); setFormError(b.error?.issues?.[0]?.message ?? `Error ${res.status}`); return }
|
|
63
35
|
setTitle('')
|
|
64
36
|
loadTasks()
|
|
65
37
|
}
|
|
66
38
|
|
|
67
|
-
const sendChat = async () => {
|
|
68
|
-
const msg = chatInput.trim()
|
|
69
|
-
if (!msg || chatBusy) return
|
|
70
|
-
setChatInput('')
|
|
71
|
-
setChat((c) => [...c, { role: 'user', text: msg }])
|
|
72
|
-
setChatBusy(true)
|
|
73
|
-
try {
|
|
74
|
-
const res = await fetch('/api/agents/assistant/chat', {
|
|
75
|
-
method: 'POST',
|
|
76
|
-
headers: hdrs(),
|
|
77
|
-
body: JSON.stringify({ message: msg, sessionId: 'session-' + Date.now() }),
|
|
78
|
-
})
|
|
79
|
-
if (res.status === 403) {
|
|
80
|
-
setChat((c) => [...c, { role: 'error', text: '403 — Need User role' }])
|
|
81
|
-
return
|
|
82
|
-
}
|
|
83
|
-
const reader = res.body?.getReader()
|
|
84
|
-
if (!reader) return
|
|
85
|
-
const decoder = new TextDecoder()
|
|
86
|
-
let buf = '',
|
|
87
|
-
agentText = ''
|
|
88
|
-
while (true) {
|
|
89
|
-
const { done, value } = await reader.read()
|
|
90
|
-
if (done) break
|
|
91
|
-
buf += decoder.decode(value, { stream: true })
|
|
92
|
-
const lines = buf.split('\n')
|
|
93
|
-
buf = lines.pop() ?? ''
|
|
94
|
-
for (const line of lines) {
|
|
95
|
-
if (!line.startsWith('data: ')) continue
|
|
96
|
-
try {
|
|
97
|
-
const ev = JSON.parse(line.slice(6))
|
|
98
|
-
if (ev.type === 'text_delta') agentText += ev.content
|
|
99
|
-
else if (ev.type === 'tool_call')
|
|
100
|
-
setChat((c) => [...c, { role: 'tool', text: `🔧 ${ev.toolName}` }])
|
|
101
|
-
else if (ev.type === 'tool_result')
|
|
102
|
-
setChat((c) => [...c, { role: 'tool', text: `✅ ${(ev.output ?? '').slice(0, 80)}` }])
|
|
103
|
-
else if (ev.type === 'error')
|
|
104
|
-
setChat((c) => [...c, { role: 'error', text: ev.message }])
|
|
105
|
-
} catch {
|
|
106
|
-
/* partial */
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
if (agentText) setChat((c) => [...c, { role: 'agent', text: agentText }])
|
|
111
|
-
loadTasks()
|
|
112
|
-
} catch (err) {
|
|
113
|
-
setChat((c) => [
|
|
114
|
-
...c,
|
|
115
|
-
{ role: 'error', text: `Error: ${err instanceof Error ? err.message : String(err)}` },
|
|
116
|
-
])
|
|
117
|
-
} finally {
|
|
118
|
-
setChatBusy(false)
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
useEffect(() => {
|
|
123
|
-
chatRef.current?.scrollTo(0, chatRef.current.scrollHeight)
|
|
124
|
-
}, [chat])
|
|
125
|
-
|
|
126
39
|
return (
|
|
127
40
|
<div className="page">
|
|
128
41
|
<div className="main">
|
|
@@ -132,128 +45,82 @@ export default function Page() {
|
|
|
132
45
|
<h1>TheoKit</h1>
|
|
133
46
|
<p className="tagline">Build the app your agent lives in.</p>
|
|
134
47
|
<nav className="ctas">
|
|
135
|
-
<a
|
|
136
|
-
href="https://usetheo.dev"
|
|
137
|
-
target="_blank"
|
|
138
|
-
rel="noopener noreferrer"
|
|
139
|
-
className="btn primary"
|
|
140
|
-
>
|
|
48
|
+
<a href="https://usetheo.dev" target="_blank" rel="noopener noreferrer" className="btn primary">
|
|
141
49
|
Get Started
|
|
142
50
|
</a>
|
|
143
|
-
<a
|
|
144
|
-
href="https://github.com/usetheodev/theokit"
|
|
145
|
-
target="_blank"
|
|
146
|
-
rel="noopener noreferrer"
|
|
147
|
-
className="btn secondary"
|
|
148
|
-
>
|
|
51
|
+
<a href="https://github.com/usetheodev/theokit" target="_blank" rel="noopener noreferrer" className="btn secondary">
|
|
149
52
|
Documentation
|
|
150
53
|
</a>
|
|
151
54
|
</nav>
|
|
152
55
|
<p className="hint">
|
|
153
|
-
Edit <code>app/page.tsx</code> to get started.
|
|
56
|
+
Edit <code>app/page.tsx</code> to get started. Changes hot-reload instantly.
|
|
154
57
|
</p>
|
|
155
58
|
</header>
|
|
156
59
|
|
|
157
60
|
{/* Role */}
|
|
158
61
|
<div className="role-bar">
|
|
159
62
|
<label htmlFor="role">Role:</label>
|
|
160
|
-
<select id="role" value={role} onChange={
|
|
63
|
+
<select id="role" value={role} onChange={e => setRole(e.target.value as Role)}>
|
|
161
64
|
<option value="">None (public)</option>
|
|
162
65
|
<option value="user">User</option>
|
|
163
66
|
<option value="admin">Admin</option>
|
|
164
67
|
</select>
|
|
165
68
|
</div>
|
|
166
69
|
|
|
167
|
-
{/*
|
|
168
|
-
<
|
|
169
|
-
<
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
<
|
|
177
|
-
<
|
|
178
|
-
<th>Status</th>
|
|
70
|
+
{/* Tasks */}
|
|
71
|
+
<section className="card">
|
|
72
|
+
<h2>Tasks <span className="badge">@Controller</span></h2>
|
|
73
|
+
<table>
|
|
74
|
+
<thead><tr><th>Task</th><th>Priority</th><th>Status</th></tr></thead>
|
|
75
|
+
<tbody>
|
|
76
|
+
{tasks.map(t => (
|
|
77
|
+
<tr key={t.id} className={t.done ? 'done' : ''}>
|
|
78
|
+
<td>{t.done ? '✅ ' : '○ '}{t.title}</td>
|
|
79
|
+
<td><span className={`prio prio-${t.priority}`}>{t.priority}</span></td>
|
|
80
|
+
<td>{t.done ? 'Done' : 'To do'}</td>
|
|
179
81
|
</tr>
|
|
180
|
-
</thead>
|
|
181
|
-
<tbody>
|
|
182
|
-
{tasks.map((t) => (
|
|
183
|
-
<tr key={t.id} className={t.done ? 'done' : ''}>
|
|
184
|
-
<td>
|
|
185
|
-
{t.done ? '✅ ' : '○ '}
|
|
186
|
-
{t.title}
|
|
187
|
-
</td>
|
|
188
|
-
<td>
|
|
189
|
-
<span className={`prio prio-${t.priority}`}>{t.priority}</span>
|
|
190
|
-
</td>
|
|
191
|
-
<td>{t.done ? 'Done' : 'To do'}</td>
|
|
192
|
-
</tr>
|
|
193
|
-
))}
|
|
194
|
-
</tbody>
|
|
195
|
-
</table>
|
|
196
|
-
<form onSubmit={createTask} className="create-bar">
|
|
197
|
-
<input
|
|
198
|
-
value={title}
|
|
199
|
-
onChange={(e) => setTitle(e.target.value)}
|
|
200
|
-
placeholder="New task..."
|
|
201
|
-
required
|
|
202
|
-
minLength={3}
|
|
203
|
-
/>
|
|
204
|
-
<select
|
|
205
|
-
value={priority}
|
|
206
|
-
onChange={(e) => setPriority(e.target.value as Task['priority'])}
|
|
207
|
-
>
|
|
208
|
-
<option value="medium">Medium</option>
|
|
209
|
-
<option value="high">High</option>
|
|
210
|
-
<option value="low">Low</option>
|
|
211
|
-
</select>
|
|
212
|
-
<button type="submit">Add</button>
|
|
213
|
-
</form>
|
|
214
|
-
{formError && <p className="error">{formError}</p>}
|
|
215
|
-
</section>
|
|
216
|
-
|
|
217
|
-
<section className="card">
|
|
218
|
-
<h2>
|
|
219
|
-
AI Assistant <span className="badge badge-ai">@Agent + SSE</span>
|
|
220
|
-
</h2>
|
|
221
|
-
<div ref={chatRef} className="chat-box">
|
|
222
|
-
{chat.map((m, i) => (
|
|
223
|
-
<div key={i} className={`msg ${m.role}`}>
|
|
224
|
-
{m.role === 'user' ? `You: ${m.text}` : m.text}
|
|
225
|
-
</div>
|
|
226
82
|
))}
|
|
227
|
-
</
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
83
|
+
</tbody>
|
|
84
|
+
</table>
|
|
85
|
+
<form onSubmit={createTask} className="create-bar">
|
|
86
|
+
<input value={title} onChange={e => setTitle(e.target.value)} placeholder="New task..." required minLength={3} />
|
|
87
|
+
<select value={priority} onChange={e => setPriority(e.target.value as Task['priority'])}>
|
|
88
|
+
<option value="medium">Medium</option>
|
|
89
|
+
<option value="high">High</option>
|
|
90
|
+
<option value="low">Low</option>
|
|
91
|
+
</select>
|
|
92
|
+
<button type="submit">Add</button>
|
|
93
|
+
</form>
|
|
94
|
+
{formError && <p className="error">{formError}</p>}
|
|
95
|
+
</section>
|
|
96
|
+
|
|
97
|
+
{/* Features */}
|
|
98
|
+
<div className="grid features">
|
|
99
|
+
<div className="feature">
|
|
100
|
+
<h3>@Controller</h3>
|
|
101
|
+
<p>NestJS-style decorators with convention naming. <code>TasksController</code> → <code>/api/tasks</code></p>
|
|
102
|
+
</div>
|
|
103
|
+
<div className="feature">
|
|
104
|
+
<h3>defineRoute</h3>
|
|
105
|
+
<p>Typed API routes with Zod validation. See <code>server/routes/health.ts</code></p>
|
|
106
|
+
</div>
|
|
107
|
+
<div className="feature">
|
|
108
|
+
<h3>@Agent + @Tool</h3>
|
|
109
|
+
<p>AI agents with SSE streaming, budget control, and human-in-the-loop approval.</p>
|
|
110
|
+
</div>
|
|
111
|
+
<div className="feature">
|
|
112
|
+
<h3>React + Vite</h3>
|
|
113
|
+
<p>File-based routing, HMR, SSR streaming. Edit and see changes instantly.</p>
|
|
114
|
+
</div>
|
|
241
115
|
</div>
|
|
242
116
|
|
|
243
117
|
{/* Footer */}
|
|
244
118
|
<footer className="footer">
|
|
245
|
-
Powered by
|
|
246
|
-
<a href="https://usetheo.dev" target="_blank" rel="noopener noreferrer">
|
|
247
|
-
TheoKit
|
|
248
|
-
</a>
|
|
119
|
+
Powered by <a href="https://usetheo.dev" target="_blank" rel="noopener noreferrer">TheoKit</a>
|
|
249
120
|
{' · '}
|
|
250
|
-
<a href="https://github.com/usetheodev/theokit" target="_blank" rel="noopener noreferrer">
|
|
251
|
-
GitHub
|
|
252
|
-
</a>
|
|
121
|
+
<a href="https://github.com/usetheodev/theokit" target="_blank" rel="noopener noreferrer">GitHub</a>
|
|
253
122
|
{' · '}
|
|
254
|
-
<a href="https://discord.usetheo.dev" target="_blank" rel="noopener noreferrer">
|
|
255
|
-
Discord
|
|
256
|
-
</a>
|
|
123
|
+
<a href="https://discord.usetheo.dev" target="_blank" rel="noopener noreferrer">Discord</a>
|
|
257
124
|
</footer>
|
|
258
125
|
</div>
|
|
259
126
|
</div>
|