asasvirtuais 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 (86) hide show
  1. package/README.md +78 -0
  2. package/actions/draw.ts +110 -0
  3. package/components/OAuthCard.tsx +346 -0
  4. package/components/icons.tsx +11 -0
  5. package/components/markdown.tsx +18 -0
  6. package/components/stack/list.tsx +21 -0
  7. package/components/stack/menu.tsx +40 -0
  8. package/components/stack/nav.tsx +39 -0
  9. package/components/table/fixed.tsx +59 -0
  10. package/components/table/key-value.tsx +19 -0
  11. package/components/ui/color-mode.tsx +108 -0
  12. package/components/ui/provider.tsx +15 -0
  13. package/components/ui/toaster.tsx +43 -0
  14. package/components/ui/tooltip.tsx +46 -0
  15. package/hooks/useBoolean.tsx +11 -0
  16. package/hooks/useForwardAs.tsx +29 -0
  17. package/hooks/useHash copy.tsx +27 -0
  18. package/hooks/useHash.tsx +27 -0
  19. package/hooks/useIsMobile.tsx +6 -0
  20. package/hooks/useOAuthTokens.ts +97 -0
  21. package/hooks/useOpenRouterModels.ts +80 -0
  22. package/lib/auth0.ts +11 -0
  23. package/lib/blob.ts +3 -0
  24. package/lib/client-token-storage.ts +216 -0
  25. package/lib/oauth-tokens.ts +85 -0
  26. package/lib/react/context.tsx +20 -0
  27. package/lib/react/index.ts +1 -0
  28. package/lib/tools.ts +375 -0
  29. package/next-env.d.ts +5 -0
  30. package/next.config.ts +23 -0
  31. package/package.json +72 -0
  32. package/packages/blob.ts +97 -0
  33. package/packages/chat/components/chat/feed/index.tsx +76 -0
  34. package/packages/chat/components/chat/feed/story.tsx +18 -0
  35. package/packages/chat/components/chat/index.tsx +16 -0
  36. package/packages/chat/components/chat/story.tsx +74 -0
  37. package/packages/chat/components/debug/index.tsx +54 -0
  38. package/packages/chat/components/header/index.tsx +14 -0
  39. package/packages/chat/components/header/menu/index.tsx +63 -0
  40. package/packages/chat/components/header/story.tsx +33 -0
  41. package/packages/chat/components/header/title/index.tsx +35 -0
  42. package/packages/chat/components/index.ts +13 -0
  43. package/packages/chat/components/input/index.tsx +17 -0
  44. package/packages/chat/components/input/menu/index.tsx +35 -0
  45. package/packages/chat/components/input/send.tsx +21 -0
  46. package/packages/chat/components/input/story.tsx +35 -0
  47. package/packages/chat/components/input/textarea/index.tsx +20 -0
  48. package/packages/chat/components/message/file.tsx +103 -0
  49. package/packages/chat/components/message/menu/index.tsx +26 -0
  50. package/packages/chat/components/message/story.tsx +49 -0
  51. package/packages/chat/components/messages/index.tsx +23 -0
  52. package/packages/chat/components/messages/story.tsx +11 -0
  53. package/packages/chat/components/ui/prose.tsx +263 -0
  54. package/packages/chat/edit-message.tsx +49 -0
  55. package/packages/chat/header.tsx +118 -0
  56. package/packages/chat/index.ts +14 -0
  57. package/packages/chat/input.tsx +89 -0
  58. package/packages/chat/message-menu.tsx +57 -0
  59. package/packages/chat/message.tsx +44 -0
  60. package/packages/chat/messages.tsx +44 -0
  61. package/packages/chat/model-selector.tsx +172 -0
  62. package/packages/chat/scenarios.tsx +68 -0
  63. package/packages/chat/settings.tsx +98 -0
  64. package/packages/chat/temperature-slider.tsx +67 -0
  65. package/packages/chat/tool-results.tsx +32 -0
  66. package/packages/crud/core.ts +75 -0
  67. package/packages/crud/fetcher.ts +64 -0
  68. package/packages/crud/index.ts +2 -0
  69. package/packages/crud/next.ts +128 -0
  70. package/packages/crud/react.tsx +365 -0
  71. package/packages/env.ts +8 -0
  72. package/packages/fields.tsx +157 -0
  73. package/packages/firebase.ts +13 -0
  74. package/packages/firestore.ts +51 -0
  75. package/packages/form.tsx +66 -0
  76. package/packages/next.ts +64 -0
  77. package/packages/openrouter.ts +4 -0
  78. package/packages/react/context.tsx +21 -0
  79. package/packages/react/crud.tsx +372 -0
  80. package/packages/react/hooks.ts +90 -0
  81. package/packages/react/store.tsx +20 -0
  82. package/packages/replit-db.ts +219 -0
  83. package/packages/wretch.ts +22 -0
  84. package/packages/yaml.ts +163 -0
  85. package/pnpm-workspace.yaml +4 -0
  86. package/server/db.ts +15 -0
@@ -0,0 +1,35 @@
1
+ import React from 'react'
2
+ import type { Meta, StoryObj } from '@storybook/react'
3
+ import { ChatInput } from './index'
4
+ import InputTextarea from './textarea'
5
+ import InputMenu from './menu'
6
+ import { Box, MenuItem } from '@chakra-ui/react'
7
+ import { InputSend } from './send'
8
+
9
+ const meta: Meta<typeof ChatInput> = {
10
+ title: 'Components/Input',
11
+ component: ({children}) => (
12
+ <Box h='100dvh' display='flex' alignItems='flex-end' justifyContent='flex-end'>
13
+ <ChatInput>{children}</ChatInput>
14
+ </Box>
15
+ ),
16
+ }
17
+
18
+ export default meta
19
+ type Story = StoryObj<typeof meta>
20
+
21
+ export const Input: Story = {
22
+ args: {
23
+ children: (
24
+ <>
25
+ <InputMenu>
26
+ <MenuItem>Menu item 1</MenuItem>
27
+ <MenuItem>Menu item 2</MenuItem>
28
+ <MenuItem>Menu item 3</MenuItem>
29
+ </InputMenu>
30
+ <InputTextarea/>
31
+ <InputSend/>
32
+ </>
33
+ )
34
+ }
35
+ }
@@ -0,0 +1,20 @@
1
+ 'use client'
2
+ import React from 'react'
3
+ import { Textarea, TextareaProps } from '@chakra-ui/react'
4
+
5
+ export function InputTextarea(props: TextareaProps) {
6
+
7
+ return (
8
+ <Textarea
9
+ rows={1}
10
+ autoFocus
11
+ variant='flushed'
12
+ size='lg'
13
+ placeholder='Type a message...'
14
+ autoresize
15
+ {...props}
16
+ />
17
+ )
18
+ }
19
+
20
+ export default InputTextarea
@@ -0,0 +1,103 @@
1
+ 'use client'
2
+ import { Box, Collapsible, Image, Clipboard, IconButton, Portal, Link } from '@chakra-ui/react'
3
+ import React from 'react'
4
+ import Markdown from 'react-markdown'
5
+ import { FiImage, FiVideo, FiMusic, FiFileText, FiFile, FiPaperclip } from 'react-icons/fi'
6
+
7
+ interface MessageFileProps {
8
+ file: {
9
+ name: string
10
+ type: string
11
+ url?: string
12
+ content?: string
13
+ }
14
+ container: React.RefObject<HTMLDivElement|null>
15
+ }
16
+
17
+ export const MessageFile = ({ container, file }: MessageFileProps) => {
18
+
19
+ return (
20
+ <Collapsible.Root>
21
+ <Collapsible.Trigger asChild>
22
+ <Box
23
+ display="flex"
24
+ alignItems="center"
25
+ gap={2}
26
+ cursor="pointer"
27
+ p={2}
28
+ borderRadius="md"
29
+ _hover={{ backgroundColor: 'gray.100' }}
30
+ >
31
+ <Box fontSize="lg" color="gray.600">
32
+ {file.type.startsWith('image/') && <FiImage />}
33
+ {file.type.startsWith('video/') && <FiVideo />}
34
+ {file.type.startsWith('audio/') && <FiMusic />}
35
+ {file.type.startsWith('text/') && <FiFile />}
36
+ {
37
+ ! file.type.startsWith('image/') &&
38
+ ! file.type.startsWith('video/') &&
39
+ ! file.type.startsWith('audio/') &&
40
+ ! file.type.startsWith('text/') &&
41
+ ! file.name.endsWith('.md') &&
42
+ <FiPaperclip />
43
+ }
44
+ </Box>
45
+ <Box fontSize="sm" fontWeight="medium">{file.name}</Box>
46
+ </Box>
47
+ </Collapsible.Trigger>
48
+
49
+ <Portal container={container}>
50
+ <Collapsible.Content>
51
+ <Box p={3} borderLeft="2px solid" borderColor="gray.200">
52
+ {file.type.startsWith('video/') && <video style={{width: '100%'}} src={file.url} controls />}
53
+ {file.type.startsWith('audio/') && <audio style={{width: '100%'}} src={file.url} controls />}
54
+ {file.type.startsWith('image/') && (<Image src={file.url} alt={file.name} style={{ maxWidth: '100%' }} />)}
55
+ {file.type === 'text/markdown' || file.name.endsWith('.md') && (
56
+ <Box position="relative">
57
+ <Box position="absolute" top={2} right={2}>
58
+ <Clipboard.Root value={file.content || ''}>
59
+ <Clipboard.Trigger asChild>
60
+ <IconButton variant="surface" size="xs">
61
+ <Clipboard.Indicator />
62
+ </IconButton>
63
+ </Clipboard.Trigger>
64
+ </Clipboard.Root>
65
+ </Box>
66
+ <Markdown>{file.content || ''}</Markdown>
67
+ </Box>
68
+ )}
69
+ {file.type.startsWith('text/') && ! file.name.endsWith('.md') && (
70
+ <Box position="relative">
71
+ <Box position="absolute" top={2} right={2}>
72
+ <Clipboard.Root value={file.content || ''}>
73
+ <Clipboard.Trigger asChild>
74
+ <IconButton variant="surface" size="xs">
75
+ <Clipboard.Indicator />
76
+ </IconButton>
77
+ </Clipboard.Trigger>
78
+ </Clipboard.Root>
79
+ </Box>
80
+ <pre style={{ whiteSpace: 'pre-wrap' }}>{file.content}</pre>
81
+ </Box>
82
+ )}
83
+ <Link
84
+ href={file.url}
85
+ download={file.name}
86
+ display='inline-block'
87
+ padding='8px 16px'
88
+ backgroundColor='#007bff'
89
+ color='white'
90
+ textDecoration='none'
91
+ borderRadius='4px'
92
+ mt={4}
93
+ >
94
+ Download {file.name}
95
+ </Link>
96
+ </Box>
97
+ </Collapsible.Content>
98
+ </Portal>
99
+ </Collapsible.Root>
100
+ )
101
+ }
102
+
103
+ export default MessageFile
@@ -0,0 +1,26 @@
1
+ import React from 'react'
2
+ import { Button, Input, Popover, Portal, Text } from "@chakra-ui/react"
3
+
4
+ export function MessageMenu({ children, items }: React.PropsWithChildren<{items: React.ReactNode}>) {
5
+ return (
6
+ <Popover.Root>
7
+ {/* @ts-expect-error dunno */}
8
+ <Popover.Trigger asChild>
9
+ {children}
10
+ </Popover.Trigger>
11
+ <Portal>
12
+ <Popover.Positioner>
13
+ <Popover.Content>
14
+ <Popover.Arrow />
15
+ <Popover.Body overflow='auto'>
16
+ {items}
17
+ </Popover.Body>
18
+ </Popover.Content>
19
+ </Popover.Positioner>
20
+ </Portal>
21
+ </Popover.Root>
22
+ )
23
+ }
24
+
25
+ export default MessageMenu
26
+
@@ -0,0 +1,49 @@
1
+ import React from 'react'
2
+ import type { Meta, StoryObj } from '@storybook/react'
3
+ import { ChatMessage } from './index'
4
+ import ChatMessages from '../messages'
5
+ const meta: Meta<typeof ChatMessage> = {
6
+ title: 'Components/Message',
7
+ component: ChatMessage,
8
+ decorators: [
9
+ Story => (
10
+ <ChatMessages>
11
+ <Story/>
12
+ </ChatMessages>
13
+ )
14
+ ]
15
+ }
16
+
17
+ export default meta
18
+ type Story = StoryObj<typeof meta>
19
+
20
+ export const Message: Story = {
21
+ args: {
22
+ username: 'Claude',
23
+ children: 'Hello! Here are some files I can help you with:',
24
+ date: '2:30 PM',
25
+ avatar: 'https://via.placeholder.com/40',
26
+ files: [
27
+ Object.assign(new File(['# Hello World\n\nThis is a **markdown** file with some content.'], 'example.md', { type: 'text/markdown' }), {
28
+ url: 'https://example.com/files/example.md',
29
+ content: '# Hello World\n\nThis is a **markdown** file with some content.'
30
+ }),
31
+ Object.assign(new File(['console.log("Hello World");'], 'script.js', { type: 'text/javascript' }), {
32
+ url: 'https://example.com/files/script.js',
33
+ content: 'console.log("Hello World");'
34
+ }),
35
+ Object.assign(new File([''], 'image.jpg', { type: 'image/jpeg' }), {
36
+ url: 'https://placehold.co/600x400/EEE/31343C'
37
+ }),
38
+ Object.assign(new File([''], 'video.mp4', { type: 'video/mp4' }), {
39
+ url: 'https://www.w3schools.com/html/mov_bbb.mp4'
40
+ }),
41
+ Object.assign(new File([''], 'audio.mp3', { type: 'audio/mpeg' }), {
42
+ url: 'https://www.soundjay.com/misc/sounds/bell-ringing-05.wav'
43
+ }),
44
+ Object.assign(new File([''], 'document.pdf', { type: 'application/pdf' }), {
45
+ url: 'https://example.com/files/document.pdf'
46
+ })
47
+ ]
48
+ }
49
+ }
@@ -0,0 +1,23 @@
1
+ 'use client'
2
+ import React from "react"
3
+ import { Flex, GridItem } from "@chakra-ui/react"
4
+
5
+ export function ChatMessages({ children }: React.PropsWithChildren) {
6
+ return (
7
+ <GridItem as="main" display="block" overflowY="auto">
8
+ <Flex
9
+ h="full"
10
+ flexDirection="column-reverse"
11
+ overflowY="auto"
12
+ w="full"
13
+ style={{ direction: "ltr" }}
14
+ px={2}
15
+ gap={4}
16
+ >
17
+ {children}
18
+ </Flex>
19
+ </GridItem>
20
+ )
21
+ }
22
+
23
+ export default ChatMessages
@@ -0,0 +1,11 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { ChatMessages } from './index'
3
+
4
+ const meta: Meta<typeof ChatMessages> = {
5
+ title: 'Components/Messages',
6
+ }
7
+
8
+ export default meta
9
+ type Story = StoryObj<typeof meta>
10
+
11
+ export const Messages: Story = {}
@@ -0,0 +1,263 @@
1
+ 'use client'
2
+ import { chakra } from "@chakra-ui/react"
3
+
4
+ export const Prose = chakra("div", {
5
+ base: {
6
+ color: "fg.muted",
7
+ maxWidth: "65ch",
8
+ fontSize: "sm",
9
+ lineHeight: "1.7em",
10
+ "& p": {
11
+ marginTop: "1em",
12
+ marginBottom: "1em",
13
+ },
14
+ "& blockquote": {
15
+ marginTop: "1.285em",
16
+ marginBottom: "1.285em",
17
+ paddingInline: "1.285em",
18
+ borderInlineStartWidth: "0.25em",
19
+ },
20
+ "& a": {
21
+ color: "fg",
22
+ textDecoration: "underline",
23
+ textUnderlineOffset: "3px",
24
+ textDecorationThickness: "2px",
25
+ textDecorationColor: "border.muted",
26
+ fontWeight: "500",
27
+ },
28
+ "& strong": {
29
+ fontWeight: "600",
30
+ },
31
+ "& a strong": {
32
+ color: "inherit",
33
+ },
34
+ "& h1": {
35
+ fontSize: "2.15em",
36
+ letterSpacing: "-0.02em",
37
+ marginTop: "0",
38
+ marginBottom: "0.8em",
39
+ lineHeight: "1.2em",
40
+ },
41
+ "& h2": {
42
+ fontSize: "1.4em",
43
+ letterSpacing: "-0.02em",
44
+ marginTop: "1.6em",
45
+ marginBottom: "0.8em",
46
+ lineHeight: "1.4em",
47
+ },
48
+ "& h3": {
49
+ fontSize: "1.285em",
50
+ letterSpacing: "-0.01em",
51
+ marginTop: "1.5em",
52
+ marginBottom: "0.4em",
53
+ lineHeight: "1.5em",
54
+ },
55
+ "& h4": {
56
+ marginTop: "1.4em",
57
+ marginBottom: "0.5em",
58
+ letterSpacing: "-0.01em",
59
+ lineHeight: "1.5em",
60
+ },
61
+ "& img": {
62
+ marginTop: "1.7em",
63
+ marginBottom: "1.7em",
64
+ borderRadius: "lg",
65
+ boxShadow: "inset",
66
+ },
67
+ "& picture": {
68
+ marginTop: "1.7em",
69
+ marginBottom: "1.7em",
70
+ },
71
+ "& picture > img": {
72
+ marginTop: "0",
73
+ marginBottom: "0",
74
+ },
75
+ "& video": {
76
+ marginTop: "1.7em",
77
+ marginBottom: "1.7em",
78
+ },
79
+ "& kbd": {
80
+ fontSize: "0.85em",
81
+ borderRadius: "xs",
82
+ paddingTop: "0.15em",
83
+ paddingBottom: "0.15em",
84
+ paddingInlineEnd: "0.35em",
85
+ paddingInlineStart: "0.35em",
86
+ fontFamily: "inherit",
87
+ color: "fg.muted",
88
+ "--shadow": "colors.border",
89
+ boxShadow: "0 0 0 1px var(--shadow),0 1px 0 1px var(--shadow)",
90
+ },
91
+ "& code": {
92
+ fontSize: "0.925em",
93
+ letterSpacing: "-0.01em",
94
+ borderRadius: "md",
95
+ borderWidth: "1px",
96
+ padding: "0.25em",
97
+ },
98
+ "& pre code": {
99
+ fontSize: "inherit",
100
+ letterSpacing: "inherit",
101
+ borderWidth: "inherit",
102
+ padding: "0",
103
+ },
104
+ "& h2 code": {
105
+ fontSize: "0.9em",
106
+ },
107
+ "& h3 code": {
108
+ fontSize: "0.8em",
109
+ },
110
+ "& pre": {
111
+ backgroundColor: "bg.subtle",
112
+ marginTop: "1.6em",
113
+ marginBottom: "1.6em",
114
+ borderRadius: "md",
115
+ fontSize: "0.9em",
116
+ paddingTop: "0.65em",
117
+ paddingBottom: "0.65em",
118
+ paddingInlineEnd: "1em",
119
+ paddingInlineStart: "1em",
120
+ overflowX: "auto",
121
+ fontWeight: "400",
122
+ },
123
+ "& ol": {
124
+ marginTop: "1em",
125
+ marginBottom: "1em",
126
+ paddingInlineStart: "1.5em",
127
+ },
128
+ "& ul": {
129
+ marginTop: "1em",
130
+ marginBottom: "1em",
131
+ paddingInlineStart: "1.5em",
132
+ },
133
+ "& li": {
134
+ marginTop: "0.285em",
135
+ marginBottom: "0.285em",
136
+ },
137
+ "& ol > li": {
138
+ paddingInlineStart: "0.4em",
139
+ listStyleType: "decimal",
140
+ "&::marker": {
141
+ color: "fg.muted",
142
+ },
143
+ },
144
+ "& ul > li": {
145
+ paddingInlineStart: "0.4em",
146
+ listStyleType: "disc",
147
+ "&::marker": {
148
+ color: "fg.muted",
149
+ },
150
+ },
151
+ "& > ul > li p": {
152
+ marginTop: "0.5em",
153
+ marginBottom: "0.5em",
154
+ },
155
+ "& > ul > li > p:first-of-type": {
156
+ marginTop: "1em",
157
+ },
158
+ "& > ul > li > p:last-of-type": {
159
+ marginBottom: "1em",
160
+ },
161
+ "& > ol > li > p:first-of-type": {
162
+ marginTop: "1em",
163
+ },
164
+ "& > ol > li > p:last-of-type": {
165
+ marginBottom: "1em",
166
+ },
167
+ "& ul ul, ul ol, ol ul, ol ol": {
168
+ marginTop: "0.5em",
169
+ marginBottom: "0.5em",
170
+ },
171
+ "& dl": {
172
+ marginTop: "1em",
173
+ marginBottom: "1em",
174
+ },
175
+ "& dt": {
176
+ fontWeight: "600",
177
+ marginTop: "1em",
178
+ },
179
+ "& dd": {
180
+ marginTop: "0.285em",
181
+ paddingInlineStart: "1.5em",
182
+ },
183
+ "& hr": {
184
+ marginTop: "2.25em",
185
+ marginBottom: "2.25em",
186
+ },
187
+ "& :is(h1,h2,h3,h4,h5,hr) + *": {
188
+ marginTop: "0",
189
+ },
190
+ "& table": {
191
+ width: "100%",
192
+ tableLayout: "auto",
193
+ textAlign: "start",
194
+ lineHeight: "1.5em",
195
+ marginTop: "2em",
196
+ marginBottom: "2em",
197
+ },
198
+ "& thead": {
199
+ borderBottomWidth: "1px",
200
+ color: "fg",
201
+ },
202
+ "& tbody tr": {
203
+ borderBottomWidth: "1px",
204
+ borderBottomColor: "border",
205
+ },
206
+ "& thead th": {
207
+ paddingInlineEnd: "1em",
208
+ paddingBottom: "0.65em",
209
+ paddingInlineStart: "1em",
210
+ fontWeight: "medium",
211
+ textAlign: "start",
212
+ },
213
+ "& thead th:first-of-type": {
214
+ paddingInlineStart: "0",
215
+ },
216
+ "& thead th:last-of-type": {
217
+ paddingInlineEnd: "0",
218
+ },
219
+ "& tbody td, tfoot td": {
220
+ paddingTop: "0.65em",
221
+ paddingInlineEnd: "1em",
222
+ paddingBottom: "0.65em",
223
+ paddingInlineStart: "1em",
224
+ },
225
+ "& tbody td:first-of-type, tfoot td:first-of-type": {
226
+ paddingInlineStart: "0",
227
+ },
228
+ "& tbody td:last-of-type, tfoot td:last-of-type": {
229
+ paddingInlineEnd: "0",
230
+ },
231
+ "& figure": {
232
+ marginTop: "1.625em",
233
+ marginBottom: "1.625em",
234
+ },
235
+ "& figure > *": {
236
+ marginTop: "0",
237
+ marginBottom: "0",
238
+ },
239
+ "& figcaption": {
240
+ fontSize: "0.85em",
241
+ lineHeight: "1.25em",
242
+ marginTop: "0.85em",
243
+ color: "fg.muted",
244
+ },
245
+ "& h1, h2, h3, h4": {
246
+ color: "fg",
247
+ fontWeight: "600",
248
+ },
249
+ },
250
+ variants: {
251
+ size: {
252
+ md: {
253
+ fontSize: "sm",
254
+ },
255
+ lg: {
256
+ fontSize: "md",
257
+ },
258
+ },
259
+ },
260
+ defaultVariants: {
261
+ size: "md",
262
+ },
263
+ })
@@ -0,0 +1,49 @@
1
+ 'use client'
2
+ import { UpdateForm } from '@/app/module'
3
+ import { Stack, Textarea, HStack, Button } from '@chakra-ui/react'
4
+ import React from 'react'
5
+
6
+ export function EditMessage({
7
+ message, onClose
8
+ }: {
9
+ message: Message
10
+ onClose: () => void
11
+ }) {
12
+ return (
13
+ <UpdateForm table='messages' id={message.id} defaults={{ content: message.content }}>
14
+ {props => (
15
+ <Stack as='form' onSubmit={async (e) => {
16
+ await props.submit(e)
17
+ onClose()
18
+ }}>
19
+ <Textarea
20
+ value={props.fields.content}
21
+ onChange={(e) => props.setField('content', e.target.value)}
22
+ minH="100px" />
23
+ <HStack mt={2}>
24
+ <Button
25
+ type="submit"
26
+ size="sm"
27
+ colorScheme="blue"
28
+ loading={props.loading}
29
+ disabled={props.loading}
30
+ >
31
+ Save
32
+ </Button>
33
+ <Button
34
+ size="sm"
35
+ variant="outline"
36
+ onClick={() => {
37
+ onClose()
38
+ props.setField('content', message.content)
39
+ }}
40
+ disabled={props.loading}
41
+ >
42
+ Cancel
43
+ </Button>
44
+ </HStack>
45
+ </Stack>
46
+ )}
47
+ </UpdateForm>
48
+ )
49
+ }