@genui-a3/create 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 (44) hide show
  1. package/dist/index.js +98 -0
  2. package/package.json +45 -0
  3. package/template/README.md +16 -0
  4. package/template/_gitignore +35 -0
  5. package/template/app/(pages)/agui/AguiChat.tsx +109 -0
  6. package/template/app/(pages)/agui/page.tsx +12 -0
  7. package/template/app/(pages)/chat/page.tsx +12 -0
  8. package/template/app/(pages)/stream/page.tsx +12 -0
  9. package/template/app/ThemeProvider.tsx +17 -0
  10. package/template/app/agents/age.ts +24 -0
  11. package/template/app/agents/greeting.ts +37 -0
  12. package/template/app/api/agui/route.ts +69 -0
  13. package/template/app/api/chat/route.ts +65 -0
  14. package/template/app/api/stream/route.ts +64 -0
  15. package/template/app/apple-icon-dark.png +0 -0
  16. package/template/app/apple-icon.png +0 -0
  17. package/template/app/components/atoms/AppLogo.tsx +44 -0
  18. package/template/app/components/atoms/ChatContainer.tsx +13 -0
  19. package/template/app/components/atoms/ChatHeader.tsx +14 -0
  20. package/template/app/components/atoms/MessageBubble.tsx +21 -0
  21. package/template/app/components/atoms/index.ts +4 -0
  22. package/template/app/components/molecules/ChatInput.tsx +66 -0
  23. package/template/app/components/molecules/ChatMessage.tsx +41 -0
  24. package/template/app/components/molecules/index.ts +2 -0
  25. package/template/app/components/organisms/Chat.tsx +77 -0
  26. package/template/app/components/organisms/ChatMessageList.tsx +37 -0
  27. package/template/app/components/organisms/PageLayout.tsx +43 -0
  28. package/template/app/components/organisms/StreamChat.tsx +149 -0
  29. package/template/app/components/organisms/index.ts +4 -0
  30. package/template/app/constants/chat.ts +4 -0
  31. package/template/app/favicon-dark.ico +0 -0
  32. package/template/app/favicon.ico +0 -0
  33. package/template/app/icon.svg +13 -0
  34. package/template/app/layout.tsx +32 -0
  35. package/template/app/page.tsx +171 -0
  36. package/template/app/styled.d.ts +6 -0
  37. package/template/app/theme.ts +22 -0
  38. package/template/app/types/index.ts +8 -0
  39. package/template/next.config.mjs +6 -0
  40. package/template/package.json +29 -0
  41. package/template/public/android-chrome-192x192.png +0 -0
  42. package/template/public/android-chrome-512x512.png +0 -0
  43. package/template/public/site.webmanifest +11 -0
  44. package/template/tsconfig.json +41 -0
@@ -0,0 +1,13 @@
1
+ 'use client'
2
+
3
+ import styled from 'styled-components'
4
+ import { Paper } from '@mui/material'
5
+
6
+ export const ChatContainer = styled(Paper)`
7
+ display: flex;
8
+ flex-direction: column;
9
+ height: 100%;
10
+ min-height: 0;
11
+ border-radius: 12px;
12
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
13
+ `
@@ -0,0 +1,14 @@
1
+ 'use client'
2
+
3
+ import styled from 'styled-components'
4
+ import type { Theme } from '@mui/material/styles'
5
+
6
+ export const ChatHeader = styled.div`
7
+ border-bottom: 1px solid ${({ theme }) => (theme as Theme).palette.divider};
8
+ background-color: ${({ theme }) => (theme as Theme).palette.background.paper};
9
+ padding: ${({ theme }) => (theme as Theme).spacing(2, 3)};
10
+ flex-shrink: 0;
11
+ display: flex;
12
+ justify-content: space-between;
13
+ align-items: center;
14
+ `
@@ -0,0 +1,21 @@
1
+ import styled from 'styled-components'
2
+ import { Paper } from '@mui/material'
3
+ import type { Theme } from '@mui/material/styles'
4
+
5
+ export const MessageBubble = styled(Paper)<{ $isUser: boolean }>`
6
+ max-width: 80%;
7
+ padding: ${({ theme }) => (theme as Theme).spacing(1.5, 2)};
8
+ border-radius: ${({ theme }) => (theme as Theme).spacing(2.5)};
9
+ ${({ $isUser, theme }) =>
10
+ $isUser
11
+ ? `
12
+ background-color: ${(theme as Theme).palette.primary.main};
13
+ color: ${(theme as Theme).palette.primary.contrastText};
14
+ border-bottom-right-radius: ${(theme as Theme).spacing(0.5)};
15
+ `
16
+ : `
17
+ background-color: ${(theme as Theme).palette.grey[200]};
18
+ color: ${(theme as Theme).palette.text.primary};
19
+ border-bottom-left-radius: ${(theme as Theme).spacing(0.5)};
20
+ `}
21
+ `
@@ -0,0 +1,4 @@
1
+ export { AppLogo } from './AppLogo'
2
+ export { MessageBubble } from './MessageBubble'
3
+ export { ChatContainer } from './ChatContainer'
4
+ export { ChatHeader } from './ChatHeader'
@@ -0,0 +1,66 @@
1
+ 'use client'
2
+
3
+ import { useState, useCallback } from 'react'
4
+ import styled from 'styled-components'
5
+ import { TextField, Button, Box } from '@mui/material'
6
+ import type { Theme } from '@mui/material/styles'
7
+ import SendIcon from '@mui/icons-material/Send'
8
+
9
+ type Props = {
10
+ onSubmit: (text: string) => void | Promise<void>
11
+ disabled?: boolean
12
+ placeholder?: string
13
+ }
14
+
15
+ const InputContainer = styled(Box)`
16
+ border-top: 1px solid ${({ theme }) => (theme as Theme).palette.divider};
17
+ background-color: ${({ theme }) => (theme as Theme).palette.background.paper};
18
+ padding: ${({ theme }) => (theme as Theme).spacing(2)};
19
+ flex-shrink: 0;
20
+ `
21
+
22
+ const InputForm = styled.form`
23
+ display: flex;
24
+ gap: ${({ theme }) => (theme as Theme).spacing(1.5)};
25
+ `
26
+
27
+ export function ChatInput({ onSubmit, disabled, placeholder = 'Type a message...' }: Props) {
28
+ const [value, setValue] = useState('')
29
+
30
+ const handleSubmit = useCallback(
31
+ (e: React.FormEvent) => {
32
+ e.preventDefault()
33
+ const trimmed = value.trim()
34
+ if (!trimmed || disabled) return
35
+ void onSubmit(trimmed)
36
+ setValue('')
37
+ },
38
+ [value, disabled, onSubmit],
39
+ )
40
+
41
+ return (
42
+ <InputContainer>
43
+ <InputForm onSubmit={handleSubmit}>
44
+ <TextField
45
+ fullWidth
46
+ value={value}
47
+ onChange={(e) => setValue(e.target.value)}
48
+ placeholder={placeholder}
49
+ disabled={disabled}
50
+ variant="outlined"
51
+ size="small"
52
+ data-testid="chat-input"
53
+ />
54
+ <Button
55
+ type="submit"
56
+ variant="contained"
57
+ disabled={disabled || !value.trim()}
58
+ startIcon={<SendIcon />}
59
+ data-testid="chat-send"
60
+ >
61
+ Send
62
+ </Button>
63
+ </InputForm>
64
+ </InputContainer>
65
+ )
66
+ }
@@ -0,0 +1,41 @@
1
+ import styled, { keyframes } from 'styled-components'
2
+ import { Typography } from '@mui/material'
3
+ import { MessageBubble } from '@atoms'
4
+ import { MESSAGE_SENDER } from '@constants/chat'
5
+ import type { ChatMessage as ChatMessageType } from 'types'
6
+
7
+ type Props = { message: ChatMessageType }
8
+
9
+ const MessageRow = styled.div<{ $isUser: boolean }>`
10
+ display: flex;
11
+ justify-content: ${({ $isUser }) => ($isUser ? 'flex-end' : 'flex-start')};
12
+ `
13
+
14
+ const blink = keyframes`
15
+ 0%, 100% { opacity: 1; }
16
+ 50% { opacity: 0; }
17
+ `
18
+
19
+ const StreamingCursor = styled.span`
20
+ display: inline-block;
21
+ width: 6px;
22
+ height: 14px;
23
+ margin-left: 2px;
24
+ background-color: currentColor;
25
+ vertical-align: text-bottom;
26
+ animation: ${blink} 0.8s step-end infinite;
27
+ `
28
+
29
+ export function ChatMessage({ message }: Props) {
30
+ const isUser = message?.source === MESSAGE_SENDER.USER
31
+ return (
32
+ <MessageRow $isUser={isUser} data-testid="chat-message">
33
+ <MessageBubble $isUser={isUser} elevation={0}>
34
+ <Typography variant="body2" sx={{ whiteSpace: 'pre-wrap' }}>
35
+ {message.body.trim()}
36
+ {message.isStreaming && <StreamingCursor />}
37
+ </Typography>
38
+ </MessageBubble>
39
+ </MessageRow>
40
+ )
41
+ }
@@ -0,0 +1,2 @@
1
+ export { ChatMessage } from './ChatMessage'
2
+ export { ChatInput } from './ChatInput'
@@ -0,0 +1,77 @@
1
+ 'use client'
2
+
3
+ import { useState, useCallback } from 'react'
4
+ import { Typography, CircularProgress } from '@mui/material'
5
+ import { ChatMessageList } from './ChatMessageList'
6
+ import { ChatContainer, ChatHeader } from '@atoms'
7
+ import { ChatInput } from '@molecules'
8
+ import type { ChatMessage as ChatMessageType } from 'types'
9
+
10
+ const SESSION_ID = 'demo-session'
11
+
12
+ type ChatApiResponse = {
13
+ response: string
14
+ activeAgentId: string | null
15
+ nextAgentId: string | null
16
+ state: Record<string, unknown>
17
+ goalAchieved: boolean
18
+ }
19
+
20
+ export function Chat() {
21
+ const [messages, setMessages] = useState<ChatMessageType[]>([])
22
+ const [isLoading, setIsLoading] = useState(false)
23
+
24
+ const handleSubmit = useCallback(async (text: string) => {
25
+ const userMsg: ChatMessageType = {
26
+ id: crypto.randomUUID(),
27
+ body: text,
28
+ source: 'user',
29
+ }
30
+ setMessages((prev) => [...prev, userMsg])
31
+ setIsLoading(true)
32
+
33
+ try {
34
+ const response = await fetch('/api/chat', {
35
+ method: 'POST',
36
+ headers: { 'Content-Type': 'application/json' },
37
+ body: JSON.stringify({ message: text, sessionId: SESSION_ID }),
38
+ })
39
+
40
+ if (!response.ok) {
41
+ throw new Error(`HTTP error: ${response.status}`)
42
+ }
43
+
44
+ const data = (await response.json()) as ChatApiResponse
45
+
46
+ const assistantMsg: ChatMessageType = {
47
+ id: crypto.randomUUID(),
48
+ body: data.response,
49
+ source: 'assistant',
50
+ }
51
+ setMessages((prev) => [...prev, assistantMsg])
52
+ } catch (error) {
53
+ console.error('Chat API error:', error)
54
+ const errorMsg: ChatMessageType = {
55
+ id: crypto.randomUUID(),
56
+ body: 'Sorry, something went wrong. Please try again.',
57
+ source: 'assistant',
58
+ }
59
+ setMessages((prev) => [...prev, errorMsg])
60
+ } finally {
61
+ setIsLoading(false)
62
+ }
63
+ }, [])
64
+
65
+ return (
66
+ <ChatContainer elevation={0}>
67
+ <ChatHeader>
68
+ <Typography variant="h6" component="h2">
69
+ Chat
70
+ </Typography>
71
+ {isLoading && <CircularProgress size={16} />}
72
+ </ChatHeader>
73
+ <ChatMessageList messages={messages} />
74
+ <ChatInput onSubmit={handleSubmit} disabled={isLoading} />
75
+ </ChatContainer>
76
+ )
77
+ }
@@ -0,0 +1,37 @@
1
+ 'use client'
2
+
3
+ import { useRef, useEffect } from 'react'
4
+ import styled from 'styled-components'
5
+ import { Box } from '@mui/material'
6
+ import type { Theme } from '@mui/material/styles'
7
+ import { ChatMessage } from '@molecules'
8
+ import type { ChatMessage as ChatMessageType } from 'types'
9
+
10
+ type Props = { messages: ChatMessageType[] }
11
+
12
+ const MessageListContainer = styled(Box)`
13
+ flex: 1;
14
+ overflow-y: auto;
15
+ padding: ${({ theme }) => (theme as Theme).spacing(2)};
16
+ display: flex;
17
+ flex-direction: column;
18
+ gap: ${({ theme }) => (theme as Theme).spacing(1.5)};
19
+ `
20
+
21
+ export function ChatMessageList({ messages }: Props) {
22
+ const bottomRef = useRef<HTMLDivElement>(null)
23
+ const lastMessage = messages[messages.length - 1]
24
+
25
+ useEffect(() => {
26
+ bottomRef.current?.scrollIntoView({ behavior: 'smooth' })
27
+ }, [messages.length, lastMessage?.body])
28
+
29
+ return (
30
+ <MessageListContainer>
31
+ {messages.map((m) => (
32
+ <ChatMessage key={m.id} message={m} />
33
+ ))}
34
+ <div ref={bottomRef} />
35
+ </MessageListContainer>
36
+ )
37
+ }
@@ -0,0 +1,43 @@
1
+ import { ReactNode } from 'react'
2
+ import { AppLogo } from '@atoms'
3
+ import { Box, Typography, Button } from '@mui/material'
4
+ import ArrowBackIcon from '@mui/icons-material/ArrowBack'
5
+ import Link from 'next/link'
6
+
7
+ interface AppPageLayoutProps {
8
+ title: string
9
+ children: ReactNode
10
+ }
11
+
12
+ export function PageLayout({ title, children }: AppPageLayoutProps) {
13
+ return (
14
+ <Box sx={{ display: 'flex', flexDirection: 'column', height: '100vh', bgcolor: 'background.default' }}>
15
+ <Box
16
+ component="header"
17
+ sx={{
18
+ p: 2,
19
+ px: 3,
20
+ display: 'flex',
21
+ alignItems: 'center',
22
+ gap: 2,
23
+ borderBottom: 1,
24
+ borderColor: 'divider',
25
+ bgcolor: 'background.paper',
26
+ }}
27
+ >
28
+ <AppLogo width={32} height={32} />
29
+ <Typography variant="h6" fontWeight="bold" sx={{ flexGrow: 1 }}>
30
+ {title}
31
+ </Typography>
32
+ <Button component={Link} href="/" variant="text" startIcon={<ArrowBackIcon />} sx={{ color: 'text.secondary' }}>
33
+ Back to Home
34
+ </Button>
35
+ </Box>
36
+ <Box sx={{ flex: 1, p: 3, display: 'flex', justifyContent: 'center', overflow: 'hidden' }}>
37
+ <Box sx={{ width: '100%', maxWidth: 'md', height: '100%', display: 'flex', flexDirection: 'column' }}>
38
+ {children}
39
+ </Box>
40
+ </Box>
41
+ </Box>
42
+ )
43
+ }
@@ -0,0 +1,149 @@
1
+ 'use client'
2
+
3
+ import { useState, useCallback, useRef } from 'react'
4
+ import { Typography } from '@mui/material'
5
+ import { ChatMessageList } from './ChatMessageList'
6
+ import { ChatContainer, ChatHeader } from '@atoms'
7
+ import { ChatInput } from '@molecules'
8
+ import type { ChatMessage as ChatMessageType } from 'types'
9
+ import { EventType } from '@ag-ui/client'
10
+
11
+ const SESSION_ID = 'demo-stream-session'
12
+
13
+ type StreamEvent = {
14
+ type: EventType
15
+ delta?: string
16
+ agentId?: string
17
+ result?: Record<string, unknown>
18
+ message?: string
19
+ content?: string
20
+ name?: string
21
+ value?: Record<string, unknown>
22
+ }
23
+
24
+ export function StreamChat() {
25
+ const [messages, setMessages] = useState<ChatMessageType[]>([])
26
+ const [isLoading, setIsLoading] = useState(false)
27
+ const [isTransitioning, setIsTransitioning] = useState(false)
28
+ const assistantIdRef = useRef<string>('')
29
+
30
+ const handleSubmit = useCallback(async (text: string) => {
31
+ const userMsg: ChatMessageType = {
32
+ id: crypto.randomUUID(),
33
+ body: text,
34
+ source: 'user',
35
+ }
36
+ setMessages((prev) => [...prev, userMsg])
37
+ setIsLoading(true)
38
+
39
+ // Create a placeholder assistant message for streaming into
40
+ let assistantId = crypto.randomUUID()
41
+ assistantIdRef.current = assistantId
42
+
43
+ const streamingMsg: ChatMessageType = {
44
+ id: assistantId,
45
+ body: '',
46
+ source: 'assistant',
47
+ isStreaming: true,
48
+ }
49
+ setMessages((prev) => [...prev, streamingMsg])
50
+
51
+ try {
52
+ const response = await fetch('/api/stream', {
53
+ method: 'POST',
54
+ headers: { 'Content-Type': 'application/json' },
55
+ body: JSON.stringify({ message: text, sessionId: SESSION_ID }),
56
+ })
57
+
58
+ if (!response.ok) {
59
+ throw new Error(`HTTP error: ${response.status}`)
60
+ }
61
+
62
+ const reader = response.body?.getReader()
63
+ if (!reader) throw new Error('No response body')
64
+
65
+ const decoder = new TextDecoder()
66
+ let buffer = ''
67
+
68
+ while (true) {
69
+ const { done, value } = await reader.read()
70
+ if (done) break
71
+
72
+ buffer += decoder.decode(value, { stream: true })
73
+
74
+ // Process complete SSE lines
75
+ const lines = buffer.split('\n')
76
+ buffer = lines.pop() ?? ''
77
+
78
+ for (const line of lines) {
79
+ if (!line.startsWith('data: ')) continue
80
+ const data = line.slice(6)
81
+ if (data === '[DONE]') continue
82
+
83
+ try {
84
+ const event = JSON.parse(data) as StreamEvent
85
+
86
+ if (event.type === EventType.TEXT_MESSAGE_CONTENT && event.delta) {
87
+ setIsTransitioning(false)
88
+ setMessages((prev) => prev.map((m) => (m.id === assistantId ? { ...m, body: m.body + event.delta } : m)))
89
+ } else if (event.type === EventType.CUSTOM && event.name === 'AgentTransition') {
90
+ const prevAssistantId = assistantId
91
+ assistantId = crypto.randomUUID()
92
+ assistantIdRef.current = assistantId
93
+ setIsTransitioning(true)
94
+ setMessages((prev) => {
95
+ const updated = prev.map((m) => (m.id === prevAssistantId ? { ...m, isStreaming: false } : m))
96
+ return [...updated, { id: assistantId, body: '', source: 'assistant', isStreaming: true }]
97
+ })
98
+ } else if (event.type === EventType.RUN_FINISHED) {
99
+ setIsTransitioning(false)
100
+ setMessages((prev) => prev.map((m) => (m.id === assistantId ? { ...m, isStreaming: false } : m)))
101
+ } else if (event.type === EventType.RUN_ERROR) {
102
+ setIsTransitioning(false)
103
+ setMessages((prev) =>
104
+ prev.map((m) =>
105
+ m.id === assistantId
106
+ ? { ...m, body: m.body || 'Sorry, something went wrong.', isStreaming: false }
107
+ : m,
108
+ ),
109
+ )
110
+ }
111
+ } catch {
112
+ // Skip malformed JSON lines
113
+ }
114
+ }
115
+ }
116
+
117
+ // Ensure streaming flag is cleared
118
+ setMessages((prev) => prev.map((m) => (m.id === assistantId ? { ...m, isStreaming: false } : m)))
119
+ } catch (error) {
120
+ console.error('Chat stream error:', error)
121
+ setMessages((prev) =>
122
+ prev.map((m) =>
123
+ m.id === assistantId
124
+ ? { ...m, body: m.body || 'Sorry, something went wrong. Please try again.', isStreaming: false }
125
+ : m,
126
+ ),
127
+ )
128
+ } finally {
129
+ setIsLoading(false)
130
+ }
131
+ }, [])
132
+
133
+ return (
134
+ <ChatContainer elevation={0}>
135
+ <ChatHeader>
136
+ <Typography variant="h6" component="h2">
137
+ Chat (Streaming)
138
+ </Typography>
139
+ </ChatHeader>
140
+ <ChatMessageList messages={messages} />
141
+ {isTransitioning && (
142
+ <Typography variant="caption" color="textSecondary" sx={{ px: 2, pb: 1, fontStyle: 'italic' }}>
143
+ Agent transition in progress...
144
+ </Typography>
145
+ )}
146
+ <ChatInput onSubmit={handleSubmit} disabled={isLoading} />
147
+ </ChatContainer>
148
+ )
149
+ }
@@ -0,0 +1,4 @@
1
+ export { Chat } from './Chat'
2
+ export { ChatMessageList } from './ChatMessageList'
3
+ export { StreamChat } from './StreamChat'
4
+ export { PageLayout } from './PageLayout'
@@ -0,0 +1,4 @@
1
+ export const MESSAGE_SENDER = {
2
+ USER: 'user',
3
+ ASSISTANT: 'assistant',
4
+ }
Binary file
Binary file
@@ -0,0 +1,13 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
2
+ <defs>
3
+ <style>
4
+ @media (prefers-color-scheme: dark) {
5
+ .logo-mark { fill: #ffffff; }
6
+ }
7
+ </style>
8
+ </defs>
9
+ <g>
10
+ <path class="logo-mark" d="M 560.50 870.11 C544.60,872.39 493.52,873.54 477.50,871.98 C429.64,867.32 381.89,853.57 340.50,832.53 C316.53,820.34 296.91,807.54 274.00,789.15 C261.51,779.12 236.58,754.18 225.88,741.00 C180.72,685.39 153.06,618.33 146.08,547.58 C144.70,533.59 144.69,497.56 146.06,483.42 C151.82,424.12 172.58,365.85 205.33,317.00 C257.37,239.38 349.03,180.93 442.00,166.07 C464.04,162.55 478.67,161.69 505.88,162.29 C555.48,163.38 594.16,171.21 639.00,189.25 C659.38,197.45 694.34,215.75 702.79,222.64 L 705.07 224.50 L 699.18 234.00 C695.94,239.23 690.48,248.11 687.05,253.75 C683.61,259.39 680.44,264.00 679.99,264.00 C679.54,264.00 674.52,261.04 668.84,257.42 C655.11,248.69 625.53,234.22 610.00,228.66 C583.75,219.25 553.35,212.43 527.00,210.05 C512.11,208.70 478.67,208.71 464.00,210.07 C448.87,211.47 421.38,216.98 407.00,221.49 C359.48,236.39 317.14,262.02 281.44,297.50 C258.31,320.48 241.26,343.77 226.14,373.00 C202.42,418.87 191.69,463.62 191.74,516.50 C191.78,555.23 197.10,585.98 210.10,622.48 C215.21,636.83 229.38,665.53 238.40,679.79 C258.58,711.72 284.51,739.87 314.60,762.50 C358.47,795.49 416.85,818.66 475.00,826.15 C490.90,828.19 536.15,827.93 552.00,825.69 C589.08,820.47 619.66,811.35 651.80,795.94 C758.47,744.79 825.34,642.74 828.69,526.00 C830.16,474.91 818.90,425.90 794.39,376.67 C790.88,369.61 788.00,363.68 788.00,363.49 C788.00,362.88 830.91,349.00 832.81,349.00 C834.20,349.00 836.30,352.42 841.26,362.75 C849.43,379.78 850.04,381.23 856.16,398.49 C887.34,486.35 882.15,583.99 841.89,667.00 C819.83,712.49 793.79,746.96 757.00,779.42 C700.79,829.00 634.78,859.47 560.50,870.11 ZM 335.80 648.00 L 331.50 660.50 L 297.25 660.76 C278.41,660.91 263.00,660.77 263.00,660.45 C263.00,659.86 278.07,617.38 291.17,581.00 C295.04,570.28 308.24,533.38 320.52,499.00 C332.80,464.62 347.87,422.55 354.01,405.50 C360.15,388.45 366.62,370.45 368.39,365.50 L 371.61 356.50 L 409.10 356.24 L 446.59 355.98 L 457.22 385.74 C463.07,402.11 475.32,436.42 484.45,462.00 C493.58,487.58 502.36,512.10 503.97,516.50 C505.58,520.90 515.27,547.90 525.51,576.50 C535.75,605.10 546.52,635.12 549.43,643.22 C552.35,651.31 554.49,658.17 554.20,658.47 C553.91,658.76 537.97,659.00 518.78,659.00 L 483.90 659.00 L 482.58 655.75 C481.85,653.96 477.26,640.69 472.38,626.26 L 463.50 600.02 L 352.45 600.00 L 346.28 617.75 C342.88,627.51 338.17,641.12 335.80,648.00 ZM 687.50 661.35 C660.93,665.46 637.94,663.57 615.06,655.41 C601.27,650.49 591.03,644.01 580.37,633.46 C572.36,625.53 570.61,623.14 566.13,613.96 C560.20,601.81 557.14,589.54 556.76,576.32 L 556.50 567.50 L 616.57 566.97 L 617.62 574.74 C620.91,598.97 640.94,614.52 666.50,612.68 C689.18,611.04 704.64,597.72 707.93,576.97 C709.92,564.44 706.02,552.13 697.07,542.67 C687.39,532.43 676.26,529.04 652.23,529.02 L 636.96 529.00 L 637.23 504.75 L 637.50 480.50 L 654.00 479.93 C674.62,479.21 680.48,477.31 690.26,468.16 C697.55,461.34 700.26,455.37 700.77,445.00 C701.35,433.25 698.88,426.59 691.06,418.89 C682.94,410.89 676.70,408.61 663.00,408.61 C649.61,408.61 643.48,410.81 635.00,418.66 C628.14,425.01 625.48,428.98 622.42,437.42 L 620.22 443.50 L 591.93 443.77 C576.36,443.91 563.30,443.69 562.90,443.27 C561.92,442.23 564.65,426.97 567.16,419.51 C578.07,387.00 606.35,365.08 645.92,358.47 C659.23,356.24 682.31,356.90 695.19,359.88 C714.30,364.30 729.13,372.16 741.60,384.49 C755.80,398.54 761.90,412.81 762.75,434.04 C763.17,444.62 762.92,447.89 761.13,455.04 C756.76,472.50 747.16,486.30 733.31,495.04 C729.72,497.32 727.16,499.52 727.64,499.94 C728.11,500.37 731.08,501.76 734.24,503.03 C747.92,508.57 761.40,522.70 767.11,537.47 C771.91,549.87 773.33,559.36 772.69,574.50 C772.04,589.80 770.30,597.27 764.55,609.50 C751.70,636.85 723.57,655.78 687.50,661.35 ZM 370.99 545.75 C371.00,546.73 379.19,547.00 408.61,547.00 C438.28,547.00 446.12,546.74 445.75,545.75 C445.49,545.06 437.02,519.64 426.93,489.25 C416.83,458.86 408.31,434.00 407.99,434.00 C407.33,434.00 370.98,543.79 370.99,545.75 Z" fill="rgb(4,4,4)"/>
11
+ <path d="M 806.90 322.97 C785.45,328.12 765.24,322.48 749.97,307.09 C737.88,294.91 733.00,282.92 733.00,265.40 C733.00,254.57 734.84,247.01 739.72,237.71 C746.39,225.02 759.41,213.62 772.68,208.85 C780.93,205.88 799.11,205.17 807.92,207.47 C830.44,213.34 847.16,231.12 851.67,253.99 C855.45,273.14 849.31,292.70 835.02,307.04 C826.17,315.92 818.73,320.14 806.90,322.97 Z" fill="rgb(248,175,81)"/>
12
+ </g>
13
+ </svg>
@@ -0,0 +1,32 @@
1
+ import type { Metadata } from 'next'
2
+ import { ThemeProvider } from './ThemeProvider'
3
+
4
+ export const metadata: Metadata = {
5
+ title: 'A3 Core Example',
6
+ description: 'Example application for @genui-a3/core',
7
+ icons: [
8
+ { rel: 'icon', url: '/favicon.ico', type: 'image/x-icon', sizes: '32x32', media: '(prefers-color-scheme: light)' },
9
+ {
10
+ rel: 'icon',
11
+ url: '/favicon-dark.ico',
12
+ type: 'image/x-icon',
13
+ sizes: '32x32',
14
+ media: '(prefers-color-scheme: dark)',
15
+ },
16
+ { rel: 'icon', url: '/icon.svg', type: 'image/svg+xml', media: '(prefers-color-scheme: light)' },
17
+ { rel: 'icon', url: '/icon.svg', type: 'image/svg+xml', media: '(prefers-color-scheme: dark)' },
18
+ { rel: 'apple-touch-icon', url: '/apple-icon.png', media: '(prefers-color-scheme: light)' },
19
+ { rel: 'apple-touch-icon', url: '/apple-icon-dark.png', media: '(prefers-color-scheme: dark)' },
20
+ ],
21
+ manifest: '/site.webmanifest',
22
+ }
23
+
24
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
25
+ return (
26
+ <html lang="en">
27
+ <body>
28
+ <ThemeProvider>{children}</ThemeProvider>
29
+ </body>
30
+ </html>
31
+ )
32
+ }