@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.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +190 -0
  3. package/lib/aui/README.md +136 -0
  4. package/lib/aui/__tests__/aui-complete.test.ts +251 -0
  5. package/lib/aui/__tests__/aui-comprehensive.test.ts +376 -0
  6. package/lib/aui/__tests__/aui-concise.test.ts +278 -0
  7. package/lib/aui/__tests__/aui-integration.test.ts +309 -0
  8. package/lib/aui/__tests__/aui-simple.test.ts +116 -0
  9. package/lib/aui/__tests__/aui.test.ts +269 -0
  10. package/lib/aui/__tests__/concise-api.test.ts +165 -0
  11. package/lib/aui/__tests__/core.test.ts +265 -0
  12. package/lib/aui/__tests__/simple-api.test.ts +200 -0
  13. package/lib/aui/ai-assistant.ts +408 -0
  14. package/lib/aui/ai-control.ts +353 -0
  15. package/lib/aui/client/use-aui.ts +55 -0
  16. package/lib/aui/client-control.ts +551 -0
  17. package/lib/aui/client-executor.ts +417 -0
  18. package/lib/aui/components/ToolRenderer.tsx +22 -0
  19. package/lib/aui/core.ts +137 -0
  20. package/lib/aui/demo.tsx +89 -0
  21. package/lib/aui/examples/ai-complete-demo.tsx +359 -0
  22. package/lib/aui/examples/ai-control-demo.tsx +356 -0
  23. package/lib/aui/examples/ai-control-tools.ts +308 -0
  24. package/lib/aui/examples/concise-api.tsx +153 -0
  25. package/lib/aui/examples/index.tsx +163 -0
  26. package/lib/aui/examples/quick-demo.tsx +91 -0
  27. package/lib/aui/examples/simple-demo.tsx +71 -0
  28. package/lib/aui/examples/simple-tools.tsx +160 -0
  29. package/lib/aui/examples/user-api.tsx +208 -0
  30. package/lib/aui/examples/user-requested.tsx +174 -0
  31. package/lib/aui/examples/weather-search-tools.tsx +119 -0
  32. package/lib/aui/examples.tsx +367 -0
  33. package/lib/aui/hooks/useAUITool.ts +142 -0
  34. package/lib/aui/hooks/useAUIToolEnhanced.ts +343 -0
  35. package/lib/aui/hooks/useAUITools.ts +195 -0
  36. package/lib/aui/index.ts +156 -0
  37. package/lib/aui/provider.tsx +45 -0
  38. package/lib/aui/server-control.ts +386 -0
  39. package/lib/aui/server-executor.ts +165 -0
  40. package/lib/aui/server.ts +167 -0
  41. package/lib/aui/tool-registry.ts +380 -0
  42. package/lib/aui/tools/advanced-examples.tsx +86 -0
  43. package/lib/aui/tools/ai-complete.ts +375 -0
  44. package/lib/aui/tools/api-tools.tsx +230 -0
  45. package/lib/aui/tools/data-tools.tsx +232 -0
  46. package/lib/aui/tools/dom-tools.tsx +202 -0
  47. package/lib/aui/tools/examples.ts +43 -0
  48. package/lib/aui/tools/file-tools.tsx +202 -0
  49. package/lib/aui/tools/form-tools.tsx +233 -0
  50. package/lib/aui/tools/index.ts +8 -0
  51. package/lib/aui/tools/navigation-tools.tsx +172 -0
  52. package/lib/aui/tools/notification-tools.ts +213 -0
  53. package/lib/aui/tools/state-tools.tsx +209 -0
  54. package/lib/aui/types.ts +47 -0
  55. package/lib/aui/vercel-ai.ts +100 -0
  56. package/package.json +51 -0
@@ -0,0 +1,208 @@
1
+ 'use client';
2
+
3
+ import React from 'react';
4
+ import aui from '../index';
5
+ import { z } from 'zod';
6
+
7
+ // Simple tool - just 2 methods (exactly as user requested)
8
+ const simpleTool = aui
9
+ .tool('weather')
10
+ .input(z.object({ city: z.string() }))
11
+ .execute(async ({ input }) => ({ temp: 72, city: input.city }))
12
+ .render(({ data }) => <div>{data.city}: {data.temp}°</div>);
13
+
14
+ // Complex tool - adds client optimization (exactly as user requested)
15
+ const complexTool = aui
16
+ .tool('search')
17
+ .input(z.object({ query: z.string() }))
18
+ .execute(async ({ input }) => {
19
+ // Simulated database search
20
+ const results = await Promise.resolve([
21
+ `Result 1 for ${input.query}`,
22
+ `Result 2 for ${input.query}`,
23
+ `Result 3 for ${input.query}`
24
+ ]);
25
+ return { results, query: input.query };
26
+ })
27
+ .clientExecute(async ({ input, ctx }) => {
28
+ // Only when you need caching, offline, etc.
29
+ const cached = ctx.cache.get(input.query);
30
+ if (cached) {
31
+ console.log('Using cached result for:', input.query);
32
+ return cached;
33
+ }
34
+
35
+ const result = await ctx.fetch('/api/tools/search', {
36
+ method: 'POST',
37
+ headers: { 'Content-Type': 'application/json' },
38
+ body: JSON.stringify(input)
39
+ }).then(r => r.json()).catch(() => {
40
+ // Fallback for demo
41
+ return {
42
+ results: [`Client result for ${input.query}`],
43
+ query: input.query
44
+ };
45
+ });
46
+
47
+ ctx.cache.set(input.query, result);
48
+ return result;
49
+ })
50
+ .render(({ data }) => (
51
+ <div className="space-y-2">
52
+ <h3 className="font-semibold">Search Results for &quot;{data.query}&quot;</h3>
53
+ <ul className="list-disc pl-5">
54
+ {data.results.map((result, i) => (
55
+ <li key={i}>{result}</li>
56
+ ))}
57
+ </ul>
58
+ </div>
59
+ ));
60
+
61
+ // Additional examples showing the power of the API
62
+
63
+ // Tool with middleware for auth/logging
64
+ const protectedTool = aui
65
+ .tool('protected')
66
+ .input(z.object({ action: z.string() }))
67
+ .middleware(async ({ input, ctx, next }) => {
68
+ console.log('Logging action:', input.action);
69
+ if (!ctx.user) {
70
+ throw new Error('Authentication required');
71
+ }
72
+ return next();
73
+ })
74
+ .execute(async ({ input, ctx }) => ({
75
+ result: `Executed ${input.action} for user ${ctx?.user?.name || 'unknown'}`
76
+ }))
77
+ .render(({ data }) => <div className="text-green-600">{data.result}</div>);
78
+
79
+ // Tool with tags and description for AI discovery
80
+ const aiReadyTool = aui
81
+ .tool('database')
82
+ .input(z.object({
83
+ table: z.string(),
84
+ operation: z.enum(['read', 'write', 'delete'])
85
+ }))
86
+ .describe('Database operations tool for CRUD operations')
87
+ .tag('database', 'crud', 'ai-controlled')
88
+ .execute(async ({ input }) => ({
89
+ success: true,
90
+ operation: input.operation,
91
+ table: input.table,
92
+ result: `${input.operation} operation on ${input.table} completed`
93
+ }))
94
+ .render(({ data }) => (
95
+ <div className={`p-2 rounded ${data.success ? 'bg-green-100' : 'bg-red-100'}`}>
96
+ {data.result}
97
+ </div>
98
+ ));
99
+
100
+ // Export all tools
101
+ export { simpleTool, complexTool, protectedTool, aiReadyTool };
102
+
103
+ // Demo component showing usage
104
+ export function UserAPIDemo() {
105
+ const [results, setResults] = React.useState<Record<string, any>>({});
106
+ const [loading, setLoading] = React.useState<Record<string, boolean>>({});
107
+
108
+ const runTool = async (toolName: string, tool: any, input: any) => {
109
+ setLoading(prev => ({ ...prev, [toolName]: true }));
110
+ try {
111
+ const result = await tool.run(input, aui.createContext({
112
+ user: { name: 'Demo User' }
113
+ }));
114
+ setResults(prev => ({ ...prev, [toolName]: result }));
115
+ } catch (error) {
116
+ console.error(`Error running ${toolName}:`, error);
117
+ setResults(prev => ({ ...prev, [toolName]: { error: error instanceof Error ? error.message : 'Unknown error' } }));
118
+ } finally {
119
+ setLoading(prev => ({ ...prev, [toolName]: false }));
120
+ }
121
+ };
122
+
123
+ return (
124
+ <div className="p-6 space-y-6 max-w-4xl mx-auto">
125
+ <h1 className="text-3xl font-bold">AUI User API Demo</h1>
126
+
127
+ <div className="space-y-4">
128
+ <div className="border rounded-lg p-4">
129
+ <h2 className="text-xl font-semibold mb-2">Simple Tool</h2>
130
+ <p className="text-gray-600 mb-3">Just input → execute → render</p>
131
+ <button
132
+ onClick={() => runTool('weather', simpleTool, { city: 'San Francisco' })}
133
+ disabled={loading.weather}
134
+ className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 disabled:opacity-50"
135
+ >
136
+ {loading.weather ? 'Loading...' : 'Get Weather'}
137
+ </button>
138
+ {results.weather && simpleTool.renderer && (
139
+ <div className="mt-3">
140
+ {simpleTool.renderer({ data: results.weather })}
141
+ </div>
142
+ )}
143
+ </div>
144
+
145
+ <div className="border rounded-lg p-4">
146
+ <h2 className="text-xl font-semibold mb-2">Complex Tool</h2>
147
+ <p className="text-gray-600 mb-3">With client-side caching and optimization</p>
148
+ <button
149
+ onClick={() => runTool('search', complexTool, { query: 'AI tools' })}
150
+ disabled={loading.search}
151
+ className="px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600 disabled:opacity-50"
152
+ >
153
+ {loading.search ? 'Searching...' : 'Search'}
154
+ </button>
155
+ {results.search && complexTool.renderer && (
156
+ <div className="mt-3">
157
+ {complexTool.renderer({ data: results.search })}
158
+ </div>
159
+ )}
160
+ </div>
161
+
162
+ <div className="border rounded-lg p-4">
163
+ <h2 className="text-xl font-semibold mb-2">Protected Tool</h2>
164
+ <p className="text-gray-600 mb-3">With middleware for auth/logging</p>
165
+ <button
166
+ onClick={() => runTool('protected', protectedTool, { action: 'update-profile' })}
167
+ disabled={loading.protected}
168
+ className="px-4 py-2 bg-purple-500 text-white rounded hover:bg-purple-600 disabled:opacity-50"
169
+ >
170
+ {loading.protected ? 'Executing...' : 'Execute Protected Action'}
171
+ </button>
172
+ {results.protected && protectedTool.renderer && (
173
+ <div className="mt-3">
174
+ {protectedTool.renderer({ data: results.protected })}
175
+ </div>
176
+ )}
177
+ </div>
178
+
179
+ <div className="border rounded-lg p-4">
180
+ <h2 className="text-xl font-semibold mb-2">AI-Ready Tool</h2>
181
+ <p className="text-gray-600 mb-3">With tags and description for AI discovery</p>
182
+ <button
183
+ onClick={() => runTool('database', aiReadyTool, {
184
+ table: 'users',
185
+ operation: 'read'
186
+ })}
187
+ disabled={loading.database}
188
+ className="px-4 py-2 bg-indigo-500 text-white rounded hover:bg-indigo-600 disabled:opacity-50"
189
+ >
190
+ {loading.database ? 'Processing...' : 'Run Database Operation'}
191
+ </button>
192
+ {results.database && aiReadyTool.renderer && (
193
+ <div className="mt-3">
194
+ {aiReadyTool.renderer({ data: results.database })}
195
+ </div>
196
+ )}
197
+ </div>
198
+ </div>
199
+
200
+ <div className="border-t pt-4">
201
+ <h3 className="text-lg font-semibold mb-2">Tool Discovery</h3>
202
+ <p className="text-gray-600">
203
+ Tools registered: {aui.getToolNames().join(', ')}
204
+ </p>
205
+ </div>
206
+ </div>
207
+ );
208
+ }
@@ -0,0 +1,174 @@
1
+ import React from 'react';
2
+ import aui from '../index';
3
+ import { z } from 'zod';
4
+
5
+ // Simple tool - just 2 methods
6
+ const simpleTool = aui
7
+ .tool('weather')
8
+ .input(z.object({ city: z.string() }))
9
+ .execute(async ({ input }) => ({ temp: 72, city: input.city }))
10
+ .render(({ data }) => <div>{data.city}: {data.temp}°</div>);
11
+
12
+ // Complex tool - adds client optimization
13
+ const complexTool = aui
14
+ .tool('search')
15
+ .input(z.object({ query: z.string() }))
16
+ .execute(async ({ input }) => {
17
+ // Server-side database search
18
+ // In real app, this would be: db.search(input.query)
19
+ return [
20
+ { id: 1, title: `Result for ${input.query}` },
21
+ { id: 2, title: `Another result for ${input.query}` }
22
+ ];
23
+ })
24
+ .clientExecute(async ({ input, ctx }) => {
25
+ // Only when you need caching, offline, etc.
26
+ const cached = ctx.cache.get(input.query);
27
+ return cached || ctx.fetch('/api/tools/search', {
28
+ method: 'POST',
29
+ headers: { 'Content-Type': 'application/json' },
30
+ body: JSON.stringify(input)
31
+ }).then(r => r.json());
32
+ })
33
+ .render(({ data }) => (
34
+ <div className="space-y-2">
35
+ <h3 className="font-semibold">Search Results:</h3>
36
+ {data.map((item: any) => (
37
+ <div key={item.id} className="p-2 border rounded">
38
+ {item.title}
39
+ </div>
40
+ ))}
41
+ </div>
42
+ ));
43
+
44
+ // AI Control Examples - Frontend UI manipulation
45
+ const uiControlTool = aui
46
+ .tool('ui-control')
47
+ .input(z.object({
48
+ action: z.enum(['show', 'hide', 'toggle']),
49
+ element: z.string()
50
+ }))
51
+ .clientExecute(async ({ input }) => {
52
+ const element = document.querySelector(input.element);
53
+ if (!element) throw new Error(`Element ${input.element} not found`);
54
+
55
+ switch (input.action) {
56
+ case 'show':
57
+ (element as HTMLElement).style.display = 'block';
58
+ break;
59
+ case 'hide':
60
+ (element as HTMLElement).style.display = 'none';
61
+ break;
62
+ case 'toggle':
63
+ const el = element as HTMLElement;
64
+ el.style.display = el.style.display === 'none' ? 'block' : 'none';
65
+ break;
66
+ }
67
+
68
+ return { success: true, action: input.action, element: input.element };
69
+ })
70
+ .render(({ data }) => (
71
+ <div className="text-green-600">
72
+ ✓ {data.action} {data.element}
73
+ </div>
74
+ ));
75
+
76
+ // Backend control - Database operations
77
+ const databaseTool = aui
78
+ .tool('database')
79
+ .input(z.object({
80
+ operation: z.enum(['create', 'read', 'update', 'delete']),
81
+ table: z.string(),
82
+ data: z.any().optional()
83
+ }))
84
+ .execute(async ({ input }) => {
85
+ // This would connect to your actual database
86
+ // For demo, just return mock response
87
+ switch (input.operation) {
88
+ case 'create':
89
+ return { id: Math.random(), ...input.data };
90
+ case 'read':
91
+ return { id: 1, name: 'Example', table: input.table };
92
+ case 'update':
93
+ return { success: true, updated: input.data };
94
+ case 'delete':
95
+ return { success: true, deleted: input.data?.id };
96
+ default:
97
+ throw new Error('Unknown operation');
98
+ }
99
+ })
100
+ .render(({ data }) => (
101
+ <pre className="bg-gray-100 p-2 rounded">
102
+ {JSON.stringify(data, null, 2)}
103
+ </pre>
104
+ ));
105
+
106
+ // Form generation - AI can create forms dynamically
107
+ const formGeneratorTool = aui
108
+ .tool('form-generator')
109
+ .input(z.object({
110
+ fields: z.array(z.object({
111
+ name: z.string(),
112
+ type: z.enum(['text', 'number', 'email', 'select']),
113
+ label: z.string(),
114
+ required: z.boolean().optional(),
115
+ options: z.array(z.string()).optional()
116
+ }))
117
+ }))
118
+ .execute(async ({ input }) => input.fields)
119
+ .render(({ data }) => (
120
+ <form className="space-y-4">
121
+ {data.map(field => (
122
+ <div key={field.name}>
123
+ <label className="block text-sm font-medium mb-1">
124
+ {field.label}
125
+ {field.required && <span className="text-red-500">*</span>}
126
+ </label>
127
+ {field.type === 'select' ? (
128
+ <select className="w-full p-2 border rounded">
129
+ {field.options?.map(opt => (
130
+ <option key={opt} value={opt}>{opt}</option>
131
+ ))}
132
+ </select>
133
+ ) : (
134
+ <input
135
+ type={field.type}
136
+ name={field.name}
137
+ required={field.required}
138
+ className="w-full p-2 border rounded"
139
+ />
140
+ )}
141
+ </div>
142
+ ))}
143
+ <button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded">
144
+ Submit
145
+ </button>
146
+ </form>
147
+ ));
148
+
149
+ // Export all tools for use
150
+ export {
151
+ simpleTool,
152
+ complexTool,
153
+ uiControlTool,
154
+ databaseTool,
155
+ formGeneratorTool
156
+ };
157
+
158
+ // Example of using tools programmatically
159
+ export async function exampleUsage() {
160
+ // Execute a tool
161
+ const weatherResult = await simpleTool.run({ city: 'New York' });
162
+ console.log(weatherResult); // { temp: 72, city: 'New York' }
163
+
164
+ // Execute with context
165
+ const searchResult = await complexTool.run(
166
+ { query: 'typescript' },
167
+ {
168
+ cache: new Map(),
169
+ fetch: globalThis.fetch,
170
+ user: { id: 1, name: 'User' }
171
+ }
172
+ );
173
+ console.log(searchResult);
174
+ }
@@ -0,0 +1,119 @@
1
+ import React from 'react';
2
+ import { z } from 'zod';
3
+ import aui from '../index';
4
+
5
+ // Simple tool - just 2 methods (input, execute, render)
6
+ export const weatherTool = aui
7
+ .tool('weather')
8
+ .input(z.object({ city: z.string() }))
9
+ .execute(async ({ input }) => ({
10
+ temp: 72,
11
+ city: input.city,
12
+ conditions: 'Sunny',
13
+ humidity: 65
14
+ }))
15
+ .render(({ data }) => (
16
+ <div className="p-4 bg-blue-50 rounded-lg">
17
+ <h3 className="font-bold text-lg">{data.city}</h3>
18
+ <p className="text-2xl">{data.temp}°F</p>
19
+ <p className="text-gray-600">{data.conditions}</p>
20
+ <p className="text-sm">Humidity: {data.humidity}%</p>
21
+ </div>
22
+ ));
23
+
24
+ // Mock database search function
25
+ const mockDbSearch = async (query: string) => {
26
+ await new Promise(resolve => setTimeout(resolve, 100));
27
+ return [
28
+ { id: 1, title: `Result for "${query}"`, score: 0.95 },
29
+ { id: 2, title: `Another match for "${query}"`, score: 0.87 },
30
+ { id: 3, title: `Related to "${query}"`, score: 0.72 }
31
+ ];
32
+ };
33
+
34
+ // Complex tool - adds client optimization
35
+ export const searchTool = aui
36
+ .tool('search')
37
+ .input(z.object({ query: z.string() }))
38
+ .execute(async ({ input }) => mockDbSearch(input.query))
39
+ .clientExecute(async ({ input, ctx }) => {
40
+ // Only when you need caching, offline, etc.
41
+ const cacheKey = `search:${input.query}`;
42
+ const cached = ctx.cache.get(cacheKey);
43
+
44
+ if (cached) {
45
+ console.log(`Cache hit for query: ${input.query}`);
46
+ return cached;
47
+ }
48
+
49
+ console.log(`Cache miss for query: ${input.query}, fetching...`);
50
+ const results = await ctx.fetch('/api/tools/search', {
51
+ method: 'POST',
52
+ headers: { 'Content-Type': 'application/json' },
53
+ body: JSON.stringify(input)
54
+ }).then(res => res.json());
55
+
56
+ ctx.cache.set(cacheKey, results);
57
+ return results;
58
+ })
59
+ .render(({ data }) => (
60
+ <div className="p-4 bg-gray-50 rounded-lg">
61
+ <h3 className="font-bold mb-3">Search Results</h3>
62
+ <ul className="space-y-2">
63
+ {data.map((result: any) => (
64
+ <li key={result.id} className="bg-white p-2 rounded shadow-sm">
65
+ <div className="font-medium">{result.title}</div>
66
+ <div className="text-sm text-gray-500">Score: {result.score}</div>
67
+ </li>
68
+ ))}
69
+ </ul>
70
+ </div>
71
+ ));
72
+
73
+ // Demo component showing both tools in action
74
+ export function WeatherSearchDemo() {
75
+ const [weatherData, setWeatherData] = React.useState<any>(null);
76
+ const [searchResults, setSearchResults] = React.useState<any>(null);
77
+
78
+ const handleWeatherSearch = async () => {
79
+ const result = await weatherTool.run({ city: 'San Francisco' });
80
+ setWeatherData(result);
81
+ };
82
+
83
+ const handleSearch = async () => {
84
+ const results = await searchTool.run({ query: 'TypeScript patterns' });
85
+ setSearchResults(results);
86
+ };
87
+
88
+ return (
89
+ <div className="p-8 space-y-6">
90
+ <div>
91
+ <h2 className="text-2xl font-bold mb-4">AUI Tool Examples</h2>
92
+
93
+ <div className="space-y-4">
94
+ <div>
95
+ <button
96
+ onClick={handleWeatherSearch}
97
+ className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
98
+ >
99
+ Get Weather for San Francisco
100
+ </button>
101
+ {weatherData && weatherTool.renderer &&
102
+ weatherTool.renderer({ data: weatherData })}
103
+ </div>
104
+
105
+ <div>
106
+ <button
107
+ onClick={handleSearch}
108
+ className="px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600"
109
+ >
110
+ Search for TypeScript patterns
111
+ </button>
112
+ {searchResults && searchTool.renderer &&
113
+ searchTool.renderer({ data: searchResults })}
114
+ </div>
115
+ </div>
116
+ </div>
117
+ </div>
118
+ );
119
+ }