@lantos1618/better-ui 0.1.0
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/LICENSE +21 -0
- package/README.md +190 -0
- package/lib/aui/README.md +136 -0
- package/lib/aui/__tests__/aui-complete.test.ts +251 -0
- package/lib/aui/__tests__/aui-comprehensive.test.ts +376 -0
- package/lib/aui/__tests__/aui-concise.test.ts +278 -0
- package/lib/aui/__tests__/aui-integration.test.ts +309 -0
- package/lib/aui/__tests__/aui-simple.test.ts +116 -0
- package/lib/aui/__tests__/aui.test.ts +269 -0
- package/lib/aui/__tests__/concise-api.test.ts +165 -0
- package/lib/aui/__tests__/core.test.ts +265 -0
- package/lib/aui/__tests__/simple-api.test.ts +200 -0
- package/lib/aui/ai-assistant.ts +408 -0
- package/lib/aui/ai-control.ts +353 -0
- package/lib/aui/client/use-aui.ts +55 -0
- package/lib/aui/client-control.ts +551 -0
- package/lib/aui/client-executor.ts +417 -0
- package/lib/aui/components/ToolRenderer.tsx +22 -0
- package/lib/aui/core.ts +137 -0
- package/lib/aui/demo.tsx +89 -0
- package/lib/aui/examples/ai-complete-demo.tsx +359 -0
- package/lib/aui/examples/ai-control-demo.tsx +356 -0
- package/lib/aui/examples/ai-control-tools.ts +308 -0
- package/lib/aui/examples/concise-api.tsx +153 -0
- package/lib/aui/examples/index.tsx +163 -0
- package/lib/aui/examples/quick-demo.tsx +91 -0
- package/lib/aui/examples/simple-demo.tsx +71 -0
- package/lib/aui/examples/simple-tools.tsx +160 -0
- package/lib/aui/examples/user-api.tsx +208 -0
- package/lib/aui/examples/user-requested.tsx +174 -0
- package/lib/aui/examples/weather-search-tools.tsx +119 -0
- package/lib/aui/examples.tsx +367 -0
- package/lib/aui/hooks/useAUITool.ts +142 -0
- package/lib/aui/hooks/useAUIToolEnhanced.ts +343 -0
- package/lib/aui/hooks/useAUITools.ts +195 -0
- package/lib/aui/index.ts +156 -0
- package/lib/aui/provider.tsx +45 -0
- package/lib/aui/server-control.ts +386 -0
- package/lib/aui/server-executor.ts +165 -0
- package/lib/aui/server.ts +167 -0
- package/lib/aui/tool-registry.ts +380 -0
- package/lib/aui/tools/advanced-examples.tsx +86 -0
- package/lib/aui/tools/ai-complete.ts +375 -0
- package/lib/aui/tools/api-tools.tsx +230 -0
- package/lib/aui/tools/data-tools.tsx +232 -0
- package/lib/aui/tools/dom-tools.tsx +202 -0
- package/lib/aui/tools/examples.ts +43 -0
- package/lib/aui/tools/file-tools.tsx +202 -0
- package/lib/aui/tools/form-tools.tsx +233 -0
- package/lib/aui/tools/index.ts +8 -0
- package/lib/aui/tools/navigation-tools.tsx +172 -0
- package/lib/aui/tools/notification-tools.ts +213 -0
- package/lib/aui/tools/state-tools.tsx +209 -0
- package/lib/aui/types.ts +47 -0
- package/lib/aui/vercel-ai.ts +100 -0
- package/package.json +51 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import aui from '../index';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { aiControlSystem } from '../ai-control';
|
|
5
|
+
import { clientControlSystem } from '../client-control';
|
|
6
|
+
|
|
7
|
+
// Simple weather tool - server execution
|
|
8
|
+
const weatherTool = aui
|
|
9
|
+
.tool('weather')
|
|
10
|
+
.input(z.object({ city: z.string() }))
|
|
11
|
+
.execute(async ({ input }) => {
|
|
12
|
+
// Simulated API call
|
|
13
|
+
const temps = { 'New York': 72, 'London': 65, 'Tokyo': 78 };
|
|
14
|
+
return {
|
|
15
|
+
temp: temps[input.city as keyof typeof temps] || 70,
|
|
16
|
+
city: input.city,
|
|
17
|
+
conditions: 'Partly cloudy'
|
|
18
|
+
};
|
|
19
|
+
})
|
|
20
|
+
.render(({ data }) => (
|
|
21
|
+
<div className="weather-widget p-4 bg-blue-50 rounded">
|
|
22
|
+
<h3>{data.city}</h3>
|
|
23
|
+
<p className="text-2xl">{data.temp}°F</p>
|
|
24
|
+
<p>{data.conditions}</p>
|
|
25
|
+
</div>
|
|
26
|
+
));
|
|
27
|
+
|
|
28
|
+
// Complex search tool with client caching
|
|
29
|
+
const searchTool = aui
|
|
30
|
+
.tool('search')
|
|
31
|
+
.input(z.object({
|
|
32
|
+
query: z.string(),
|
|
33
|
+
limit: z.number().default(10)
|
|
34
|
+
}))
|
|
35
|
+
.execute(async ({ input }) => {
|
|
36
|
+
// Server-side database search
|
|
37
|
+
const results = await fetch('/api/search', {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
body: JSON.stringify(input)
|
|
40
|
+
}).then(r => r.json());
|
|
41
|
+
return results;
|
|
42
|
+
})
|
|
43
|
+
.clientExecute(async ({ input, ctx }) => {
|
|
44
|
+
// Check cache first
|
|
45
|
+
const cacheKey = `search:${input.query}:${input.limit}`;
|
|
46
|
+
const cached = ctx.cache.get(cacheKey);
|
|
47
|
+
|
|
48
|
+
if (cached && Date.now() - cached.timestamp < 60000) {
|
|
49
|
+
return cached.data;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Fetch from server
|
|
53
|
+
const data = await ctx.fetch('/api/tools/search', {
|
|
54
|
+
method: 'POST',
|
|
55
|
+
headers: { 'Content-Type': 'application/json' },
|
|
56
|
+
body: JSON.stringify(input)
|
|
57
|
+
}).then(r => r.json());
|
|
58
|
+
|
|
59
|
+
// Cache the result
|
|
60
|
+
ctx.cache.set(cacheKey, { data, timestamp: Date.now() });
|
|
61
|
+
return data;
|
|
62
|
+
})
|
|
63
|
+
.render(({ data, loading }) => (
|
|
64
|
+
<div className="search-results">
|
|
65
|
+
{loading ? (
|
|
66
|
+
<div>Searching...</div>
|
|
67
|
+
) : (
|
|
68
|
+
<ul>
|
|
69
|
+
{data?.results?.map((item: any, i: number) => (
|
|
70
|
+
<li key={i}>{item.title}</li>
|
|
71
|
+
))}
|
|
72
|
+
</ul>
|
|
73
|
+
)}
|
|
74
|
+
</div>
|
|
75
|
+
));
|
|
76
|
+
|
|
77
|
+
// AI-controlled form automation
|
|
78
|
+
const formAutomationTool = aui
|
|
79
|
+
.tool('form-automation')
|
|
80
|
+
.input(z.object({
|
|
81
|
+
formId: z.string(),
|
|
82
|
+
fields: z.record(z.any()),
|
|
83
|
+
autoSubmit: z.boolean().default(false)
|
|
84
|
+
}))
|
|
85
|
+
.clientExecute(async ({ input }) => {
|
|
86
|
+
const form = document.getElementById(input.formId) as HTMLFormElement;
|
|
87
|
+
if (!form) throw new Error('Form not found');
|
|
88
|
+
|
|
89
|
+
// Fill form fields
|
|
90
|
+
Object.entries(input.fields).forEach(([name, value]) => {
|
|
91
|
+
const field = form.elements.namedItem(name) as HTMLInputElement;
|
|
92
|
+
if (field) {
|
|
93
|
+
field.value = String(value);
|
|
94
|
+
field.dispatchEvent(new Event('input', { bubbles: true }));
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Auto-submit if requested
|
|
99
|
+
if (input.autoSubmit) {
|
|
100
|
+
form.submit();
|
|
101
|
+
return { submitted: true, fields: input.fields };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return { filled: true, fields: input.fields };
|
|
105
|
+
})
|
|
106
|
+
.render(({ data }) => (
|
|
107
|
+
<div className="p-2 bg-green-50 rounded">
|
|
108
|
+
{data.submitted ? 'Form submitted' : 'Form filled'}
|
|
109
|
+
<pre>{JSON.stringify(data.fields, null, 2)}</pre>
|
|
110
|
+
</div>
|
|
111
|
+
));
|
|
112
|
+
|
|
113
|
+
// Data visualization tool
|
|
114
|
+
const dataVisualizationTool = aui
|
|
115
|
+
.tool('data-viz')
|
|
116
|
+
.input(z.object({
|
|
117
|
+
type: z.enum(['chart', 'table', 'metrics']),
|
|
118
|
+
data: z.array(z.record(z.any())),
|
|
119
|
+
config: z.object({
|
|
120
|
+
title: z.string().optional(),
|
|
121
|
+
xAxis: z.string().optional(),
|
|
122
|
+
yAxis: z.string().optional()
|
|
123
|
+
}).optional()
|
|
124
|
+
}))
|
|
125
|
+
.execute(async ({ input }) => {
|
|
126
|
+
// Process data on server
|
|
127
|
+
return {
|
|
128
|
+
type: input.type,
|
|
129
|
+
processedData: input.data,
|
|
130
|
+
config: input.config
|
|
131
|
+
};
|
|
132
|
+
})
|
|
133
|
+
.render(({ data }) => {
|
|
134
|
+
switch (data.type) {
|
|
135
|
+
case 'metrics':
|
|
136
|
+
return (
|
|
137
|
+
<div className="metrics-grid grid grid-cols-3 gap-4">
|
|
138
|
+
{data.processedData.map((metric: any, i: number) => (
|
|
139
|
+
<div key={i} className="metric-card p-4 bg-gray-50 rounded">
|
|
140
|
+
<h4>{metric.label}</h4>
|
|
141
|
+
<p className="text-2xl font-bold">{metric.value}</p>
|
|
142
|
+
</div>
|
|
143
|
+
))}
|
|
144
|
+
</div>
|
|
145
|
+
);
|
|
146
|
+
case 'table':
|
|
147
|
+
return (
|
|
148
|
+
<table className="data-table w-full">
|
|
149
|
+
<thead>
|
|
150
|
+
<tr>
|
|
151
|
+
{Object.keys(data.processedData[0] || {}).map(key => (
|
|
152
|
+
<th key={key}>{key}</th>
|
|
153
|
+
))}
|
|
154
|
+
</tr>
|
|
155
|
+
</thead>
|
|
156
|
+
<tbody>
|
|
157
|
+
{data.processedData.map((row: any, i: number) => (
|
|
158
|
+
<tr key={i}>
|
|
159
|
+
{Object.values(row).map((val: any, j: number) => (
|
|
160
|
+
<td key={j}>{String(val)}</td>
|
|
161
|
+
))}
|
|
162
|
+
</tr>
|
|
163
|
+
))}
|
|
164
|
+
</tbody>
|
|
165
|
+
</table>
|
|
166
|
+
);
|
|
167
|
+
default:
|
|
168
|
+
return <div>Chart visualization placeholder</div>;
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Backend control tool for database operations
|
|
173
|
+
const databaseTool = aui
|
|
174
|
+
.tool('database')
|
|
175
|
+
.input(z.object({
|
|
176
|
+
operation: z.enum(['query', 'insert', 'update', 'delete']),
|
|
177
|
+
table: z.string(),
|
|
178
|
+
data: z.any().optional(),
|
|
179
|
+
where: z.record(z.any()).optional(),
|
|
180
|
+
select: z.array(z.string()).optional()
|
|
181
|
+
}))
|
|
182
|
+
.execute(async ({ input, ctx }) => {
|
|
183
|
+
// This would connect to your actual database
|
|
184
|
+
// For demo, returning mock data
|
|
185
|
+
switch (input.operation) {
|
|
186
|
+
case 'query':
|
|
187
|
+
return {
|
|
188
|
+
rows: [
|
|
189
|
+
{ id: 1, name: 'Item 1', created: new Date().toISOString() },
|
|
190
|
+
{ id: 2, name: 'Item 2', created: new Date().toISOString() }
|
|
191
|
+
],
|
|
192
|
+
count: 2
|
|
193
|
+
};
|
|
194
|
+
case 'insert':
|
|
195
|
+
return { id: Math.random(), inserted: true };
|
|
196
|
+
case 'update':
|
|
197
|
+
return { updated: 1 };
|
|
198
|
+
case 'delete':
|
|
199
|
+
return { deleted: 1 };
|
|
200
|
+
default:
|
|
201
|
+
throw new Error('Unknown operation');
|
|
202
|
+
}
|
|
203
|
+
})
|
|
204
|
+
.render(({ data }) => (
|
|
205
|
+
<div className="database-result">
|
|
206
|
+
<pre>{JSON.stringify(data, null, 2)}</pre>
|
|
207
|
+
</div>
|
|
208
|
+
));
|
|
209
|
+
|
|
210
|
+
// File operations tool (server-side only)
|
|
211
|
+
const fileOperationsTool = aui
|
|
212
|
+
.tool('file-ops')
|
|
213
|
+
.input(z.object({
|
|
214
|
+
action: z.enum(['read', 'write', 'list', 'delete']),
|
|
215
|
+
path: z.string(),
|
|
216
|
+
content: z.string().optional(),
|
|
217
|
+
encoding: z.enum(['utf8', 'base64']).default('utf8')
|
|
218
|
+
}))
|
|
219
|
+
.execute(async ({ input, ctx }) => {
|
|
220
|
+
// Only allow on server
|
|
221
|
+
if (!ctx?.isServer) {
|
|
222
|
+
throw new Error('File operations only available on server');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Mock file operations
|
|
226
|
+
switch (input.action) {
|
|
227
|
+
case 'read':
|
|
228
|
+
return { content: 'File content here', path: input.path };
|
|
229
|
+
case 'write':
|
|
230
|
+
return { written: true, path: input.path, size: input.content?.length };
|
|
231
|
+
case 'list':
|
|
232
|
+
return { files: ['file1.txt', 'file2.json'], path: input.path };
|
|
233
|
+
case 'delete':
|
|
234
|
+
return { deleted: true, path: input.path };
|
|
235
|
+
default:
|
|
236
|
+
throw new Error('Unknown action');
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// AI Control Demo Component
|
|
241
|
+
export function AIControlDemo() {
|
|
242
|
+
const [results, setResults] = React.useState<any[]>([]);
|
|
243
|
+
|
|
244
|
+
const executeAICommand = async (command: string) => {
|
|
245
|
+
// Parse AI command and execute appropriate tool
|
|
246
|
+
const lowerCommand = command.toLowerCase();
|
|
247
|
+
|
|
248
|
+
try {
|
|
249
|
+
let result: any;
|
|
250
|
+
|
|
251
|
+
if (lowerCommand.includes('weather')) {
|
|
252
|
+
const city = command.match(/weather (?:in|for) (.+)/i)?.[1] || 'New York';
|
|
253
|
+
result = await aui.execute('weather', { city });
|
|
254
|
+
} else if (lowerCommand.includes('search')) {
|
|
255
|
+
const query = command.match(/search (?:for )?"(.+)"/i)?.[1] || command;
|
|
256
|
+
result = await aui.execute('search', { query, limit: 5 });
|
|
257
|
+
} else if (lowerCommand.includes('fill form')) {
|
|
258
|
+
result = await aui.execute('form-automation', {
|
|
259
|
+
formId: 'demo-form',
|
|
260
|
+
fields: { name: 'AI User', email: 'ai@example.com' },
|
|
261
|
+
autoSubmit: false
|
|
262
|
+
});
|
|
263
|
+
} else if (lowerCommand.includes('database')) {
|
|
264
|
+
result = await aui.execute('database', {
|
|
265
|
+
operation: 'query',
|
|
266
|
+
table: 'users',
|
|
267
|
+
select: ['id', 'name', 'created']
|
|
268
|
+
});
|
|
269
|
+
} else if (lowerCommand.includes('visualize')) {
|
|
270
|
+
result = await aui.execute('data-viz', {
|
|
271
|
+
type: 'metrics',
|
|
272
|
+
data: [
|
|
273
|
+
{ label: 'Users', value: 1234 },
|
|
274
|
+
{ label: 'Sessions', value: 5678 },
|
|
275
|
+
{ label: 'Revenue', value: '$9,012' }
|
|
276
|
+
]
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
setResults(prev => [...prev, { command, result, timestamp: new Date() }]);
|
|
281
|
+
} catch (error) {
|
|
282
|
+
setResults(prev => [...prev, {
|
|
283
|
+
command,
|
|
284
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
285
|
+
timestamp: new Date()
|
|
286
|
+
}]);
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
return (
|
|
291
|
+
<div className="ai-control-demo p-6">
|
|
292
|
+
<h2 className="text-2xl font-bold mb-4">AI Control System Demo</h2>
|
|
293
|
+
|
|
294
|
+
<div className="command-examples mb-4 p-4 bg-gray-50 rounded">
|
|
295
|
+
<h3 className="font-semibold mb-2">Try these AI commands:</h3>
|
|
296
|
+
<ul className="list-disc list-inside space-y-1">
|
|
297
|
+
<li>Get weather in Tokyo</li>
|
|
298
|
+
<li>Search for "machine learning"</li>
|
|
299
|
+
<li>Fill form with test data</li>
|
|
300
|
+
<li>Query database users table</li>
|
|
301
|
+
<li>Visualize metrics dashboard</li>
|
|
302
|
+
</ul>
|
|
303
|
+
</div>
|
|
304
|
+
|
|
305
|
+
<div className="command-input mb-4">
|
|
306
|
+
<input
|
|
307
|
+
type="text"
|
|
308
|
+
className="w-full p-2 border rounded"
|
|
309
|
+
placeholder="Enter AI command..."
|
|
310
|
+
onKeyPress={(e) => {
|
|
311
|
+
if (e.key === 'Enter') {
|
|
312
|
+
executeAICommand((e.target as HTMLInputElement).value);
|
|
313
|
+
(e.target as HTMLInputElement).value = '';
|
|
314
|
+
}
|
|
315
|
+
}}
|
|
316
|
+
/>
|
|
317
|
+
</div>
|
|
318
|
+
|
|
319
|
+
<div className="results space-y-4">
|
|
320
|
+
{results.map((r, i) => (
|
|
321
|
+
<div key={i} className="result-item p-4 border rounded">
|
|
322
|
+
<div className="font-mono text-sm text-gray-600 mb-2">
|
|
323
|
+
{r.timestamp.toLocaleTimeString()} - {r.command}
|
|
324
|
+
</div>
|
|
325
|
+
{r.error ? (
|
|
326
|
+
<div className="text-red-600">Error: {r.error}</div>
|
|
327
|
+
) : (
|
|
328
|
+
<div className="result-content">
|
|
329
|
+
<pre>{JSON.stringify(r.result, null, 2)}</pre>
|
|
330
|
+
</div>
|
|
331
|
+
)}
|
|
332
|
+
</div>
|
|
333
|
+
))}
|
|
334
|
+
</div>
|
|
335
|
+
|
|
336
|
+
<form id="demo-form" className="hidden">
|
|
337
|
+
<input name="name" type="text" />
|
|
338
|
+
<input name="email" type="email" />
|
|
339
|
+
</form>
|
|
340
|
+
</div>
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Export all tools for registration
|
|
345
|
+
export const aiCompleteDemoTools = {
|
|
346
|
+
weatherTool,
|
|
347
|
+
searchTool,
|
|
348
|
+
formAutomationTool,
|
|
349
|
+
dataVisualizationTool,
|
|
350
|
+
databaseTool,
|
|
351
|
+
fileOperationsTool
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
// Register all tools with AI control system
|
|
355
|
+
Object.values(aiCompleteDemoTools).forEach(tool => {
|
|
356
|
+
aiControlSystem.register(tool as any);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
export default AIControlDemo;
|