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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-theokit",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "type": "module",
5
5
  "description": "Scaffold a new TheoKit project",
6
6
  "license": "Apache-2.0",
@@ -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, useRef, type FormEvent } from 'react'
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
- method: 'POST',
51
- headers: hdrs(),
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={(e) => setRole(e.target.value as Role)}>
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
- {/* Content */}
168
- <div className="grid">
169
- <section className="card">
170
- <h2>
171
- Tasks <span className="badge">@Controller</span>
172
- </h2>
173
- <table>
174
- <thead>
175
- <tr>
176
- <th>Task</th>
177
- <th>Priority</th>
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
- </div>
228
- <div className="chat-bar">
229
- <input
230
- value={chatInput}
231
- onChange={(e) => setChatInput(e.target.value)}
232
- onKeyDown={(e) => e.key === 'Enter' && sendChat()}
233
- placeholder="Message the AI assistant..."
234
- disabled={chatBusy}
235
- />
236
- <button type="button" onClick={sendChat} disabled={chatBusy}>
237
- Send
238
- </button>
239
- </div>
240
- </section>
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>