create-theokit 1.0.4 → 1.0.5
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,10 +1,11 @@
|
|
|
1
|
-
/* TheoKit —
|
|
1
|
+
/* TheoKit — design system */
|
|
2
2
|
|
|
3
3
|
:root {
|
|
4
4
|
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
|
|
5
5
|
--font-mono: 'SF Mono', SFMono-Regular, ui-monospace, 'DejaVu Sans Mono', Menlo, Consolas, monospace;
|
|
6
|
-
|
|
7
|
-
--bg
|
|
6
|
+
|
|
7
|
+
--bg: #fafafa;
|
|
8
|
+
--bg-content: #ffffff;
|
|
8
9
|
--card: #ffffff;
|
|
9
10
|
--border: #e5e5e5;
|
|
10
11
|
--text: #171717;
|
|
@@ -12,6 +13,10 @@
|
|
|
12
13
|
--text-muted: #999999;
|
|
13
14
|
--accent: #6366f1;
|
|
14
15
|
--accent-hover: #4f46e5;
|
|
16
|
+
--btn-primary-bg: #171717;
|
|
17
|
+
--btn-primary-hover: #383838;
|
|
18
|
+
--btn-secondary-hover: #f2f2f2;
|
|
19
|
+
--btn-secondary-border: #ebebeb;
|
|
15
20
|
--green: #22c55e;
|
|
16
21
|
--red: #ef4444;
|
|
17
22
|
--yellow: #eab308;
|
|
@@ -20,7 +25,7 @@
|
|
|
20
25
|
@media (prefers-color-scheme: dark) {
|
|
21
26
|
:root {
|
|
22
27
|
--bg: #0a0a0a;
|
|
23
|
-
--bg-
|
|
28
|
+
--bg-content: #0a0a0a;
|
|
24
29
|
--card: #141414;
|
|
25
30
|
--border: #2a2a2a;
|
|
26
31
|
--text: #ededed;
|
|
@@ -28,11 +33,14 @@
|
|
|
28
33
|
--text-muted: #666666;
|
|
29
34
|
--accent: #818cf8;
|
|
30
35
|
--accent-hover: #6366f1;
|
|
36
|
+
--btn-primary-bg: #ededed;
|
|
37
|
+
--btn-primary-hover: #cccccc;
|
|
38
|
+
--btn-secondary-hover: #1a1a1a;
|
|
39
|
+
--btn-secondary-border: #2a2a2a;
|
|
31
40
|
--green: #22c55e;
|
|
32
41
|
--red: #ef4444;
|
|
33
42
|
--yellow: #eab308;
|
|
34
43
|
}
|
|
35
|
-
|
|
36
44
|
html { color-scheme: dark; }
|
|
37
45
|
}
|
|
38
46
|
|
|
@@ -50,31 +58,146 @@ body {
|
|
|
50
58
|
min-height: 100vh;
|
|
51
59
|
display: flex;
|
|
52
60
|
flex-direction: column;
|
|
61
|
+
align-items: center;
|
|
53
62
|
-webkit-font-smoothing: antialiased;
|
|
54
63
|
-moz-osx-font-smoothing: grayscale;
|
|
55
64
|
}
|
|
56
65
|
|
|
66
|
+
a { color: inherit; text-decoration: none; }
|
|
57
67
|
code, pre { font-family: var(--font-mono); }
|
|
58
68
|
|
|
59
|
-
|
|
69
|
+
/* ─── Page container ────────────────────────────────── */
|
|
60
70
|
|
|
61
|
-
|
|
71
|
+
.page {
|
|
72
|
+
display: flex;
|
|
73
|
+
flex: 1;
|
|
74
|
+
flex-direction: column;
|
|
75
|
+
align-items: center;
|
|
76
|
+
width: 100%;
|
|
77
|
+
}
|
|
62
78
|
|
|
63
|
-
.
|
|
64
|
-
|
|
65
|
-
margin: 0 auto;
|
|
66
|
-
padding: 24px;
|
|
79
|
+
.main {
|
|
80
|
+
display: flex;
|
|
67
81
|
flex: 1;
|
|
82
|
+
flex-direction: column;
|
|
68
83
|
width: 100%;
|
|
84
|
+
max-width: 900px;
|
|
85
|
+
background: var(--bg-content);
|
|
86
|
+
padding: 80px 48px 48px;
|
|
69
87
|
}
|
|
70
88
|
|
|
71
|
-
|
|
89
|
+
@media (max-width: 768px) {
|
|
90
|
+
.main { padding: 48px 20px 32px; }
|
|
91
|
+
}
|
|
72
92
|
|
|
73
|
-
|
|
93
|
+
/* ─── Hero ──────────────────────────────────────────── */
|
|
94
|
+
|
|
95
|
+
.hero {
|
|
96
|
+
display: flex;
|
|
97
|
+
flex-direction: column;
|
|
98
|
+
align-items: center;
|
|
99
|
+
text-align: center;
|
|
100
|
+
gap: 16px;
|
|
101
|
+
margin-bottom: 48px;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.hero-logo {
|
|
105
|
+
border-radius: 16px;
|
|
106
|
+
margin-bottom: 8px;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.hero h1 {
|
|
110
|
+
font-size: 40px;
|
|
111
|
+
font-weight: 700;
|
|
112
|
+
letter-spacing: -2.4px;
|
|
113
|
+
line-height: 48px;
|
|
114
|
+
text-wrap: balance;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.hero .tagline {
|
|
118
|
+
font-size: 18px;
|
|
119
|
+
line-height: 28px;
|
|
120
|
+
color: var(--text-secondary);
|
|
121
|
+
max-width: 440px;
|
|
122
|
+
text-wrap: balance;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.hero .hint {
|
|
126
|
+
font-size: 14px;
|
|
127
|
+
color: var(--text-muted);
|
|
128
|
+
margin-top: 8px;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.hero .hint code {
|
|
74
132
|
background: var(--card);
|
|
75
133
|
border: 1px solid var(--border);
|
|
76
|
-
|
|
77
|
-
|
|
134
|
+
padding: 2px 8px;
|
|
135
|
+
border-radius: 6px;
|
|
136
|
+
font-size: 13px;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
@media (max-width: 768px) {
|
|
140
|
+
.hero h1 { font-size: 32px; line-height: 40px; letter-spacing: -1.92px; }
|
|
141
|
+
.hero .tagline { font-size: 16px; }
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* ─── CTA Buttons ───────────────────────────────────── */
|
|
145
|
+
|
|
146
|
+
.ctas {
|
|
147
|
+
display: flex;
|
|
148
|
+
gap: 12px;
|
|
149
|
+
margin-top: 8px;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.btn {
|
|
153
|
+
display: inline-flex;
|
|
154
|
+
justify-content: center;
|
|
155
|
+
align-items: center;
|
|
156
|
+
height: 40px;
|
|
157
|
+
padding: 0 20px;
|
|
158
|
+
border-radius: 128px;
|
|
159
|
+
border: 1px solid transparent;
|
|
160
|
+
font-size: 14px;
|
|
161
|
+
font-weight: 500;
|
|
162
|
+
cursor: pointer;
|
|
163
|
+
transition: background 0.2s, border-color 0.2s;
|
|
164
|
+
text-decoration: none;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.btn.primary {
|
|
168
|
+
background: var(--btn-primary-bg);
|
|
169
|
+
color: var(--bg);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.btn.secondary {
|
|
173
|
+
border-color: var(--btn-secondary-border);
|
|
174
|
+
color: var(--text);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
@media (hover: hover) and (pointer: fine) {
|
|
178
|
+
.btn.primary:hover { background: var(--btn-primary-hover); }
|
|
179
|
+
.btn.secondary:hover { background: var(--btn-secondary-hover); border-color: transparent; }
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* ─── Role selector ─────────────────────────────────── */
|
|
183
|
+
|
|
184
|
+
.role-bar {
|
|
185
|
+
display: flex;
|
|
186
|
+
justify-content: center;
|
|
187
|
+
align-items: center;
|
|
188
|
+
gap: 8px;
|
|
189
|
+
margin-bottom: 32px;
|
|
190
|
+
font-size: 14px;
|
|
191
|
+
color: var(--text-muted);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.role-bar select {
|
|
195
|
+
padding: 6px 12px;
|
|
196
|
+
background: var(--card);
|
|
197
|
+
border: 1px solid var(--border);
|
|
198
|
+
border-radius: 6px;
|
|
199
|
+
color: var(--text);
|
|
200
|
+
font-size: 13px;
|
|
78
201
|
}
|
|
79
202
|
|
|
80
203
|
/* ─── Grid ──────────────────────────────────────────── */
|
|
@@ -83,24 +206,35 @@ a { color: inherit; text-decoration: none; }
|
|
|
83
206
|
display: grid;
|
|
84
207
|
grid-template-columns: 1fr 1fr;
|
|
85
208
|
gap: 24px;
|
|
209
|
+
width: 100%;
|
|
86
210
|
}
|
|
87
211
|
|
|
88
212
|
@media (max-width: 768px) {
|
|
89
213
|
.grid { grid-template-columns: 1fr; }
|
|
90
214
|
}
|
|
91
215
|
|
|
92
|
-
/* ───
|
|
216
|
+
/* ─── Cards ─────────────────────────────────────────── */
|
|
93
217
|
|
|
94
|
-
|
|
95
|
-
|
|
218
|
+
.card {
|
|
219
|
+
background: var(--card);
|
|
220
|
+
border: 1px solid var(--border);
|
|
221
|
+
border-radius: 12px;
|
|
222
|
+
padding: 24px;
|
|
223
|
+
}
|
|
96
224
|
|
|
97
|
-
.
|
|
98
|
-
|
|
225
|
+
.card h2 {
|
|
226
|
+
font-size: 15px;
|
|
227
|
+
font-weight: 600;
|
|
228
|
+
margin-bottom: 16px;
|
|
229
|
+
display: flex;
|
|
230
|
+
align-items: center;
|
|
231
|
+
gap: 8px;
|
|
232
|
+
}
|
|
99
233
|
|
|
100
234
|
/* ─── Badges ────────────────────────────────────────── */
|
|
101
235
|
|
|
102
236
|
.badge {
|
|
103
|
-
font-size:
|
|
237
|
+
font-size: 11px;
|
|
104
238
|
padding: 2px 10px;
|
|
105
239
|
border-radius: 99px;
|
|
106
240
|
font-weight: 500;
|
|
@@ -115,87 +249,88 @@ h2 { font-size: 1rem; font-weight: 600; margin-bottom: 16px; display: flex; alig
|
|
|
115
249
|
|
|
116
250
|
/* ─── Table ─────────────────────────────────────────── */
|
|
117
251
|
|
|
118
|
-
table { width: 100%; border-collapse: collapse; font-size:
|
|
119
|
-
th {
|
|
120
|
-
|
|
121
|
-
|
|
252
|
+
table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
|
253
|
+
th {
|
|
254
|
+
text-align: left; padding: 8px 6px;
|
|
255
|
+
color: var(--text-muted); border-bottom: 1px solid var(--border);
|
|
256
|
+
font-weight: 500; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em;
|
|
257
|
+
}
|
|
258
|
+
td { padding: 8px 6px; border-bottom: 1px solid var(--border); }
|
|
259
|
+
tr.done td { opacity: 0.4; text-decoration: line-through; }
|
|
122
260
|
|
|
123
|
-
.prio { font-size:
|
|
261
|
+
.prio { font-size: 11px; padding: 2px 8px; border-radius: 99px; font-weight: 500; }
|
|
124
262
|
.prio-high { background: color-mix(in srgb, var(--red) 12%, transparent); color: var(--red); }
|
|
125
|
-
.prio-
|
|
263
|
+
.prio-medium { background: color-mix(in srgb, var(--yellow) 12%, transparent); color: var(--yellow); }
|
|
126
264
|
.prio-low { background: color-mix(in srgb, var(--green) 12%, transparent); color: var(--green); }
|
|
127
265
|
|
|
128
266
|
/* ─── Forms ─────────────────────────────────────────── */
|
|
129
267
|
|
|
130
268
|
.create-bar { display: flex; gap: 8px; margin-top: 16px; }
|
|
131
269
|
.create-bar input {
|
|
132
|
-
flex: 1; padding:
|
|
133
|
-
background: var(--bg
|
|
134
|
-
border-radius: 8px; color: var(--text); font-size:
|
|
270
|
+
flex: 1; padding: 8px 12px;
|
|
271
|
+
background: var(--bg); border: 1px solid var(--border);
|
|
272
|
+
border-radius: 8px; color: var(--text); font-size: 13px;
|
|
135
273
|
outline: none; transition: border-color 0.2s;
|
|
136
274
|
}
|
|
137
275
|
.create-bar input:focus { border-color: var(--accent); }
|
|
138
276
|
.create-bar select {
|
|
139
|
-
padding: 10px
|
|
277
|
+
padding: 8px 10px; background: var(--bg);
|
|
140
278
|
border: 1px solid var(--border); border-radius: 8px;
|
|
141
|
-
color: var(--text); font-size:
|
|
279
|
+
color: var(--text); font-size: 13px;
|
|
142
280
|
}
|
|
143
281
|
.create-bar button, .chat-bar button {
|
|
144
|
-
padding:
|
|
282
|
+
padding: 8px 16px; background: var(--accent); color: white;
|
|
145
283
|
border: none; border-radius: 8px; cursor: pointer;
|
|
146
|
-
font-weight: 600; font-size:
|
|
284
|
+
font-weight: 600; font-size: 13px; transition: background 0.2s;
|
|
147
285
|
}
|
|
148
|
-
|
|
149
286
|
@media (hover: hover) and (pointer: fine) {
|
|
150
287
|
.create-bar button:hover, .chat-bar button:hover { background: var(--accent-hover); }
|
|
151
288
|
}
|
|
152
|
-
|
|
153
289
|
.create-bar button:disabled, .chat-bar button:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
154
|
-
.error { color: var(--red); font-size:
|
|
155
|
-
|
|
156
|
-
/* ─── Role selector ─────────────────────────────────── */
|
|
157
|
-
|
|
158
|
-
.role-bar { margin-top: 14px; display: flex; align-items: center; gap: 8px; }
|
|
159
|
-
.role-bar label { font-size: 0.8rem; color: var(--text-muted); }
|
|
160
|
-
.role-bar select {
|
|
161
|
-
padding: 6px 12px; background: var(--bg-secondary);
|
|
162
|
-
border: 1px solid var(--border); border-radius: 6px;
|
|
163
|
-
color: var(--text); font-size: 0.8rem;
|
|
164
|
-
}
|
|
290
|
+
.error { color: var(--red); font-size: 13px; margin-top: 6px; }
|
|
165
291
|
|
|
166
292
|
/* ─── Chat ──────────────────────────────────────────── */
|
|
167
293
|
|
|
168
294
|
.chat-box {
|
|
169
|
-
height:
|
|
170
|
-
background: var(--bg
|
|
295
|
+
height: 320px; overflow-y: auto; padding: 14px;
|
|
296
|
+
background: var(--bg); border: 1px solid var(--border);
|
|
171
297
|
border-radius: 10px; margin-bottom: 12px;
|
|
172
|
-
font-size:
|
|
298
|
+
font-size: 13px; line-height: 1.6;
|
|
173
299
|
}
|
|
174
300
|
|
|
175
|
-
.msg { margin-bottom:
|
|
301
|
+
.msg { margin-bottom: 8px; padding: 8px 12px; border-radius: 8px; }
|
|
176
302
|
.msg.user { background: color-mix(in srgb, var(--accent) 10%, transparent); color: var(--accent); }
|
|
177
303
|
.msg.agent { background: var(--card); border: 1px solid var(--border); }
|
|
178
|
-
.msg.tool { background: color-mix(in srgb, var(--yellow) 8%, transparent); color: var(--yellow); font-size:
|
|
179
|
-
.msg.system { color: var(--text-muted); font-size:
|
|
180
|
-
.msg.error { color: var(--red); font-size:
|
|
304
|
+
.msg.tool { background: color-mix(in srgb, var(--yellow) 8%, transparent); color: var(--yellow); font-size: 12px; font-family: var(--font-mono); }
|
|
305
|
+
.msg.system { color: var(--text-muted); font-size: 12px; font-style: italic; }
|
|
306
|
+
.msg.error { color: var(--red); font-size: 13px; }
|
|
181
307
|
|
|
182
308
|
.chat-bar { display: flex; gap: 8px; }
|
|
183
309
|
.chat-bar input {
|
|
184
|
-
flex: 1; padding:
|
|
185
|
-
background: var(--bg
|
|
186
|
-
border-radius: 10px; color: var(--text); font-size:
|
|
310
|
+
flex: 1; padding: 10px 14px;
|
|
311
|
+
background: var(--bg); border: 1px solid var(--border);
|
|
312
|
+
border-radius: 10px; color: var(--text); font-size: 14px;
|
|
187
313
|
outline: none; transition: border-color 0.2s;
|
|
188
314
|
}
|
|
189
315
|
.chat-bar input:focus { border-color: var(--accent); }
|
|
190
316
|
|
|
191
|
-
|
|
317
|
+
/* ─── Footer ────────────────────────────────────────── */
|
|
318
|
+
|
|
319
|
+
.footer {
|
|
320
|
+
text-align: center;
|
|
321
|
+
padding: 40px 24px 32px;
|
|
322
|
+
font-size: 13px;
|
|
323
|
+
color: var(--text-muted);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.footer a { color: var(--text-secondary); transition: color 0.2s; }
|
|
327
|
+
.footer a:hover { color: var(--accent); }
|
|
192
328
|
|
|
193
329
|
/* ─── Loading ───────────────────────────────────────── */
|
|
194
330
|
|
|
195
331
|
.loading-spinner {
|
|
196
|
-
width:
|
|
197
|
-
|
|
198
|
-
border: 3px solid var(--border);
|
|
332
|
+
width: 24px; height: 24px;
|
|
333
|
+
border: 2px solid var(--border);
|
|
199
334
|
border-top-color: var(--accent);
|
|
200
335
|
border-radius: 50%;
|
|
201
336
|
animation: spin 0.8s linear infinite;
|
|
@@ -204,93 +339,3 @@ tr.done td { opacity: 0.45; text-decoration: line-through; }
|
|
|
204
339
|
@keyframes spin {
|
|
205
340
|
to { transform: rotate(360deg); }
|
|
206
341
|
}
|
|
207
|
-
|
|
208
|
-
/* ─── Hero ──────────────────────────────────────────── */
|
|
209
|
-
|
|
210
|
-
.hero {
|
|
211
|
-
text-align: center;
|
|
212
|
-
padding: 48px 24px 32px;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
.hero img {
|
|
216
|
-
margin-bottom: 16px;
|
|
217
|
-
border-radius: 16px;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
.hero h1 {
|
|
221
|
-
font-size: 2.5rem;
|
|
222
|
-
font-weight: 800;
|
|
223
|
-
letter-spacing: -0.03em;
|
|
224
|
-
margin-bottom: 4px;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
.hero .tagline {
|
|
228
|
-
color: var(--text-secondary);
|
|
229
|
-
font-size: 1.1rem;
|
|
230
|
-
margin-bottom: 24px;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
.hero-links {
|
|
234
|
-
display: flex;
|
|
235
|
-
justify-content: center;
|
|
236
|
-
gap: 12px;
|
|
237
|
-
margin-bottom: 24px;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
.btn {
|
|
241
|
-
display: inline-flex;
|
|
242
|
-
align-items: center;
|
|
243
|
-
padding: 10px 24px;
|
|
244
|
-
border-radius: 99px;
|
|
245
|
-
font-weight: 600;
|
|
246
|
-
font-size: 0.9rem;
|
|
247
|
-
text-decoration: none;
|
|
248
|
-
transition: background 0.2s, border-color 0.2s;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
.btn.primary {
|
|
252
|
-
background: var(--accent);
|
|
253
|
-
color: white;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
.btn.secondary {
|
|
257
|
-
background: transparent;
|
|
258
|
-
border: 1px solid var(--border);
|
|
259
|
-
color: var(--text);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
@media (hover: hover) and (pointer: fine) {
|
|
263
|
-
.btn.primary:hover { background: var(--accent-hover); }
|
|
264
|
-
.btn.secondary:hover { background: var(--bg-secondary); }
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
.hero .hint {
|
|
268
|
-
color: var(--text-muted);
|
|
269
|
-
font-size: 0.8rem;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
.hero .hint code {
|
|
273
|
-
font-family: var(--font-mono);
|
|
274
|
-
background: var(--bg-secondary);
|
|
275
|
-
padding: 2px 6px;
|
|
276
|
-
border-radius: 4px;
|
|
277
|
-
font-size: 0.75rem;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/* ─── Footer ────────────────────────────────────────── */
|
|
281
|
-
|
|
282
|
-
.footer {
|
|
283
|
-
text-align: center;
|
|
284
|
-
padding: 32px 24px;
|
|
285
|
-
color: var(--text-muted);
|
|
286
|
-
font-size: 0.8rem;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
.footer a {
|
|
290
|
-
color: var(--accent);
|
|
291
|
-
text-decoration: none;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
.footer a:hover {
|
|
295
|
-
text-decoration: underline;
|
|
296
|
-
}
|
|
@@ -2,23 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState, useEffect, useCallback, useRef, type FormEvent } from 'react'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
interface Task {
|
|
8
|
-
id: number
|
|
9
|
-
title: string
|
|
10
|
-
priority: 'high' | 'medium' | 'low'
|
|
11
|
-
done: boolean
|
|
12
|
-
}
|
|
13
|
-
|
|
5
|
+
interface Task { id: number; title: string; priority: 'high' | 'medium' | 'low'; done: boolean }
|
|
14
6
|
type Role = '' | 'user' | 'admin'
|
|
15
|
-
|
|
16
|
-
interface ChatMsg {
|
|
17
|
-
role: 'user' | 'agent' | 'tool' | 'system' | 'error'
|
|
18
|
-
text: string
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// ── Page ──
|
|
7
|
+
interface ChatMsg { role: 'user' | 'agent' | 'tool' | 'system' | 'error'; text: string }
|
|
22
8
|
|
|
23
9
|
export default function Page() {
|
|
24
10
|
const [tasks, setTasks] = useState<Task[]>([])
|
|
@@ -31,13 +17,12 @@ export default function Page() {
|
|
|
31
17
|
const [chatBusy, setChatBusy] = useState(false)
|
|
32
18
|
const chatRef = useRef<HTMLDivElement>(null)
|
|
33
19
|
|
|
34
|
-
const
|
|
20
|
+
const hdrs = useCallback((): Record<string, string> => {
|
|
35
21
|
const h: Record<string, string> = { 'Content-Type': 'application/json' }
|
|
36
22
|
if (role) h['x-role'] = role
|
|
37
23
|
return h
|
|
38
24
|
}, [role])
|
|
39
25
|
|
|
40
|
-
// Fetch tasks on mount + when role changes
|
|
41
26
|
const loadTasks = useCallback(async () => {
|
|
42
27
|
const res = await fetch('/api/tasks')
|
|
43
28
|
if (res.ok) setTasks(await res.json())
|
|
@@ -45,38 +30,33 @@ export default function Page() {
|
|
|
45
30
|
|
|
46
31
|
useEffect(() => { loadTasks() }, [loadTasks])
|
|
47
32
|
|
|
48
|
-
// Create task
|
|
49
33
|
const createTask = async (e: FormEvent) => {
|
|
50
34
|
e.preventDefault()
|
|
51
35
|
setFormError('')
|
|
52
36
|
if (!title.trim()) return
|
|
53
|
-
const res = await fetch('/api/tasks', { method: 'POST', headers:
|
|
37
|
+
const res = await fetch('/api/tasks', { method: 'POST', headers: hdrs(), body: JSON.stringify({ title, priority }) })
|
|
54
38
|
if (res.status === 403) { setFormError('403 — Need User role'); return }
|
|
55
39
|
if (!res.ok) { const b = await res.json(); setFormError(b.error?.issues?.[0]?.message ?? `Error ${res.status}`); return }
|
|
56
40
|
setTitle('')
|
|
57
41
|
loadTasks()
|
|
58
42
|
}
|
|
59
43
|
|
|
60
|
-
// AI Chat
|
|
61
44
|
const sendChat = async () => {
|
|
62
45
|
const msg = chatInput.trim()
|
|
63
46
|
if (!msg || chatBusy) return
|
|
64
47
|
setChatInput('')
|
|
65
48
|
setChat(c => [...c, { role: 'user', text: msg }])
|
|
66
49
|
setChatBusy(true)
|
|
67
|
-
|
|
68
50
|
try {
|
|
69
51
|
const res = await fetch('/api/agents/assistant/chat', {
|
|
70
|
-
method: 'POST', headers:
|
|
52
|
+
method: 'POST', headers: hdrs(),
|
|
71
53
|
body: JSON.stringify({ message: msg, sessionId: 'session-' + Date.now() }),
|
|
72
54
|
})
|
|
73
55
|
if (res.status === 403) { setChat(c => [...c, { role: 'error', text: '403 — Need User role' }]); return }
|
|
74
|
-
|
|
75
56
|
const reader = res.body?.getReader()
|
|
76
57
|
if (!reader) return
|
|
77
58
|
const decoder = new TextDecoder()
|
|
78
59
|
let buf = '', agentText = ''
|
|
79
|
-
|
|
80
60
|
while (true) {
|
|
81
61
|
const { done, value } = await reader.read()
|
|
82
62
|
if (done) break
|
|
@@ -90,111 +70,100 @@ export default function Page() {
|
|
|
90
70
|
else if (ev.type === 'tool_call') setChat(c => [...c, { role: 'tool', text: `🔧 ${ev.toolName}` }])
|
|
91
71
|
else if (ev.type === 'tool_result') setChat(c => [...c, { role: 'tool', text: `✅ ${(ev.output ?? '').slice(0, 80)}` }])
|
|
92
72
|
else if (ev.type === 'error') setChat(c => [...c, { role: 'error', text: ev.message }])
|
|
93
|
-
} catch { /* partial
|
|
73
|
+
} catch { /* partial */ }
|
|
94
74
|
}
|
|
95
75
|
}
|
|
96
76
|
if (agentText) setChat(c => [...c, { role: 'agent', text: agentText }])
|
|
97
77
|
loadTasks()
|
|
98
78
|
} catch (err) {
|
|
99
79
|
setChat(c => [...c, { role: 'error', text: `Error: ${err instanceof Error ? err.message : String(err)}` }])
|
|
100
|
-
} finally {
|
|
101
|
-
setChatBusy(false)
|
|
102
|
-
}
|
|
80
|
+
} finally { setChatBusy(false) }
|
|
103
81
|
}
|
|
104
82
|
|
|
105
83
|
useEffect(() => { chatRef.current?.scrollTo(0, chatRef.current.scrollHeight) }, [chat])
|
|
106
84
|
|
|
107
85
|
return (
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
<
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
<
|
|
116
|
-
usetheo.dev
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
<
|
|
130
|
-
|
|
131
|
-
<
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
86
|
+
<div className="page">
|
|
87
|
+
<div className="main">
|
|
88
|
+
{/* Hero */}
|
|
89
|
+
<header className="hero">
|
|
90
|
+
<img src="/logo.png" alt="TheoKit" width={72} height={72} className="hero-logo" />
|
|
91
|
+
<h1>TheoKit</h1>
|
|
92
|
+
<p className="tagline">Build the app your agent lives in.</p>
|
|
93
|
+
<nav className="ctas">
|
|
94
|
+
<a href="https://usetheo.dev" target="_blank" rel="noopener noreferrer" className="btn primary">
|
|
95
|
+
Get Started
|
|
96
|
+
</a>
|
|
97
|
+
<a href="https://github.com/usetheodev/theokit" target="_blank" rel="noopener noreferrer" className="btn secondary">
|
|
98
|
+
Documentation
|
|
99
|
+
</a>
|
|
100
|
+
</nav>
|
|
101
|
+
<p className="hint">
|
|
102
|
+
Edit <code>app/page.tsx</code> to get started.
|
|
103
|
+
</p>
|
|
104
|
+
</header>
|
|
105
|
+
|
|
106
|
+
{/* Role */}
|
|
107
|
+
<div className="role-bar">
|
|
108
|
+
<label htmlFor="role">Role:</label>
|
|
109
|
+
<select id="role" value={role} onChange={e => setRole(e.target.value as Role)}>
|
|
110
|
+
<option value="">None (public)</option>
|
|
111
|
+
<option value="user">User</option>
|
|
112
|
+
<option value="admin">Admin</option>
|
|
113
|
+
</select>
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
{/* Content */}
|
|
117
|
+
<div className="grid">
|
|
118
|
+
<section className="card">
|
|
119
|
+
<h2>Tasks <span className="badge">@Controller</span></h2>
|
|
120
|
+
<table>
|
|
121
|
+
<thead><tr><th>Task</th><th>Priority</th><th>Status</th></tr></thead>
|
|
122
|
+
<tbody>
|
|
123
|
+
{tasks.map(t => (
|
|
124
|
+
<tr key={t.id} className={t.done ? 'done' : ''}>
|
|
125
|
+
<td>{t.done ? '✅ ' : '○ '}{t.title}</td>
|
|
126
|
+
<td><span className={`prio prio-${t.priority}`}>{t.priority}</span></td>
|
|
127
|
+
<td>{t.done ? 'Done' : 'To do'}</td>
|
|
128
|
+
</tr>
|
|
129
|
+
))}
|
|
130
|
+
</tbody>
|
|
131
|
+
</table>
|
|
132
|
+
<form onSubmit={createTask} className="create-bar">
|
|
133
|
+
<input value={title} onChange={e => setTitle(e.target.value)} placeholder="New task..." required minLength={3} />
|
|
134
|
+
<select value={priority} onChange={e => setPriority(e.target.value as Task['priority'])}>
|
|
135
|
+
<option value="medium">Medium</option>
|
|
136
|
+
<option value="high">High</option>
|
|
137
|
+
<option value="low">Low</option>
|
|
138
|
+
</select>
|
|
139
|
+
<button type="submit">Add</button>
|
|
140
|
+
</form>
|
|
141
|
+
{formError && <p className="error">{formError}</p>}
|
|
142
|
+
</section>
|
|
143
|
+
|
|
144
|
+
<section className="card">
|
|
145
|
+
<h2>AI Assistant <span className="badge badge-ai">@Agent + SSE</span></h2>
|
|
146
|
+
<div ref={chatRef} className="chat-box">
|
|
147
|
+
{chat.map((m, i) => (
|
|
148
|
+
<div key={i} className={`msg ${m.role}`}>{m.role === 'user' ? `You: ${m.text}` : m.text}</div>
|
|
151
149
|
))}
|
|
152
|
-
</
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
</
|
|
163
|
-
{formError && <p className="error">{formError}</p>}
|
|
164
|
-
</section>
|
|
165
|
-
|
|
166
|
-
{/* AI Chat */}
|
|
167
|
-
<section className="card">
|
|
168
|
-
<h2>AI Assistant <span className="badge badge-ai">@Agent + SSE</span></h2>
|
|
169
|
-
<div ref={chatRef} className="chat-box">
|
|
170
|
-
{chat.map((m, i) => (
|
|
171
|
-
<div key={i} className={`msg ${m.role}`}>{m.role === 'user' ? `You: ${m.text}` : m.text}</div>
|
|
172
|
-
))}
|
|
173
|
-
</div>
|
|
174
|
-
<div className="chat-bar">
|
|
175
|
-
<input
|
|
176
|
-
value={chatInput}
|
|
177
|
-
onChange={e => setChatInput(e.target.value)}
|
|
178
|
-
onKeyDown={e => e.key === 'Enter' && sendChat()}
|
|
179
|
-
placeholder="Message the AI assistant..."
|
|
180
|
-
disabled={chatBusy}
|
|
181
|
-
/>
|
|
182
|
-
<button type="button" onClick={sendChat} disabled={chatBusy}>Send</button>
|
|
183
|
-
</div>
|
|
184
|
-
</section>
|
|
185
|
-
</main>
|
|
186
|
-
|
|
187
|
-
{/* Footer */}
|
|
188
|
-
<footer className="footer">
|
|
189
|
-
<p>
|
|
190
|
-
Powered by{' '}
|
|
191
|
-
<a href="https://usetheo.dev" target="_blank" rel="noopener noreferrer">TheoKit</a>
|
|
150
|
+
</div>
|
|
151
|
+
<div className="chat-bar">
|
|
152
|
+
<input value={chatInput} onChange={e => setChatInput(e.target.value)} onKeyDown={e => e.key === 'Enter' && sendChat()} placeholder="Message the AI assistant..." disabled={chatBusy} />
|
|
153
|
+
<button type="button" onClick={sendChat} disabled={chatBusy}>Send</button>
|
|
154
|
+
</div>
|
|
155
|
+
</section>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
{/* Footer */}
|
|
159
|
+
<footer className="footer">
|
|
160
|
+
Powered by <a href="https://usetheo.dev" target="_blank" rel="noopener noreferrer">TheoKit</a>
|
|
192
161
|
{' · '}
|
|
193
162
|
<a href="https://github.com/usetheodev/theokit" target="_blank" rel="noopener noreferrer">GitHub</a>
|
|
194
163
|
{' · '}
|
|
195
164
|
<a href="https://discord.usetheo.dev" target="_blank" rel="noopener noreferrer">Discord</a>
|
|
196
|
-
</
|
|
197
|
-
</
|
|
198
|
-
|
|
165
|
+
</footer>
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
199
168
|
)
|
|
200
169
|
}
|