@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,167 @@
1
+ import { cookies, headers } from 'next/headers';
2
+ import aui, { AUITool, AUIContext, z } from './index';
3
+
4
+ // Server-only control system exports
5
+ export {
6
+ serverTools,
7
+ serverControlSystem,
8
+ createServerControlSystem,
9
+ type ServerControlContext
10
+ } from './server-control';
11
+
12
+ export interface ServerToolOptions {
13
+ requireAuth?: boolean;
14
+ rateLimit?: {
15
+ requests: number;
16
+ window: number; // in milliseconds
17
+ };
18
+ middleware?: (ctx: AUIContext) => Promise<void> | void;
19
+ }
20
+
21
+ /**
22
+ * Creates a server-side AUI context with Next.js integration
23
+ */
24
+ export async function createServerContext(
25
+ additions?: Partial<AUIContext>
26
+ ): Promise<AUIContext> {
27
+ try {
28
+ const cookieStore = await cookies();
29
+ const headersList = await headers();
30
+
31
+ return {
32
+ cache: new Map(),
33
+ fetch: globalThis.fetch,
34
+ isServer: true,
35
+ headers: Object.fromEntries(headersList.entries()),
36
+ cookies: Object.fromEntries(
37
+ cookieStore.getAll().map(c => [c.name, c.value])
38
+ ),
39
+ ...additions,
40
+ };
41
+ } catch {
42
+ // Fallback for non-Next.js environments
43
+ return {
44
+ cache: new Map(),
45
+ fetch: globalThis.fetch,
46
+ isServer: true,
47
+ ...additions,
48
+ };
49
+ }
50
+ }
51
+
52
+ export function createServerTool<TInput = any, TOutput = any>(
53
+ name: string,
54
+ schema: z.ZodType<TInput>,
55
+ handler: (params: { input: TInput; ctx?: AUIContext }) => Promise<TOutput> | TOutput,
56
+ options?: ServerToolOptions
57
+ ): AUITool<TInput, TOutput> {
58
+ const tool = aui
59
+ .tool(name)
60
+ .input(schema)
61
+ .execute(async ({ input, ctx }) => {
62
+ // Apply middleware
63
+ if (options?.middleware) {
64
+ await options.middleware(ctx!);
65
+ }
66
+
67
+ // Check auth if required
68
+ if (options?.requireAuth && !ctx?.user) {
69
+ throw new Error('Authentication required');
70
+ }
71
+
72
+ // Execute handler
73
+ return await handler({ input, ctx });
74
+ });
75
+
76
+ return tool;
77
+ }
78
+
79
+ export async function executeServerTool<TInput = any, TOutput = any>(
80
+ toolName: string,
81
+ input: TInput,
82
+ contextAdditions?: Partial<AUIContext>
83
+ ): Promise<TOutput> {
84
+ const ctx = await createServerContext(contextAdditions);
85
+ return await aui.execute(toolName, input, ctx);
86
+ }
87
+
88
+ /**
89
+ * Server action wrapper for tools
90
+ */
91
+ export function createServerAction<TInput, TOutput>(
92
+ tool: AUITool<TInput, TOutput>
93
+ ) {
94
+ return async (input: TInput): Promise<TOutput> => {
95
+ 'use server';
96
+ const ctx = await createServerContext();
97
+ return tool.run(input, ctx);
98
+ };
99
+ }
100
+
101
+ /**
102
+ * Batch execute multiple tools on the server
103
+ */
104
+ export async function batchExecute(
105
+ executions: Array<{ tool: string; input: any }>,
106
+ contextAdditions?: Partial<AUIContext>
107
+ ): Promise<Array<{ tool: string; result?: any; error?: string }>> {
108
+ const ctx = await createServerContext(contextAdditions);
109
+
110
+ const results = await Promise.allSettled(
111
+ executions.map(exec => aui.execute(exec.tool, exec.input, ctx))
112
+ );
113
+
114
+ return results.map((result, index) => ({
115
+ tool: executions[index].tool,
116
+ ...(result.status === 'fulfilled'
117
+ ? { result: result.value }
118
+ : { error: result.reason?.message || 'Unknown error' }),
119
+ }));
120
+ }
121
+
122
+ export function registerServerTools(tools: AUITool[]) {
123
+ tools.forEach(tool => {
124
+ if (!aui.has(tool.name)) {
125
+ aui.get(tool.name); // This will register the tool
126
+ }
127
+ });
128
+ }
129
+
130
+ export async function handleToolRequest(
131
+ request: Request
132
+ ): Promise<Response> {
133
+ try {
134
+ const { tool: toolName, input, context } = await request.json();
135
+
136
+ const tool = aui.get(toolName);
137
+ if (!tool) {
138
+ return new Response(
139
+ JSON.stringify({ error: `Tool "${toolName}" not found` }),
140
+ {
141
+ status: 404,
142
+ headers: { 'Content-Type': 'application/json' }
143
+ }
144
+ );
145
+ }
146
+
147
+ const ctx = await createServerContext(context);
148
+ const result = await tool.run(input, ctx);
149
+
150
+ return new Response(
151
+ JSON.stringify({ success: true, data: result }),
152
+ {
153
+ status: 200,
154
+ headers: { 'Content-Type': 'application/json' }
155
+ }
156
+ );
157
+ } catch (error) {
158
+ const message = error instanceof Error ? error.message : 'Unknown error';
159
+ return new Response(
160
+ JSON.stringify({ error: message }),
161
+ {
162
+ status: 400,
163
+ headers: { 'Content-Type': 'application/json' }
164
+ }
165
+ );
166
+ }
167
+ }
@@ -0,0 +1,380 @@
1
+ import { z } from 'zod';
2
+ import { AUITool, AUIContext } from './core';
3
+ import { AIControlledTool } from './ai-control';
4
+ import { clientTools, clientControlSystem } from './client-control';
5
+
6
+ export interface ToolMetadata {
7
+ name: string;
8
+ description?: string;
9
+ tags: string[];
10
+ category: 'server' | 'client' | 'hybrid' | 'custom';
11
+ permissions?: string[];
12
+ schema?: z.ZodType<any>;
13
+ examples?: Array<{
14
+ description: string;
15
+ input: any;
16
+ expectedOutput?: any;
17
+ }>;
18
+ }
19
+
20
+ export class ToolRegistry {
21
+ private tools = new Map<string, AUITool | AIControlledTool>();
22
+ private metadata = new Map<string, ToolMetadata>();
23
+ private categories = new Map<string, Set<string>>();
24
+ private tagIndex = new Map<string, Set<string>>();
25
+
26
+ constructor() {
27
+ this.initializeBuiltInTools();
28
+ }
29
+
30
+ private initializeBuiltInTools() {
31
+ // Only initialize client tools by default
32
+ // Server tools should be registered separately in server-side code
33
+ Object.entries(clientTools).forEach(([key, tool]) => {
34
+ this.register(tool, {
35
+ name: tool.name,
36
+ description: tool.description,
37
+ tags: tool.tags,
38
+ category: 'client'
39
+ });
40
+ });
41
+ }
42
+
43
+ register(
44
+ tool: AUITool | AIControlledTool,
45
+ metadata?: Partial<ToolMetadata>
46
+ ): this {
47
+ const meta: ToolMetadata = {
48
+ name: tool.name,
49
+ description: tool.description,
50
+ tags: tool.tags,
51
+ category: metadata?.category || 'custom',
52
+ permissions: metadata?.permissions,
53
+ schema: tool.schema,
54
+ examples: metadata?.examples,
55
+ ...metadata
56
+ };
57
+
58
+ this.tools.set(tool.name, tool);
59
+ this.metadata.set(tool.name, meta);
60
+
61
+ if (!this.categories.has(meta.category)) {
62
+ this.categories.set(meta.category, new Set());
63
+ }
64
+ this.categories.get(meta.category)!.add(tool.name);
65
+
66
+ meta.tags.forEach(tag => {
67
+ if (!this.tagIndex.has(tag)) {
68
+ this.tagIndex.set(tag, new Set());
69
+ }
70
+ this.tagIndex.get(tag)!.add(tool.name);
71
+ });
72
+
73
+ return this;
74
+ }
75
+
76
+ unregister(name: string): boolean {
77
+ const tool = this.tools.get(name);
78
+ if (!tool) return false;
79
+
80
+ const meta = this.metadata.get(name);
81
+ if (meta) {
82
+ this.categories.get(meta.category)?.delete(name);
83
+ meta.tags.forEach(tag => {
84
+ this.tagIndex.get(tag)?.delete(name);
85
+ });
86
+ }
87
+
88
+ this.tools.delete(name);
89
+ this.metadata.delete(name);
90
+ return true;
91
+ }
92
+
93
+ get(name: string): AUITool | AIControlledTool | undefined {
94
+ return this.tools.get(name);
95
+ }
96
+
97
+ getMetadata(name: string): ToolMetadata | undefined {
98
+ return this.metadata.get(name);
99
+ }
100
+
101
+ findByCategory(category: string): Array<AUITool | AIControlledTool> {
102
+ const names = this.categories.get(category);
103
+ if (!names) return [];
104
+ return Array.from(names)
105
+ .map(name => this.tools.get(name))
106
+ .filter(Boolean) as Array<AUITool | AIControlledTool>;
107
+ }
108
+
109
+ findByTag(tag: string): Array<AUITool | AIControlledTool> {
110
+ const names = this.tagIndex.get(tag);
111
+ if (!names) return [];
112
+ return Array.from(names)
113
+ .map(name => this.tools.get(name))
114
+ .filter(Boolean) as Array<AUITool | AIControlledTool>;
115
+ }
116
+
117
+ findByTags(tags: string[]): Array<AUITool | AIControlledTool> {
118
+ const toolSets = tags.map(tag => this.tagIndex.get(tag) || new Set());
119
+ if (toolSets.length === 0) return [];
120
+
121
+ const intersection = toolSets.reduce((acc, set) => {
122
+ return new Set([...acc].filter(x => set.has(x)));
123
+ });
124
+
125
+ return Array.from(intersection)
126
+ .map(name => this.tools.get(name as string))
127
+ .filter(Boolean) as Array<AUITool | AIControlledTool>;
128
+ }
129
+
130
+ search(query: string): Array<{
131
+ tool: AUITool | AIControlledTool;
132
+ metadata: ToolMetadata;
133
+ relevance: number;
134
+ }> {
135
+ const results: Array<{
136
+ tool: AUITool | AIControlledTool;
137
+ metadata: ToolMetadata;
138
+ relevance: number;
139
+ }> = [];
140
+
141
+ const lowerQuery = query.toLowerCase();
142
+
143
+ this.tools.forEach((tool, name) => {
144
+ const meta = this.metadata.get(name)!;
145
+ let relevance = 0;
146
+
147
+ if (name.toLowerCase().includes(lowerQuery)) {
148
+ relevance += 10;
149
+ }
150
+
151
+ if (meta.description?.toLowerCase().includes(lowerQuery)) {
152
+ relevance += 5;
153
+ }
154
+
155
+ meta.tags.forEach(tag => {
156
+ if (tag.toLowerCase().includes(lowerQuery)) {
157
+ relevance += 3;
158
+ }
159
+ });
160
+
161
+ if (meta.category.toLowerCase().includes(lowerQuery)) {
162
+ relevance += 2;
163
+ }
164
+
165
+ if (relevance > 0) {
166
+ results.push({ tool, metadata: meta, relevance });
167
+ }
168
+ });
169
+
170
+ return results.sort((a, b) => b.relevance - a.relevance);
171
+ }
172
+
173
+ async execute(
174
+ name: string,
175
+ input: any,
176
+ context?: AUIContext
177
+ ): Promise<any> {
178
+ const tool = this.tools.get(name);
179
+ if (!tool) {
180
+ throw new Error(`Tool "${name}" not found in registry`);
181
+ }
182
+
183
+ const meta = this.metadata.get(name);
184
+ const enhancedContext: AUIContext = {
185
+ cache: context?.cache || new Map(),
186
+ fetch: context?.fetch || globalThis.fetch,
187
+ ...context,
188
+ isServer: meta?.category === 'server' ? true :
189
+ meta?.category === 'client' ? false :
190
+ context?.isServer ?? (typeof window === 'undefined')
191
+ };
192
+
193
+ return await tool.run(input, enhancedContext);
194
+ }
195
+
196
+ list(): ToolMetadata[] {
197
+ return Array.from(this.metadata.values());
198
+ }
199
+
200
+ listByCategory(): Record<string, ToolMetadata[]> {
201
+ const result: Record<string, ToolMetadata[]> = {};
202
+
203
+ this.categories.forEach((tools, category) => {
204
+ result[category] = Array.from(tools)
205
+ .map(name => this.metadata.get(name))
206
+ .filter(Boolean) as ToolMetadata[];
207
+ });
208
+
209
+ return result;
210
+ }
211
+
212
+ exportSchema(): Record<string, any> {
213
+ const schemas: Record<string, any> = {};
214
+
215
+ this.tools.forEach((tool, name) => {
216
+ const meta = this.metadata.get(name)!;
217
+ schemas[name] = {
218
+ name,
219
+ description: meta.description,
220
+ category: meta.category,
221
+ tags: meta.tags,
222
+ inputSchema: tool.schema ? zodToJsonSchema(tool.schema) : null,
223
+ examples: meta.examples
224
+ };
225
+ });
226
+
227
+ return schemas;
228
+ }
229
+
230
+ importTools(tools: Array<AUITool | AIControlledTool>, category?: 'server' | 'client' | 'hybrid' | 'custom'): void {
231
+ tools.forEach(tool => {
232
+ this.register(tool, { category: category || 'custom' });
233
+ });
234
+ }
235
+
236
+ clear(): void {
237
+ this.tools.clear();
238
+ this.metadata.clear();
239
+ this.categories.clear();
240
+ this.tagIndex.clear();
241
+ }
242
+
243
+ getStats(): {
244
+ totalTools: number;
245
+ byCategory: Record<string, number>;
246
+ byTag: Record<string, number>;
247
+ } {
248
+ const byCategory: Record<string, number> = {};
249
+ this.categories.forEach((tools, category) => {
250
+ byCategory[category] = tools.size;
251
+ });
252
+
253
+ const byTag: Record<string, number> = {};
254
+ this.tagIndex.forEach((tools, tag) => {
255
+ byTag[tag] = tools.size;
256
+ });
257
+
258
+ return {
259
+ totalTools: this.tools.size,
260
+ byCategory,
261
+ byTag
262
+ };
263
+ }
264
+ }
265
+
266
+ function zodToJsonSchema(schema: z.ZodType<any>): any {
267
+ if (schema instanceof z.ZodObject) {
268
+ const shape = schema.shape;
269
+ const properties: Record<string, any> = {};
270
+ const required: string[] = [];
271
+
272
+ Object.entries(shape).forEach(([key, value]: [string, any]) => {
273
+ properties[key] = zodToJsonSchema(value);
274
+ if (!value.isOptional()) {
275
+ required.push(key);
276
+ }
277
+ });
278
+
279
+ return {
280
+ type: 'object',
281
+ properties,
282
+ required: required.length > 0 ? required : undefined
283
+ };
284
+ }
285
+
286
+ if (schema instanceof z.ZodString) {
287
+ return { type: 'string' };
288
+ }
289
+
290
+ if (schema instanceof z.ZodNumber) {
291
+ return { type: 'number' };
292
+ }
293
+
294
+ if (schema instanceof z.ZodBoolean) {
295
+ return { type: 'boolean' };
296
+ }
297
+
298
+ if (schema instanceof z.ZodArray) {
299
+ return {
300
+ type: 'array',
301
+ items: zodToJsonSchema(schema.element)
302
+ };
303
+ }
304
+
305
+ if (schema instanceof z.ZodEnum) {
306
+ return {
307
+ type: 'string',
308
+ enum: schema.options
309
+ };
310
+ }
311
+
312
+ if (schema instanceof z.ZodOptional) {
313
+ return zodToJsonSchema(schema.unwrap());
314
+ }
315
+
316
+ return { type: 'any' };
317
+ }
318
+
319
+ export const globalToolRegistry = new ToolRegistry();
320
+
321
+ export class ToolDiscovery {
322
+ private registry: ToolRegistry;
323
+
324
+ constructor(registry: ToolRegistry = globalToolRegistry) {
325
+ this.registry = registry;
326
+ }
327
+
328
+ async discoverTools(pattern?: string): Promise<ToolMetadata[]> {
329
+ if (pattern) {
330
+ return this.registry.search(pattern).map(r => r.metadata);
331
+ }
332
+ return this.registry.list();
333
+ }
334
+
335
+ async getToolCapabilities(name: string): Promise<{
336
+ canExecuteOnServer: boolean;
337
+ canExecuteOnClient: boolean;
338
+ requiresAuth: boolean;
339
+ rateLimit?: { requestsPerMinute?: number; requestsPerHour?: number };
340
+ }> {
341
+ const tool = this.registry.get(name);
342
+ if (!tool) {
343
+ throw new Error(`Tool "${name}" not found`);
344
+ }
345
+
346
+ const meta = this.registry.getMetadata(name);
347
+ const isAIControlled = tool instanceof AIControlledTool;
348
+
349
+ return {
350
+ canExecuteOnServer: meta?.category === 'server' || meta?.category === 'hybrid',
351
+ canExecuteOnClient: meta?.category === 'client' || meta?.category === 'hybrid',
352
+ requiresAuth: meta?.permissions?.includes('auth') || false,
353
+ rateLimit: isAIControlled ? (tool as any).controlOptions?.rateLimit : undefined
354
+ };
355
+ }
356
+
357
+ async getRecommendedTools(context: {
358
+ task?: string;
359
+ tags?: string[];
360
+ category?: string;
361
+ }): Promise<ToolMetadata[]> {
362
+ let tools: ToolMetadata[] = [];
363
+
364
+ if (context.category) {
365
+ tools = this.registry.findByCategory(context.category)
366
+ .map(t => this.registry.getMetadata(t.name)!);
367
+ } else if (context.tags && context.tags.length > 0) {
368
+ tools = this.registry.findByTags(context.tags)
369
+ .map(t => this.registry.getMetadata(t.name)!);
370
+ } else if (context.task) {
371
+ tools = this.registry.search(context.task).map(r => r.metadata);
372
+ } else {
373
+ tools = this.registry.list();
374
+ }
375
+
376
+ return tools.slice(0, 10);
377
+ }
378
+ }
379
+
380
+ export const toolDiscovery = new ToolDiscovery(globalToolRegistry);
@@ -0,0 +1,86 @@
1
+ import React from 'react';
2
+ import aui, { z } from '../index';
3
+
4
+ export const databaseTool = aui
5
+ .tool('database')
6
+ .input(z.object({
7
+ operation: z.enum(['create', 'read', 'update', 'delete']),
8
+ table: z.string(),
9
+ data: z.any().optional()
10
+ }))
11
+ .execute(async ({ input }) => {
12
+ switch (input.operation) {
13
+ case 'create':
14
+ return { id: Date.now(), ...input.data };
15
+ case 'read':
16
+ return { records: [], count: 0 };
17
+ case 'update':
18
+ return { updated: 1 };
19
+ case 'delete':
20
+ return { deleted: 1 };
21
+ }
22
+ });
23
+
24
+ export const fileSystemTool = aui
25
+ .tool('fileSystem')
26
+ .input(z.object({
27
+ action: z.enum(['read', 'write', 'list']),
28
+ path: z.string(),
29
+ content: z.string().optional()
30
+ }))
31
+ .execute(async ({ input }) => {
32
+ return { success: true, action: input.action, path: input.path };
33
+ });
34
+
35
+ export const apiTool = aui
36
+ .tool('api')
37
+ .input(z.object({
38
+ method: z.enum(['GET', 'POST', 'PUT', 'DELETE']),
39
+ endpoint: z.string(),
40
+ body: z.any().optional()
41
+ }))
42
+ .execute(async ({ input }) => {
43
+ return { status: 200, data: { message: 'Success' } };
44
+ });
45
+
46
+ export const processTool = aui
47
+ .tool('process')
48
+ .input(z.object({
49
+ command: z.string(),
50
+ args: z.array(z.string()).optional()
51
+ }))
52
+ .execute(async ({ input }) => {
53
+ return { output: `Executed: ${input.command}`, exitCode: 0 };
54
+ });
55
+
56
+ export const stateTool = aui
57
+ .tool('state')
58
+ .input(z.object({
59
+ action: z.enum(['get', 'set', 'update']),
60
+ key: z.string(),
61
+ value: z.any().optional()
62
+ }))
63
+ .execute(async ({ input }) => {
64
+ return { key: input.key, value: input.value || null };
65
+ });
66
+
67
+ export const notificationTool = aui
68
+ .tool('notification')
69
+ .input(z.object({
70
+ type: z.enum(['info', 'success', 'warning', 'error']),
71
+ message: z.string(),
72
+ duration: z.number().optional()
73
+ }))
74
+ .execute(async ({ input }) => {
75
+ return { sent: true, ...input };
76
+ })
77
+ .render(({ data }) => (
78
+ <div className={`p-4 rounded-lg ${
79
+ data.type === 'error' ? 'bg-red-100' :
80
+ data.type === 'warning' ? 'bg-yellow-100' :
81
+ data.type === 'success' ? 'bg-green-100' :
82
+ 'bg-blue-100'
83
+ }`}>
84
+ {data.message}
85
+ </div>
86
+ ));